From d4e2ae4c4cb86c4fb5f491c88e81c51b15e767ea Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Thu, 17 Aug 2023 14:39:38 -0400 Subject: [PATCH 1/4] feat: STALE state, minor event changes * added the "STALE" provider state * added "providerName" to EventDetails * run handlers immediately if provider in corresponding state Signed-off-by: Todd Baert --- .../client/src/client/open-feature-client.ts | 8 +- packages/client/test/client.spec.ts | 12 ++- packages/client/test/events.spec.ts | 74 ++++++++++-------- .../server/src/client/open-feature-client.ts | 22 +++--- packages/server/test/client.spec.ts | 9 ++- packages/server/test/events.spec.ts | 76 +++++++++++-------- packages/shared/src/events/event-utils.ts | 21 +++++ packages/shared/src/events/eventing.ts | 1 + packages/shared/src/events/index.ts | 3 +- .../src/events/open-feature-event-emitter.ts | 8 +- packages/shared/src/open-feature.ts | 41 +++++++--- packages/shared/src/provider/provider.ts | 10 ++- 12 files changed, 179 insertions(+), 106 deletions(-) create mode 100644 packages/shared/src/events/event-utils.ts diff --git a/packages/client/src/client/open-feature-client.ts b/packages/client/src/client/open-feature-client.ts index 20442e7de..fb22f5780 100644 --- a/packages/client/src/client/open-feature-client.ts +++ b/packages/client/src/client/open-feature-client.ts @@ -1,5 +1,3 @@ -import { Client } from './client'; -import { Provider } from '../provider'; import { ClientMetadata, ErrorCode, @@ -20,8 +18,10 @@ import { SafeLogger, StandardResolutionReasons, } from '@openfeature/shared'; -import { OpenFeature } from '../open-feature'; import { FlagEvaluationOptions } from '../evaluation'; +import { OpenFeature } from '../open-feature'; +import { Provider } from '../provider'; +import { Client } from './client'; type OpenFeatureClientOptions = { name?: string; @@ -56,7 +56,7 @@ export class OpenFeatureClient implements Client { if (eventType === ProviderEvents.Ready && providerReady) { // run immediately, we're ready. try { - handler({ clientName: this.metadata.name }); + handler({ clientName: this.metadata.name, providerName: this._provider.metadata.name }); } catch (err) { this._logger?.error('Error running event handler:', err); } diff --git a/packages/client/test/client.spec.ts b/packages/client/test/client.spec.ts index ac8e3c96c..fb9d1fc1e 100644 --- a/packages/client/test/client.spec.ts +++ b/packages/client/test/client.spec.ts @@ -1,16 +1,16 @@ import { Client, - Provider, ErrorCode, EvaluationDetails, - JsonValue, + FlagNotFoundError, JsonArray, JsonObject, + JsonValue, + OpenFeature, + OpenFeatureClient, + Provider, ResolutionDetails, StandardResolutionReasons, - FlagNotFoundError, - OpenFeatureClient, - OpenFeature, } from '../src'; const BOOLEAN_VALUE = true; @@ -46,7 +46,6 @@ const MOCK_PROVIDER: Provider = { metadata: { name: 'mock', }, - events: undefined, hooks: [], initialize(): Promise { @@ -370,7 +369,6 @@ describe('Evaluation details structure', () => { metadata: { name: 'error-mock', }, - resolveNumberEvaluation: jest.fn((): Promise> => { throw new Error(NON_OPEN_FEATURE_ERROR_MESSAGE); // throw a non-open-feature error }), diff --git a/packages/client/test/events.spec.ts b/packages/client/test/events.spec.ts index 83082eb6e..7bdcbb6e9 100644 --- a/packages/client/test/events.spec.ts +++ b/packages/client/test/events.spec.ts @@ -1,3 +1,4 @@ +import { v4 as uuid } from 'uuid'; import { JsonValue, NOOP_PROVIDER, @@ -10,7 +11,6 @@ import { ResolutionDetails, StaleEvent, } from '../src'; -import { v4 as uuid } from 'uuid'; const TIMEOUT = 1000; @@ -166,24 +166,23 @@ describe('Events', () => { const provider = new MockProvider(); const client = OpenFeature.getClient(clientId); - let clientHandlerRan = false; - let apiHandlerRan = false; - - client.addHandler(ProviderEvents.Ready, () => { - clientHandlerRan = true; - if (clientHandlerRan && apiHandlerRan) { - done(); - } - }); - - OpenFeature.addHandler(ProviderEvents.Ready, () => { - apiHandlerRan = true; - if (clientHandlerRan && apiHandlerRan) { - done(); - } + Promise.all([ + new Promise((resolve) => { + client.addHandler(ProviderEvents.Error, () => { + resolve(); + }); + }), + new Promise((resolve) => { + OpenFeature.addHandler(ProviderEvents.Error, (details) => { + resolve(); + }); + }) + ]).then(() => { + done(); }); OpenFeature.setProvider(clientId, provider); + provider.events?.emit(ProviderEvents.Error); }); }); @@ -344,11 +343,13 @@ describe('Events', () => { }); describe('Requirement 5.2.3,', () => { - it('The event details contain the client name associated with the event in the API', (done) => { - const provider = new MockProvider(); + it('The event details MUST contain the provider name associated with the event.', (done) => { + const providerName = '5.2.3'; + const provider = new MockProvider({ name: providerName }); const client = OpenFeature.getClient(clientId); client.addHandler(ProviderEvents.Ready, (details) => { + expect(details?.providerName).toEqual(providerName); expect(details?.clientName).toEqual(clientId); done(); }); @@ -493,20 +494,33 @@ describe('Events', () => { }); describe('Requirement 5.3.3', () => { - it('`PROVIDER_READY` handlers added after the provider is already in a ready state MUST run immediately.', (done) => { - const provider = new MockProvider(); - const client = OpenFeature.getClient(clientId); - - OpenFeature.setProvider(clientId, provider); - expect(provider.initialize).toHaveBeenCalled(); + describe('API', () => { + it('Handlers attached after the provider is already in the associated state, MUST run immediately.', (done) => { + const provider = new MockProvider({ initialStatus: ProviderStatus.ERROR }); + const client = OpenFeature.getClient(clientId); + + OpenFeature.setProvider(clientId, provider); + expect(provider.initialize).not.toHaveBeenCalled(); + + OpenFeature.addHandler(ProviderEvents.Error, () => { - let handlerCalled = false; - client.addHandler(ProviderEvents.Ready, () => { - if (!handlerCalled) { - handlerCalled = true; - done(); - } + done(); + }); }); }); + + describe('client', () => { + it('Handlers attached after the provider is already in the associated state, MUST run immediately.', (done) => { + const provider = new MockProvider({ initialStatus: ProviderStatus.READY }); + const client = OpenFeature.getClient(clientId); + + OpenFeature.setProvider(clientId, provider); + expect(provider.initialize).not.toHaveBeenCalled(); + + client.addHandler(ProviderEvents.Ready, () => { + done(); + }); + }); + }); }); }); diff --git a/packages/server/src/client/open-feature-client.ts b/packages/server/src/client/open-feature-client.ts index e6331a121..9e2d4ef24 100644 --- a/packages/server/src/client/open-feature-client.ts +++ b/packages/server/src/client/open-feature-client.ts @@ -6,23 +6,23 @@ import { EventHandler, FlagValue, FlagValueType, + Hook, HookContext, + InternalEventEmitter, JsonValue, Logger, + ManageContext, OpenFeatureError, - InternalEventEmitter, ProviderEvents, - ProviderStatus, ResolutionDetails, - StandardResolutionReasons, SafeLogger, - Hook, - ManageContext, + StandardResolutionReasons, + statusMatchesEvent } from '@openfeature/shared'; +import { FlagEvaluationOptions } from '../evaluation'; import { OpenFeature } from '../open-feature'; -import { Client } from './client'; import { Provider } from '../provider'; -import { FlagEvaluationOptions } from '../evaluation'; +import { Client } from './client'; type OpenFeatureClientOptions = { name?: string; @@ -56,12 +56,12 @@ export class OpenFeatureClient implements Client, ManageContext(eventType: T, handler: EventHandler): void { this.emitterAccessor().addHandler(eventType, handler); - const providerReady = !this._provider.status || this._provider.status === ProviderStatus.READY; + const shouldRunNow = statusMatchesEvent(eventType, this._provider.status); - if (eventType === ProviderEvents.Ready && providerReady) { - // run immediately, we're ready. + if (shouldRunNow) { + // run immediately, we're in the matching state try { - handler({ clientName: this.metadata.name }); + handler({ clientName: this.metadata.name, providerName: this._provider.metadata.name }); } catch (err) { this._logger?.error('Error running event handler:', err); } diff --git a/packages/server/test/client.spec.ts b/packages/server/test/client.spec.ts index f50cf84e5..59ddd98d8 100644 --- a/packages/server/test/client.spec.ts +++ b/packages/server/test/client.spec.ts @@ -7,11 +7,11 @@ import { JsonArray, JsonObject, JsonValue, - ResolutionDetails, - StandardResolutionReasons, - Provider, OpenFeature, OpenFeatureClient, + Provider, + ResolutionDetails, + StandardResolutionReasons, TransactionContext, TransactionContextPropagator, } from '../src'; @@ -348,7 +348,10 @@ describe('OpenFeatureClient', () => { metadata: { name: 'error-mock', }, +<<<<<<< HEAD +======= +>>>>>>> 68aec64 (feat: STALE state, minor event changes) resolveNumberEvaluation: jest.fn((): Promise> => { throw new Error(NON_OPEN_FEATURE_ERROR_MESSAGE); // throw a non-open-feature error }), diff --git a/packages/server/test/events.spec.ts b/packages/server/test/events.spec.ts index 340691e13..2ff069156 100644 --- a/packages/server/test/events.spec.ts +++ b/packages/server/test/events.spec.ts @@ -1,5 +1,7 @@ +import { v4 as uuid } from 'uuid'; import { JsonValue, + NOOP_PROVIDER, OpenFeature, OpenFeatureEventEmitter, Provider, @@ -7,10 +9,8 @@ import { ProviderMetadata, ProviderStatus, ResolutionDetails, - NOOP_PROVIDER, StaleEvent, } from '../src'; -import { v4 as uuid } from 'uuid'; const TIMEOUT = 1000; @@ -170,24 +170,23 @@ describe('Events', () => { const provider = new MockProvider(); const client = OpenFeature.getClient(clientId); - let clientHandlerRan = false; - let apiHandlerRan = false; - - client.addHandler(ProviderEvents.Ready, () => { - clientHandlerRan = true; - if (clientHandlerRan && apiHandlerRan) { - done(); - } - }); - - OpenFeature.addHandler(ProviderEvents.Ready, () => { - apiHandlerRan = true; - if (clientHandlerRan && apiHandlerRan) { - done(); - } + Promise.all([ + new Promise((resolve) => { + client.addHandler(ProviderEvents.Error, () => { + resolve(); + }); + }), + new Promise((resolve) => { + OpenFeature.addHandler(ProviderEvents.Error, (details) => { + resolve(); + }); + }) + ]).then(() => { + done(); }); OpenFeature.setProvider(clientId, provider); + provider.events?.emit(ProviderEvents.Error); }); }); @@ -348,11 +347,13 @@ describe('Events', () => { }); describe('Requirement 5.2.3,', () => { - it('The event details contain the client name associated with the event in the API', (done) => { - const provider = new MockProvider(); + it('The event details MUST contain the provider name associated with the event.', (done) => { + const providerName = '5.2.3'; + const provider = new MockProvider({ name: providerName }); const client = OpenFeature.getClient(clientId); client.addHandler(ProviderEvents.Ready, (details) => { + expect(details?.providerName).toEqual(providerName); expect(details?.clientName).toEqual(clientId); done(); }); @@ -497,20 +498,33 @@ describe('Events', () => { }); describe('Requirement 5.3.3', () => { - it('`PROVIDER_READY` handlers added after the provider is already in a ready state MUST run immediately.', (done) => { - const provider = new MockProvider(); - const client = OpenFeature.getClient(clientId); - - OpenFeature.setProvider(clientId, provider); - expect(provider.initialize).toHaveBeenCalled(); + describe('API', () => { + it('Handlers attached after the provider is already in the associated state, MUST run immediately.', (done) => { + const provider = new MockProvider({ initialStatus: ProviderStatus.ERROR }); + const client = OpenFeature.getClient(clientId); + + OpenFeature.setProvider(clientId, provider); + expect(provider.initialize).not.toHaveBeenCalled(); + + OpenFeature.addHandler(ProviderEvents.Error, () => { - let handlerCalled = false; - client.addHandler(ProviderEvents.Ready, () => { - if (!handlerCalled) { - handlerCalled = true; - done(); - } + done(); + }); }); }); + + describe('client', () => { + it('Handlers attached after the provider is already in the associated state, MUST run immediately.', (done) => { + const provider = new MockProvider({ initialStatus: ProviderStatus.READY }); + const client = OpenFeature.getClient(clientId); + + OpenFeature.setProvider(clientId, provider); + expect(provider.initialize).not.toHaveBeenCalled(); + + client.addHandler(ProviderEvents.Ready, () => { + done(); + }); + }); + }); }); }); diff --git a/packages/shared/src/events/event-utils.ts b/packages/shared/src/events/event-utils.ts new file mode 100644 index 000000000..636b07d2d --- /dev/null +++ b/packages/shared/src/events/event-utils.ts @@ -0,0 +1,21 @@ +import { ProviderStatus } from "../provider"; +import { ProviderEvents } from "./events"; + +const eventStatusMap = { + [ProviderStatus.READY]: ProviderEvents.Ready, + [ProviderStatus.ERROR]: ProviderEvents.Error, + [ProviderStatus.STALE]: ProviderEvents.Stale, + [ProviderStatus.NOT_READY]: undefined, +}; + +/** + * Returns true if the provider's status matches the event. + * If the provider's status is not defined, it matches READY. + * + * @param event + * @param status + * @returns boolean indicating if the provider status matches the event. + */ +export const statusMatchesEvent = (event: ProviderEvents, status?: ProviderStatus): boolean => { + return (!status && event === ProviderEvents.Ready) || eventStatusMap[status!] === event; +}; \ No newline at end of file diff --git a/packages/shared/src/events/eventing.ts b/packages/shared/src/events/eventing.ts index 6f2d62ace..cc556c2fd 100644 --- a/packages/shared/src/events/eventing.ts +++ b/packages/shared/src/events/eventing.ts @@ -5,6 +5,7 @@ export type EventMetadata = { }; export type CommonEventDetails = { + providerName: string; clientName?: string; }; diff --git a/packages/shared/src/events/index.ts b/packages/shared/src/events/index.ts index ec3d42fe5..c32213c31 100644 --- a/packages/shared/src/events/index.ts +++ b/packages/shared/src/events/index.ts @@ -1,3 +1,4 @@ -export * from './events'; +export * from './event-utils'; export * from './eventing'; +export * from './events'; export * from './open-feature-event-emitter'; diff --git a/packages/shared/src/events/open-feature-event-emitter.ts b/packages/shared/src/events/open-feature-event-emitter.ts index a143e2205..56cf4d84f 100644 --- a/packages/shared/src/events/open-feature-event-emitter.ts +++ b/packages/shared/src/events/open-feature-event-emitter.ts @@ -1,7 +1,7 @@ -import { Logger, ManageLogger, SafeLogger } from '../logger'; import EventEmitter from 'events'; +import { Logger, ManageLogger, SafeLogger } from '../logger'; +import { CommonEventDetails, EventContext, EventDetails, EventHandler } from './eventing'; import { ProviderEvents } from './events'; -import { EventContext, EventDetails, EventHandler, CommonEventDetails } from './eventing'; abstract class GenericEventEmitter = Record> implements ManageLogger> @@ -24,8 +24,8 @@ abstract class GenericEventEmitter(eventType: T, handler: EventHandler): void { // The handlers have to be wrapped with an async function because if a synchronous functions throws an error, // the other handlers will not run. - const asyncHandler = async (context?: EventDetails) => { - await handler(context); + const asyncHandler = async (details?: EventDetails) => { + await handler(details); }; // The async handler has to be written to the map, because we need to get the wrapper function when deleting a listener this._handlers.set(handler, asyncHandler); diff --git a/packages/shared/src/open-feature.ts b/packages/shared/src/open-feature.ts index b4a15853f..675f37c50 100644 --- a/packages/shared/src/open-feature.ts +++ b/packages/shared/src/open-feature.ts @@ -1,6 +1,6 @@ import { GeneralError } from './errors'; import { EvaluationContext, FlagValue } from './evaluation'; -import { EventDetails, EventHandler, Eventing, ProviderEvents } from './events'; +import { EventDetails, EventHandler, Eventing, ProviderEvents, statusMatchesEvent } from './events'; import { InternalEventEmitter } from './events/open-feature-event-emitter'; import { isDefined } from './filter'; import { EvaluationLifeCycle, Hook } from './hooks'; @@ -70,11 +70,27 @@ export abstract class OpenFeatureCommonAPI

(eventType: T, handler: EventHandler): void { + [...new Map([[undefined, this._defaultProvider]]), ...this._clientProviders].forEach((keyProviderTuple) => { + const clientName = keyProviderTuple[0]; + const provider = keyProviderTuple[1]; + const shouldRunNow = statusMatchesEvent(eventType, keyProviderTuple[1].status); + + if (shouldRunNow) { + // run immediately, we're in the matching state + try { + handler({ clientName, providerName: provider.metadata.name }); + } catch (err) { + this._logger?.error('Error running event handler:', err); + } + } + }); + this._events.addHandler(eventType, handler); } @@ -124,6 +140,7 @@ export abstract class OpenFeatureCommonAPI

{ // fetch the most recent event emitters, some may have been added during init this.getAssociatedEventEmitters(clientName).forEach((emitter) => { - emitter?.emit(ProviderEvents.Ready, { clientName }); + emitter?.emit(ProviderEvents.Ready, { clientName, providerName }); }); - this._events?.emit(ProviderEvents.Ready, { clientName }); + this._events?.emit(ProviderEvents.Ready, { clientName, providerName }); }) ?.catch((error) => { this.getAssociatedEventEmitters(clientName).forEach((emitter) => { - emitter?.emit(ProviderEvents.Error, { clientName, message: error.message }); + emitter?.emit(ProviderEvents.Error, { clientName, providerName, message: error.message }); }); - this._events?.emit(ProviderEvents.Error, { clientName, message: error.message }); + this._events?.emit(ProviderEvents.Error, { clientName, providerName, message: error.message }); }); } else { emitters.forEach((emitter) => { - emitter?.emit(ProviderEvents.Ready, { clientName }); + emitter?.emit(ProviderEvents.Ready, { clientName, providerName }); }); - this._events?.emit(ProviderEvents.Ready, { clientName }); + this._events?.emit(ProviderEvents.Ready, { clientName, providerName }); } if (clientName) { @@ -208,7 +225,7 @@ export abstract class OpenFeatureCommonAPI

(ProviderEvents).forEach((eventType) => clientProvider.events?.addHandler(eventType, async (details) => { - newEmitter.emit(eventType, { ...details, clientName: name }); + newEmitter.emit(eventType, { ...details, clientName: name, providerName: clientProvider.metadata.name }); }) ); @@ -248,9 +265,9 @@ export abstract class OpenFeatureCommonAPI

) => { // on each event type, fire the associated handlers emitters.forEach((emitter) => { - emitter?.emit(eventType, { ...details, clientName }); + emitter?.emit(eventType, { ...details, clientName, providerName: newProvider.metadata.name }); }); - this._events.emit(eventType, { ...details, clientName }); + this._events.emit(eventType, { ...details, clientName, providerName: newProvider.metadata.name }); }; return [eventType, handler]; diff --git a/packages/shared/src/provider/provider.ts b/packages/shared/src/provider/provider.ts index df42ec33e..e67b78c3d 100644 --- a/packages/shared/src/provider/provider.ts +++ b/packages/shared/src/provider/provider.ts @@ -1,7 +1,6 @@ -import { OpenFeatureEventEmitter } from '../events'; -import { Metadata } from '../types'; import { EvaluationContext } from '../evaluation'; -import { Paradigm } from '../types'; +import { OpenFeatureEventEmitter } from '../events'; +import { Metadata, Paradigm } from '../types'; /** * The state of the provider. @@ -21,6 +20,11 @@ export enum ProviderStatus { * The provider is in an error state and unable to evaluate flags. */ ERROR = 'ERROR', + + /** + * The provider's cached state is no longer valid and may not be up-to-date with the source of truth. + */ + STALE = 'STALE', } /** From fd49e5c6eb9fa43aecd4255f7e4c3730b9a383ff Mon Sep 17 00:00:00 2001 From: Todd Baert Date: Thu, 17 Aug 2023 15:30:35 -0400 Subject: [PATCH 2/4] fixup: comment, lint errors Signed-off-by: Todd Baert --- packages/server/test/client.spec.ts | 4 ---- packages/shared/src/events/event-utils.ts | 12 ++++++------ packages/shared/src/open-feature.ts | 2 +- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/server/test/client.spec.ts b/packages/server/test/client.spec.ts index 59ddd98d8..2eb74e881 100644 --- a/packages/server/test/client.spec.ts +++ b/packages/server/test/client.spec.ts @@ -348,10 +348,6 @@ describe('OpenFeatureClient', () => { metadata: { name: 'error-mock', }, -<<<<<<< HEAD - -======= ->>>>>>> 68aec64 (feat: STALE state, minor event changes) resolveNumberEvaluation: jest.fn((): Promise> => { throw new Error(NON_OPEN_FEATURE_ERROR_MESSAGE); // throw a non-open-feature error }), diff --git a/packages/shared/src/events/event-utils.ts b/packages/shared/src/events/event-utils.ts index 636b07d2d..da01efa0d 100644 --- a/packages/shared/src/events/event-utils.ts +++ b/packages/shared/src/events/event-utils.ts @@ -1,5 +1,5 @@ -import { ProviderStatus } from "../provider"; -import { ProviderEvents } from "./events"; +import { ProviderStatus } from '../provider'; +import { ProviderEvents } from './events'; const eventStatusMap = { [ProviderStatus.READY]: ProviderEvents.Ready, @@ -9,12 +9,12 @@ const eventStatusMap = { }; /** - * Returns true if the provider's status matches the event. + * Returns true if the provider's status corresponds to the event. * If the provider's status is not defined, it matches READY. * - * @param event - * @param status - * @returns boolean indicating if the provider status matches the event. + * @param event event to match + * @param status status of provider + * @returns boolean indicating if the provider status corresponds to the event. */ export const statusMatchesEvent = (event: ProviderEvents, status?: ProviderStatus): boolean => { return (!status && event === ProviderEvents.Ready) || eventStatusMap[status!] === event; diff --git a/packages/shared/src/open-feature.ts b/packages/shared/src/open-feature.ts index 675f37c50..498aa0a0d 100644 --- a/packages/shared/src/open-feature.ts +++ b/packages/shared/src/open-feature.ts @@ -140,7 +140,7 @@ export abstract class OpenFeatureCommonAPI

Date: Tue, 12 Sep 2023 14:11:15 -0400 Subject: [PATCH 3/4] fixup: lint errors Signed-off-by: Todd Baert --- packages/client/test/events.spec.ts | 6 +++--- packages/server/test/events.spec.ts | 6 +++--- packages/shared/src/events/event-utils.ts | 7 +++---- packages/shared/src/filter.ts | 2 +- packages/shared/src/open-feature.ts | 1 - 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/client/test/events.spec.ts b/packages/client/test/events.spec.ts index 7bdcbb6e9..9efa1dcfc 100644 --- a/packages/client/test/events.spec.ts +++ b/packages/client/test/events.spec.ts @@ -82,7 +82,9 @@ describe('Events', () => { jest.clearAllMocks(); clientId = uuid(); // hacky, but it's helpful to clear the handlers between tests + /* eslint-disable @typescript-eslint/no-explicit-any */ (OpenFeature as any)._clientEventHandlers = new Map(); + /* eslint-disable @typescript-eslint/no-explicit-any */ (OpenFeature as any)._clientEvents = new Map(); }); beforeEach(() => { @@ -173,7 +175,7 @@ describe('Events', () => { }); }), new Promise((resolve) => { - OpenFeature.addHandler(ProviderEvents.Error, (details) => { + OpenFeature.addHandler(ProviderEvents.Error, () => { resolve(); }); }) @@ -497,13 +499,11 @@ describe('Events', () => { describe('API', () => { it('Handlers attached after the provider is already in the associated state, MUST run immediately.', (done) => { const provider = new MockProvider({ initialStatus: ProviderStatus.ERROR }); - const client = OpenFeature.getClient(clientId); OpenFeature.setProvider(clientId, provider); expect(provider.initialize).not.toHaveBeenCalled(); OpenFeature.addHandler(ProviderEvents.Error, () => { - done(); }); }); diff --git a/packages/server/test/events.spec.ts b/packages/server/test/events.spec.ts index 2ff069156..9e8916301 100644 --- a/packages/server/test/events.spec.ts +++ b/packages/server/test/events.spec.ts @@ -85,7 +85,9 @@ describe('Events', () => { jest.clearAllMocks(); clientId = uuid(); // hacky, but it's helpful to clear the handlers between tests + /* eslint-disable @typescript-eslint/no-explicit-any */ (OpenFeature as any)._clientEventHandlers = new Map(); + /* eslint-disable @typescript-eslint/no-explicit-any */ (OpenFeature as any)._clientEvents = new Map(); }); @@ -177,7 +179,7 @@ describe('Events', () => { }); }), new Promise((resolve) => { - OpenFeature.addHandler(ProviderEvents.Error, (details) => { + OpenFeature.addHandler(ProviderEvents.Error, () => { resolve(); }); }) @@ -501,13 +503,11 @@ describe('Events', () => { describe('API', () => { it('Handlers attached after the provider is already in the associated state, MUST run immediately.', (done) => { const provider = new MockProvider({ initialStatus: ProviderStatus.ERROR }); - const client = OpenFeature.getClient(clientId); OpenFeature.setProvider(clientId, provider); expect(provider.initialize).not.toHaveBeenCalled(); OpenFeature.addHandler(ProviderEvents.Error, () => { - done(); }); }); diff --git a/packages/shared/src/events/event-utils.ts b/packages/shared/src/events/event-utils.ts index da01efa0d..eff6cab7d 100644 --- a/packages/shared/src/events/event-utils.ts +++ b/packages/shared/src/events/event-utils.ts @@ -11,10 +11,9 @@ const eventStatusMap = { /** * Returns true if the provider's status corresponds to the event. * If the provider's status is not defined, it matches READY. - * - * @param event event to match - * @param status status of provider - * @returns boolean indicating if the provider status corresponds to the event. + * @param {ProviderEvents} event event to match + * @param {ProviderStatus} status status of provider + * @returns {boolean} boolean indicating if the provider status corresponds to the event. */ export const statusMatchesEvent = (event: ProviderEvents, status?: ProviderStatus): boolean => { return (!status && event === ProviderEvents.Ready) || eventStatusMap[status!] === event; diff --git a/packages/shared/src/filter.ts b/packages/shared/src/filter.ts index 66b8a995f..f0ab489fc 100644 --- a/packages/shared/src/filter.ts +++ b/packages/shared/src/filter.ts @@ -2,7 +2,7 @@ * Checks if a value is not null or undefined and returns it as type assertion * @template T * @param {T} input The value to check - * @returns If the value is not null or undefined + * @returns {T} If the value is not null or undefined */ export function isDefined(input?: T | null | undefined): input is T { return typeof input !== 'undefined' && input !== null; diff --git a/packages/shared/src/open-feature.ts b/packages/shared/src/open-feature.ts index 498aa0a0d..8ca601063 100644 --- a/packages/shared/src/open-feature.ts +++ b/packages/shared/src/open-feature.ts @@ -71,7 +71,6 @@ export abstract class OpenFeatureCommonAPI

Date: Tue, 12 Sep 2023 14:14:08 -0400 Subject: [PATCH 4/4] fixup: spec badge Signed-off-by: Todd Baert --- packages/client/README.md | 4 ++-- packages/server/README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/client/README.md b/packages/client/README.md index 4e9c9aad8..c1a98ec2a 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -13,8 +13,8 @@

- - Specification + + Specification diff --git a/packages/server/README.md b/packages/server/README.md index 44fcfa837..f0600cdd2 100644 --- a/packages/server/README.md +++ b/packages/server/README.md @@ -13,8 +13,8 @@

- - Specification + + Specification