Skip to content

Commit c26b421

Browse files
committed
fix: named client events
* fixed issues where named client wouldnt get events from a default provider Signed-off-by: Todd Baert <[email protected]>
1 parent a8703ed commit c26b421

File tree

2 files changed

+59
-7
lines changed

2 files changed

+59
-7
lines changed

packages/client/test/events.spec.ts

+36
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
22
EventDetails,
33
JsonValue,
4+
NOOP_PROVIDER,
45
OpenFeature,
56
OpenFeatureEventEmitter,
67
Provider,
@@ -76,6 +77,10 @@ describe('Events', () => {
7677
clientId = uuid();
7778
});
7879

80+
beforeEach(() => {
81+
OpenFeature.setProvider(NOOP_PROVIDER);
82+
});
83+
7984
describe('Requirement 5.1.1', () => {
8085
describe('provider implements events', () => {
8186
it('The provider defines a mechanism for signalling the occurrence of an event`PROVIDER_READY`', (done) => {
@@ -192,6 +197,37 @@ describe('Events', () => {
192197
OpenFeature.setProvider(clientId, provider);
193198
});
194199

200+
it('anonymous provider with named client should run', (done) => {
201+
const defaultProvider = new MockProvider({ failOnInit: false, initialStatus: ProviderStatus.NOT_READY, name: 'defauwlt' });
202+
const unboundName = 'some-new-unbound-name';
203+
204+
// get a client using the default because it has not other mapping
205+
const unBoundClient = OpenFeature.getClient(unboundName);
206+
unBoundClient.addHandler(ProviderEvents.ConfigurationChanged, () => {
207+
done();
208+
});
209+
210+
// set the default provider
211+
OpenFeature.setProvider(defaultProvider);
212+
213+
// fire events
214+
defaultProvider.events?.emit(ProviderEvents.ConfigurationChanged);
215+
});
216+
217+
it('anonymous provider with named client should run init events', (done) => {
218+
const defaultProvider = new MockProvider({ failOnInit: false, initialStatus: ProviderStatus.NOT_READY, name: 'defauwlt' });
219+
const unboundName = 'some-other-unbound-name';
220+
221+
// get a client using the default because it has not other mapping
222+
const unBoundClient = OpenFeature.getClient(unboundName);
223+
unBoundClient.addHandler(ProviderEvents.Ready, () => {
224+
done();
225+
});
226+
227+
// set the default provider
228+
OpenFeature.setProvider(defaultProvider);
229+
});
230+
195231
it('un-bound client event handlers still run after new provider set', (done) => {
196232
const defaultProvider = new MockProvider({ name: 'default' });
197233
const namedProvider = new MockProvider();

packages/shared/src/open-feature.ts

+23-7
Original file line numberDiff line numberDiff line change
@@ -104,21 +104,28 @@ export abstract class OpenFeatureCommonAPI<P extends CommonProvider = CommonProv
104104
return this;
105105
}
106106

107-
const clientEmitter = this.getAndCacheEventEmitterForClient(clientName);
107+
// get the named emitter, or if this is the default provider, get all event emitters not associated with a provider
108+
const emitters = clientName ? [this._clientEvents.get(clientName)] : this.getUnboundEmitters();
108109

109110
if (typeof provider.initialize === 'function') {
110111
provider
111112
.initialize?.(this._context)
112113
?.then(() => {
113-
clientEmitter.emit(ProviderEvents.Ready, { clientName });
114+
emitters.forEach((emitter) => {
115+
emitter?.emit(ProviderEvents.Ready, { clientName });
116+
});
114117
this._events?.emit(ProviderEvents.Ready, { clientName });
115118
})
116119
?.catch((error) => {
117-
clientEmitter.emit(ProviderEvents.Error, { clientName, message: error.message });
120+
emitters.forEach((emitter) => {
121+
emitter?.emit(ProviderEvents.Error, { clientName, message: error.message });
122+
});
118123
this._events?.emit(ProviderEvents.Error, { clientName, message: error.message });
119124
});
120125
} else {
121-
clientEmitter.emit(ProviderEvents.Ready, { clientName });
126+
emitters.forEach((emitter) => {
127+
emitter?.emit(ProviderEvents.Ready, { clientName });
128+
});
122129
this._events?.emit(ProviderEvents.Ready, { clientName });
123130
}
124131

@@ -128,7 +135,7 @@ export abstract class OpenFeatureCommonAPI<P extends CommonProvider = CommonProv
128135
this._defaultProvider = provider;
129136
}
130137

131-
this.transferListeners(oldProvider, provider, clientName, clientEmitter);
138+
this.transferListeners(oldProvider, provider, clientName, emitters);
132139

133140
// Do not close a provider that is bound to any client
134141
if (![...this._clientProviders.values(), this._defaultProvider].includes(oldProvider)) {
@@ -167,11 +174,18 @@ export abstract class OpenFeatureCommonAPI<P extends CommonProvider = CommonProv
167174
return newEmitter;
168175
}
169176

177+
private getUnboundEmitters(): OpenFeatureEventEmitter[] {
178+
const namedProviders = [...this._clientProviders.keys()];
179+
const eventEmitterNames = [...this._clientEvents.keys()].filter(key => key) as string[];
180+
const unboundEmitterNames = eventEmitterNames.filter(name => !namedProviders.includes(name));
181+
return unboundEmitterNames.map(name => this._clientEvents.get(name)!);
182+
}
183+
170184
private transferListeners(
171185
oldProvider: P,
172186
newProvider: P,
173187
clientName: string | undefined,
174-
clientEmitter: OpenFeatureEventEmitter
188+
emitters: (OpenFeatureEventEmitter | undefined)[]
175189
) {
176190
this._clientEventHandlers
177191
.get(clientName)
@@ -182,7 +196,9 @@ export abstract class OpenFeatureCommonAPI<P extends CommonProvider = CommonProv
182196
(eventType) => {
183197
const handler = async (details?: EventDetails) => {
184198
// on each event type, fire the associated handlers
185-
clientEmitter.emit(eventType, { ...details, clientName });
199+
emitters.forEach(emitter => {
200+
emitter?.emit(eventType, { ...details, clientName });
201+
});
186202
this._events.emit(eventType, { ...details, clientName });
187203
};
188204

0 commit comments

Comments
 (0)