Skip to content

Commit b47b1dc

Browse files
feat: use interface for events (#798)
Make the providers events member an interface to improve flexibility and reduce possible typing issues. I'm confident this is non-breaking: I've locally updated the my js-contribs to use this, and everything compiled and tested fine. --------- Signed-off-by: Todd Baert <[email protected]> Co-authored-by: Jonathan Norris <[email protected]>
1 parent 7cc1e09 commit b47b1dc

File tree

6 files changed

+63
-24
lines changed

6 files changed

+63
-24
lines changed

packages/client/README.md

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -267,43 +267,52 @@ This can be a new repository or included in [the existing contrib repository](ht
267267
You’ll then need to write the provider by implementing the [Provider interface](./src/provider/provider.ts) exported by the OpenFeature SDK.
268268

269269
```ts
270-
import { JsonValue, Provider, ResolutionDetails } from '@openfeature/web-sdk';
270+
import {
271+
AnyProviderEvent,
272+
EvaluationContext,
273+
Hook,
274+
JsonValue,
275+
Logger,
276+
Provider,
277+
ProviderEventEmitter,
278+
ProviderStatus,
279+
ResolutionDetails
280+
} from '@openfeature/web-sdk';
271281

272282
// implement the provider interface
273283
class MyProvider implements Provider {
274284
// Adds runtime validation that the provider is used with the expected SDK
275-
public readonly runsOn = 'client';
276-
285+
public readonly runsOn = 'server';
277286
readonly metadata = {
278287
name: 'My Provider',
279288
} as const;
280-
281289
// Optional provider managed hooks
282-
hooks?: Hook<FlagValue>[];
283-
290+
hooks?: Hook[];
284291
resolveBooleanEvaluation(flagKey: string, defaultValue: boolean, context: EvaluationContext, logger: Logger): ResolutionDetails<boolean> {
285292
// code to evaluate a boolean
286293
}
287-
288294
resolveStringEvaluation(flagKey: string, defaultValue: string, context: EvaluationContext, logger: Logger): ResolutionDetails<string> {
289295
// code to evaluate a string
290296
}
291-
292297
resolveNumberEvaluation(flagKey: string, defaultValue: number, context: EvaluationContext, logger: Logger): ResolutionDetails<number> {
293298
// code to evaluate a number
294299
}
295-
296300
resolveObjectEvaluation<T extends JsonValue>(flagKey: string, defaultValue: T, context: EvaluationContext, logger: Logger): ResolutionDetails<T> {
297301
// code to evaluate an object
298302
}
299303

304+
onContextChange?(oldContext: EvaluationContext, newContext: EvaluationContext): Promise<void> {
305+
// reconcile the provider's cached flags, if applicable
306+
}
307+
300308
status?: ProviderStatus | undefined;
301-
events?: OpenFeatureEventEmitter | undefined;
309+
310+
// implement with "new OpenFeatureEventEmitter()", and use "emit()" to emit events
311+
events?: ProviderEventEmitter<AnyProviderEvent> | undefined;
302312

303313
initialize?(context?: EvaluationContext | undefined): Promise<void> {
304314
// code to initialize your provider
305315
}
306-
307316
onClose?(): Promise<void> {
308317
// code to shut down your provider
309318
}

packages/server/README.md

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -271,43 +271,48 @@ This can be a new repository or included in [the existing contrib repository](ht
271271
You’ll then need to write the provider by implementing the [Provider interface](./src/provider/provider.ts) exported by the OpenFeature SDK.
272272

273273
```ts
274-
import { JsonValue, Provider, ResolutionDetails } from '@openfeature/server-sdk';
274+
import {
275+
AnyProviderEvent,
276+
EvaluationContext,
277+
Hook,
278+
JsonValue,
279+
Logger,
280+
Provider,
281+
ProviderEventEmitter,
282+
ProviderStatus,
283+
ResolutionDetails
284+
} from '@openfeature/server-sdk';
275285

276286
// implement the provider interface
277287
class MyProvider implements Provider {
278288
// Adds runtime validation that the provider is used with the expected SDK
279289
public readonly runsOn = 'server';
280-
281290
readonly metadata = {
282291
name: 'My Provider',
283292
} as const;
284-
285293
// Optional provider managed hooks
286-
hooks?: Hook<FlagValue>[];
287-
294+
hooks?: Hook[];
288295
resolveBooleanEvaluation(flagKey: string, defaultValue: boolean, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<boolean>> {
289296
// code to evaluate a boolean
290297
}
291-
292298
resolveStringEvaluation(flagKey: string, defaultValue: string, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<string>> {
293299
// code to evaluate a string
294300
}
295-
296301
resolveNumberEvaluation(flagKey: string, defaultValue: number, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<number>> {
297302
// code to evaluate a number
298303
}
299-
300304
resolveObjectEvaluation<T extends JsonValue>(flagKey: string, defaultValue: T, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<T>> {
301305
// code to evaluate an object
302306
}
303307

304308
status?: ProviderStatus | undefined;
305-
events?: OpenFeatureEventEmitter | undefined;
309+
310+
// implement with "new OpenFeatureEventEmitter()", and use "emit()" to emit events
311+
events?: ProviderEventEmitter<AnyProviderEvent> | undefined;
306312

307313
initialize?(context?: EvaluationContext | undefined): Promise<void> {
308314
// code to initialize your provider
309315
}
310-
311316
onClose?(): Promise<void> {
312317
// code to shut down your provider
313318
}

packages/shared/src/events/generic-event-emitter.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Logger, ManageLogger, SafeLogger } from '../logger';
2+
import { ProviderEventEmitter } from './provider-event-emitter';
23
import { EventContext, EventDetails, EventHandler } from './eventing';
34
import { AllProviderEvents, AnyProviderEvent } from './events';
45

@@ -7,7 +8,7 @@ import { AllProviderEvents, AnyProviderEvent } from './events';
78
* in the event details.
89
*/
910
export abstract class GenericEventEmitter<E extends AnyProviderEvent, AdditionalContext extends Record<string, unknown> = Record<string, unknown>>
10-
implements ManageLogger<GenericEventEmitter<E, AdditionalContext>>
11+
implements ProviderEventEmitter<E>, ManageLogger<GenericEventEmitter<E, AdditionalContext>>
1112
{
1213
protected abstract readonly eventEmitter: PlatformEventEmitter;
1314

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
export * from './provider-event-emitter';
12
export * from './event-utils';
23
export * from './eventing';
34
export * from './events';
45
export * from './generic-event-emitter';
6+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { ManageLogger } from '../logger';
2+
import { EventContext, EventHandler } from './eventing';
3+
import { AnyProviderEvent } from './events';
4+
5+
/**
6+
* Event emitter to be optionally implemented by providers.
7+
* Implemented by @see OpenFeatureEventEmitter.
8+
*/
9+
export interface ProviderEventEmitter<E extends AnyProviderEvent, AdditionalContext extends Record<string, unknown> = Record<string, unknown>>
10+
extends ManageLogger<ProviderEventEmitter<E, AdditionalContext>>
11+
{
12+
// here we use E, to restrict the events a provider can manually emit (PROVIDER_CONTEXT_CHANGED is emitted by the SDK)
13+
emit(eventType: E, context?: EventContext): void;
14+
15+
addHandler(eventType: AnyProviderEvent, handler: EventHandler): void;
16+
17+
removeHandler(eventType: AnyProviderEvent, handler: EventHandler): void;
18+
19+
removeAllHandlers(eventType?: AnyProviderEvent): void;
20+
21+
getHandlers(eventType: AnyProviderEvent): EventHandler[];
22+
}

packages/shared/src/provider/provider.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { EvaluationContext } from '../evaluation';
2-
import { AnyProviderEvent, GenericEventEmitter } from '../events';
2+
import { AnyProviderEvent, ProviderEventEmitter } from '../events';
33
import { Metadata, Paradigm } from '../types';
44

55
/**
@@ -57,7 +57,7 @@ export interface CommonProvider {
5757
* An event emitter for ProviderEvents.
5858
* @see ProviderEvents
5959
*/
60-
events?: GenericEventEmitter<AnyProviderEvent>;
60+
events?: ProviderEventEmitter<AnyProviderEvent>;
6161

6262
/**
6363
* A function used to shut down the provider.

0 commit comments

Comments
 (0)