From 214c168c2071c5a5153061a86289f28ac35989ad Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Thu, 11 Sep 2025 15:10:19 -0300 Subject: [PATCH 1/7] Add factory option to provider --- src/__tests__/nodeSuites/provider.spec.js | 31 +++++++++++++++-------- src/__tests__/testUtils/index.js | 6 ++++- src/lib/js-split-provider.ts | 30 +++++++++++++++++----- 3 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/__tests__/nodeSuites/provider.spec.js b/src/__tests__/nodeSuites/provider.spec.js index abc1aa8..f120590 100644 --- a/src/__tests__/nodeSuites/provider.spec.js +++ b/src/__tests__/nodeSuites/provider.spec.js @@ -1,20 +1,29 @@ /* eslint-disable jest/no-conditional-expect */ -import { getLocalHostSplitClient } from '../testUtils'; +import { getLocalHostSplitClient, getSplitFactory } from '../testUtils'; import { OpenFeatureSplitProvider } from '../../lib/js-split-provider'; -describe('provider tests', () => { +const cases = [ + [ + 'Localhost', + { splitClient: getLocalHostSplitClient()}, + + ], + [ + 'Factory', + getSplitFactory() + ], +]; + +describe.each(cases)('%s', (label, getOptions) => { - let splitClient; let provider; - beforeEach(() => { - splitClient = getLocalHostSplitClient(); - provider = new OpenFeatureSplitProvider({ splitClient }); + beforeEach(async () => { + provider = new OpenFeatureSplitProvider(getOptions); }); - afterEach(() => { - splitClient.destroy(); - provider = undefined; + afterEach(async () => { + jest.clearAllMocks() }); test('evaluate Boolean null/empty test', async () => { @@ -210,14 +219,14 @@ describe('provider tests', () => { }); test('track: ok without details', async () => { - const trackSpy = jest.spyOn(splitClient, 'track'); + const trackSpy = jest.spyOn(getOptions.splitClient ? getOptions.splitClient : getOptions.client(), 'track'); await provider.track('view', { targetingKey: 'u1', trafficType: 'user' }, null); expect(trackSpy).toHaveBeenCalledTimes(1); expect(trackSpy).toHaveBeenCalledWith('u1', 'user', 'view', undefined, {}); }); test('track: ok with details', async () => { - const trackSpy = jest.spyOn(splitClient, 'track'); + const trackSpy = jest.spyOn(getOptions.splitClient ? getOptions.splitClient : getOptions.client(), 'track'); await provider.track( 'purchase', { targetingKey: 'u1', trafficType: 'user' }, diff --git a/src/__tests__/testUtils/index.js b/src/__tests__/testUtils/index.js index 2f71380..35e6a34 100644 --- a/src/__tests__/testUtils/index.js +++ b/src/__tests__/testUtils/index.js @@ -106,4 +106,8 @@ export function getLocalHostSplitClient() { export function getRedisSplitClient(redisPort) { return SplitFactory(getRedisConfig(redisPort)).client(); -} \ No newline at end of file +} + +export function getSplitFactory() { + return SplitFactory(config); +} diff --git a/src/lib/js-split-provider.ts b/src/lib/js-split-provider.ts index 9f9455c..9c58d66 100644 --- a/src/lib/js-split-provider.ts +++ b/src/lib/js-split-provider.ts @@ -36,16 +36,28 @@ export class OpenFeatureSplitProvider implements Provider { public readonly events = new OpenFeatureEventEmitter(); - constructor(options: SplitProviderOptions | string) { - + private getSplitClient(options: SplitProviderOptions | string | SplitIO.ISDK | SplitIO.IAsyncSDK) { if (typeof(options) === 'string') { const splitFactory = SplitFactory({core: { authorizationKey: options } }); - this.client = splitFactory.client(); - } else { - this.client = options.splitClient; + return splitFactory.client(); + } + + let splitClient; + try { + splitClient = (options as SplitIO.ISDK | SplitIO.IAsyncSDK).client(); + } catch { + splitClient = (options as SplitProviderOptions).splitClient } - this.client.on(this.client.Event.SDK_UPDATE, (payload) => { - this.events.emit(ProviderEvents.ConfigurationChanged, payload) + + return splitClient; + } + + constructor(options: SplitProviderOptions | string | SplitIO.ISDK | SplitIO.IAsyncSDK) { + + this.client = this.getSplitClient(options); + + this.client.on(this.client.Event.SDK_UPDATE, () => { + this.events.emit(ProviderEvents.ConfigurationChanged) }); this.initialized = new Promise((resolve) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -191,6 +203,10 @@ export class OpenFeatureSplitProvider implements Provider { this.client.track(targetingKey, trafficType, trackingEventName, value, properties); } + async onClose?(): Promise { + return this.client.destroy(); + } + //Transform the context into an object useful for the Split API, an key string with arbitrary Split 'Attributes'. private transformContext(context: EvaluationContext): Consumer { const { targetingKey, ...attributes } = context; From 20c14176828f2e15b43753a85260e152ad620011 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Thu, 11 Sep 2025 15:15:32 -0300 Subject: [PATCH 2/7] Add provider initialization with splitFactory in readme --- README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9bcf14f..95bfb55 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ npm install @splitsoftware/splitio npm install @openfeature/server-sdk ``` -### Register the Split provider with OpenFeature +### Register the Split provider with OpenFeature using splitClient ```js const OpenFeature = require('@openfeature/server-sdk').OpenFeature; const SplitFactory = require('@splitsoftware/splitio').SplitFactory; @@ -35,6 +35,18 @@ const provider = new OpenFeatureSplitProvider({splitClient}); OpenFeature.setProvider(provider); ``` +### Register the Split provider with OpenFeature using splitFactory +```js +const OpenFeature = require('@openfeature/server-sdk').OpenFeature; +const SplitFactory = require('@splitsoftware/splitio').SplitFactory; +const OpenFeatureSplitProvider = require('@splitsoftware/openfeature-js-split-provider').OpenFeatureSplitProvider; + +const authorizationKey = 'your auth key' +const splitFactory = SplitFactory({core: {authorizationKey}}); +const provider = new OpenFeatureSplitProvider(splitFactory); +OpenFeature.setProvider(provider); +``` + ## Use of OpenFeature with Split After the initial setup you can use OpenFeature according to their [documentation](https://docs.openfeature.dev/docs/reference/concepts/evaluation-api/). From 159de6c6d51184ce3db38425123d15163665ac8b Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Thu, 11 Sep 2025 15:18:53 -0300 Subject: [PATCH 3/7] Update changes file --- CHANGES.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.txt b/CHANGES.txt index 5f82f62..f148594 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -2,6 +2,7 @@ - Up to date with @openfeature/server-sdk 1.19.0 - Added tracking support - Added “evaluate with details” support + - Support provider initialization using splitFactory 1.1.0 (June 16, 2025) - Uses renamed @openfeature/js-sdk to @openfeature/server-sdk From 083999fbeafa8e743f35f3ee4b65797da12b4233 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Thu, 11 Sep 2025 15:20:12 -0300 Subject: [PATCH 4/7] Update release date --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index f148594..a6acf67 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,4 @@ -1.2.0 (September 9, 2025) +1.2.0 (September 11, 2025) - Up to date with @openfeature/server-sdk 1.19.0 - Added tracking support - Added “evaluate with details” support From c13cdd17f5197c3e2b8ea6acaf0fd6e87ecea003 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Thu, 11 Sep 2025 15:54:19 -0300 Subject: [PATCH 5/7] Update tests --- src/__tests__/nodeSuites/client.spec.js | 31 +++++++++++++++-------- src/__tests__/nodeSuites/provider.spec.js | 19 ++++++++------ src/__tests__/testUtils/index.js | 1 - 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/src/__tests__/nodeSuites/client.spec.js b/src/__tests__/nodeSuites/client.spec.js index e2faccc..83b0b0d 100644 --- a/src/__tests__/nodeSuites/client.spec.js +++ b/src/__tests__/nodeSuites/client.spec.js @@ -1,19 +1,31 @@ /* eslint-disable jest/no-conditional-expect */ import { OpenFeatureSplitProvider } from '../../lib/js-split-provider'; -import { getLocalHostSplitClient } from '../testUtils'; +import { getLocalHostSplitClient, getSplitFactory } from '../testUtils'; import { OpenFeature } from '@openfeature/server-sdk'; -describe('client tests', () => { +const cases = [ + [ + 'openfeature client tests mode: splitClient', + () => ({ splitClient: getLocalHostSplitClient()}), + + ], + [ + 'openfeature client tests mode: splitFactory', + getSplitFactory + ], +]; + +describe.each(cases)('%s', (label, getOptions) => { let client; - let splitClient; let provider; + let options; beforeEach(() => { - splitClient = getLocalHostSplitClient(); - provider = new OpenFeatureSplitProvider({ splitClient }); + options = getOptions(); + provider = new OpenFeatureSplitProvider(options); OpenFeature.setProvider(provider); client = OpenFeature.getClient('test'); @@ -22,9 +34,8 @@ describe('client tests', () => { }; client.setContext(evaluationContext); }); - afterEach(() => { - splitClient.destroy(); - provider = undefined; + afterEach(async () => { + await OpenFeature.close(); }); test('use default test', async () => { @@ -206,13 +217,13 @@ describe('client tests', () => { }); test('track: without value', async () => { - const trackSpy = jest.spyOn(splitClient, 'track'); + const trackSpy = jest.spyOn(options.splitClient ? options.splitClient : options.client(), 'track'); await client.track('my-event', { targetingKey: 'u1', trafficType: 'user' }, { properties: { prop1: 'value1' } }); expect(trackSpy).toHaveBeenCalledWith('u1', 'user', 'my-event', undefined, { prop1: 'value1' }); }); test('track: with value', async () => { - const trackSpy = jest.spyOn(splitClient, 'track'); + const trackSpy = jest.spyOn(options.splitClient ? options.splitClient : options.client(), 'track'); await client.track('my-event', { targetingKey: 'u1', trafficType: 'user' }, { value: 9.99, properties: { prop1: 'value1' } }); expect(trackSpy).toHaveBeenCalledWith('u1', 'user', 'my-event', 9.99, { prop1: 'value1' }); }); diff --git a/src/__tests__/nodeSuites/provider.spec.js b/src/__tests__/nodeSuites/provider.spec.js index f120590..c9ea3d1 100644 --- a/src/__tests__/nodeSuites/provider.spec.js +++ b/src/__tests__/nodeSuites/provider.spec.js @@ -4,26 +4,29 @@ import { OpenFeatureSplitProvider } from '../../lib/js-split-provider'; const cases = [ [ - 'Localhost', - { splitClient: getLocalHostSplitClient()}, + 'provider tests mode: splitClient', + () => ({ splitClient: getLocalHostSplitClient()}), ], [ - 'Factory', - getSplitFactory() + 'provider tests mode: splitFactory', + getSplitFactory ], ]; describe.each(cases)('%s', (label, getOptions) => { let provider; + let options; - beforeEach(async () => { - provider = new OpenFeatureSplitProvider(getOptions); + beforeEach(() => { + options = getOptions(); + provider = new OpenFeatureSplitProvider(options); }); afterEach(async () => { jest.clearAllMocks() + await provider.onClose(); }); test('evaluate Boolean null/empty test', async () => { @@ -219,14 +222,14 @@ describe.each(cases)('%s', (label, getOptions) => { }); test('track: ok without details', async () => { - const trackSpy = jest.spyOn(getOptions.splitClient ? getOptions.splitClient : getOptions.client(), 'track'); + const trackSpy = jest.spyOn(options.splitClient ? options.splitClient : options.client(), 'track'); await provider.track('view', { targetingKey: 'u1', trafficType: 'user' }, null); expect(trackSpy).toHaveBeenCalledTimes(1); expect(trackSpy).toHaveBeenCalledWith('u1', 'user', 'view', undefined, {}); }); test('track: ok with details', async () => { - const trackSpy = jest.spyOn(getOptions.splitClient ? getOptions.splitClient : getOptions.client(), 'track'); + const trackSpy = jest.spyOn(options.splitClient ? options.splitClient : options.client(), 'track'); await provider.track( 'purchase', { targetingKey: 'u1', trafficType: 'user' }, diff --git a/src/__tests__/testUtils/index.js b/src/__tests__/testUtils/index.js index 35e6a34..4e7db2b 100644 --- a/src/__tests__/testUtils/index.js +++ b/src/__tests__/testUtils/index.js @@ -95,7 +95,6 @@ const config = { authorizationKey: 'localhost' }, features: './split.yaml', - debug: 'DEBUG' } /** * get a Split client in localhost mode for testing purposes From 516b220f4329ba840f13f6a911b77bd96a243cd4 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Fri, 12 Sep 2025 11:14:30 -0300 Subject: [PATCH 6/7] update release date --- CHANGES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.txt b/CHANGES.txt index a6acf67..4a2ac8e 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,4 @@ -1.2.0 (September 11, 2025) +1.2.0 (September 12, 2025) - Up to date with @openfeature/server-sdk 1.19.0 - Added tracking support - Added “evaluate with details” support From 0d03ee8b09ba343a41d9070b6d4aee6b113e95a7 Mon Sep 17 00:00:00 2001 From: Emmanuel Zamora Date: Fri, 12 Sep 2025 11:40:35 -0300 Subject: [PATCH 7/7] Add apiKey instantiation to readme --- README.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 95bfb55..17b2ad4 100644 --- a/README.md +++ b/README.md @@ -23,15 +23,13 @@ npm install @splitsoftware/splitio npm install @openfeature/server-sdk ``` -### Register the Split provider with OpenFeature using splitClient +### Register the Split provider with OpenFeature using sdk apiKey ```js const OpenFeature = require('@openfeature/server-sdk').OpenFeature; -const SplitFactory = require('@splitsoftware/splitio').SplitFactory; const OpenFeatureSplitProvider = require('@splitsoftware/openfeature-js-split-provider').OpenFeatureSplitProvider; const authorizationKey = 'your auth key' -const splitClient = SplitFactory({core: {authorizationKey}}).client(); -const provider = new OpenFeatureSplitProvider({splitClient}); +const provider = new OpenFeatureSplitProvider(authorizationKey); OpenFeature.setProvider(provider); ``` @@ -47,6 +45,18 @@ const provider = new OpenFeatureSplitProvider(splitFactory); OpenFeature.setProvider(provider); ``` +### Register the Split provider with OpenFeature using splitClient +```js +const OpenFeature = require('@openfeature/server-sdk').OpenFeature; +const SplitFactory = require('@splitsoftware/splitio').SplitFactory; +const OpenFeatureSplitProvider = require('@splitsoftware/openfeature-js-split-provider').OpenFeatureSplitProvider; + +const authorizationKey = 'your auth key' +const splitClient = SplitFactory({core: {authorizationKey}}).client(); +const provider = new OpenFeatureSplitProvider({splitClient}); +OpenFeature.setProvider(provider); +``` + ## Use of OpenFeature with Split After the initial setup you can use OpenFeature according to their [documentation](https://docs.openfeature.dev/docs/reference/concepts/evaluation-api/).