feat: add a top-level method for accessing providers (#1152)

## This PR

- add a top-level method for accessing providers

### Notes

While working on some improvements to the way Suspense works in the
React SDK, I ran into a few scenarios were having access to the provider
itself was important. I needed a way to confidently tell that a provider
was the noop provider since it has special properties like never being
in a ready state. There are a few ways could could achieve this but I
noticed that the Java SDK [already has
methods](https://github.com/open-feature/java-sdk/blob/main/src/main/java/dev/openfeature/sdk/OpenFeatureAPI.java#L279)
that expose the provider. It also allowed me to improve some of our
existing tests.

### How to test

Unit tests have been updated accordingly.

Signed-off-by: Michael Beemer <beeme1mr@users.noreply.github.com>
This commit is contained in:
Michael Beemer 2025-03-06 15:50:06 -05:00 committed by GitHub
parent eec21dda82
commit ae8fce8753
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 57 additions and 15 deletions

View File

@ -138,6 +138,27 @@ export class OpenFeatureAPI
return this;
}
/**
* Get the default provider.
*
* Note that it isn't recommended to interact with the provider directly, but rather through
* an OpenFeature client.
* @returns {Provider} Default Provider
*/
getProvider(): Provider;
/**
* Get the provider bound to the specified domain.
*
* Note that it isn't recommended to interact with the provider directly, but rather through
* an OpenFeature client.
* @param {string} domain An identifier which logically binds clients with providers
* @returns {Provider} Domain-scoped provider
*/
getProvider(domain?: string): Provider;
getProvider(domain?: string): Provider {
return this.getProviderForClient(domain);
}
setContext(context: EvaluationContext): this {
this._context = context;
return this;

View File

@ -74,8 +74,8 @@ describe('OpenFeature', () => {
it('should set the default provider if no domain is provided', () => {
const provider = mockProvider();
OpenFeature.setProvider(provider);
const client = OpenFeature.getClient();
expect(client.metadata.providerMetadata.name).toEqual(provider.metadata.name);
const registeredProvider = OpenFeature.getProvider();
expect(registeredProvider).toEqual(provider);
});
it('should not change providers associated with a domain when setting a new default provider', () => {
@ -85,11 +85,11 @@ describe('OpenFeature', () => {
OpenFeature.setProvider(provider);
OpenFeature.setProvider(domain, fakeProvider);
const defaultClient = OpenFeature.getClient();
const domainSpecificClient = OpenFeature.getClient(domain);
const defaultProvider = OpenFeature.getProvider();
const domainSpecificProvider = OpenFeature.getProvider(domain);
expect(defaultClient.metadata.providerMetadata.name).toEqual(provider.metadata.name);
expect(domainSpecificClient.metadata.providerMetadata.name).toEqual(fakeProvider.metadata.name);
expect(defaultProvider).toEqual(provider);
expect(domainSpecificProvider).toEqual(fakeProvider);
});
it('should bind a new provider to existing clients in a matching domain', () => {

View File

@ -205,6 +205,27 @@ export class OpenFeatureAPI
return this;
}
/**
* Get the default provider.
*
* Note that it isn't recommended to interact with the provider directly, but rather through
* an OpenFeature client.
* @returns {Provider} Default Provider
*/
getProvider(): Provider;
/**
* Get the provider bound to the specified domain.
*
* Note that it isn't recommended to interact with the provider directly, but rather through
* an OpenFeature client.
* @param {string} domain An identifier which logically binds clients with providers
* @returns {Provider} Domain-scoped provider
*/
getProvider(domain?: string): Provider;
getProvider(domain?: string): Provider {
return this.getProviderForClient(domain);
}
/**
* Sets the evaluation context globally.
* This will be used by all providers that have not bound to a domain.
@ -325,9 +346,9 @@ export class OpenFeatureAPI
}
/**
* A factory function for creating new named OpenFeature clients. Clients can contain
* their own state (e.g. logger, hook, context). Multiple clients can be used
* to segment feature flag configuration.
* A factory function for creating new domain-scoped OpenFeature clients. Clients
* can contain their own state (e.g. logger, hook, context). Multiple domains
* can be used to segment feature flag configuration.
*
* If there is already a provider bound to this name via {@link this.setProvider setProvider}, this provider will be used.
* Otherwise, the default provider is used until a provider is assigned to that name.

View File

@ -75,8 +75,8 @@ describe('OpenFeature', () => {
it('should set the default provider if no domain is provided', () => {
const provider = mockProvider();
OpenFeature.setProvider(provider);
const client = OpenFeature.getClient();
expect(client.metadata.providerMetadata.name).toEqual(provider.metadata.name);
const registeredProvider = OpenFeature.getProvider();
expect(registeredProvider).toEqual(provider);
});
it('should not change providers associated with a domain when setting a new default provider', () => {
@ -86,11 +86,11 @@ describe('OpenFeature', () => {
OpenFeature.setProvider(provider);
OpenFeature.setProvider(domain, fakeProvider);
const defaultClient = OpenFeature.getClient();
const domainSpecificClient = OpenFeature.getClient(domain);
const defaultProvider = OpenFeature.getProvider();
const domainSpecificProvider = OpenFeature.getProvider(domain);
expect(defaultClient.metadata.providerMetadata.name).toEqual(provider.metadata.name);
expect(domainSpecificClient.metadata.providerMetadata.name).toEqual(fakeProvider.metadata.name);
expect(defaultProvider).toEqual(provider);
expect(domainSpecificProvider).toEqual(fakeProvider);
});
it('should bind a new provider to existing clients in a matching domain', () => {