diff --git a/README.md b/README.md index c7120e7..497a3a3 100644 --- a/README.md +++ b/README.md @@ -293,3 +293,9 @@ const unleash = new UnleashClient({ **NOTES: ⚠️** If `bootstrapOverride` is `true` (by default), any local cached data will be overridden with the bootstrap specified. If `bootstrapOverride` is `false` any local cached data will not be overridden unless the local cache is empty. + +## Manage your own refresh mechanism + +You can opt out of the Unleash feature flag auto-refresh mechanism and metrics update by settings the `refreshInterval` and/or `metricsInterval` options to `false`. +In this case, it becomes your responsibility to call `updateToggles` and/or `sendMetrics` methods. +This approach is useful in environments that do not support the `setInterval` API, such as service workers. diff --git a/src/index.test.ts b/src/index.test.ts index 5e22af6..7268c94 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -12,6 +12,7 @@ import { lastUpdateKey, } from './index'; import { getTypeSafeRequest, getTypeSafeRequestUrl } from './test'; +import Metrics from './metrics'; jest.useFakeTimers(); @@ -1657,6 +1658,26 @@ test('Should report metrics', async () => { client.stop(); }); +test('should send metrics when sendMetrics called', async () => { + const config: IConfig = { + url: 'http://localhost/test', + clientKey: '12', + appName: 'web', + }; + + jest.spyOn(Metrics.prototype, 'sendMetrics'); + + const client = new UnleashClient(config); + + client.start(); + + expect(Metrics.prototype.sendMetrics).not.toHaveBeenCalled(); + + await client.sendMetrics(); + + expect(Metrics.prototype.sendMetrics).toHaveBeenCalled(); +}); + test('Should emit RECOVERED event when sdkStatus is error and status is less than 400', (done) => { const data = { status: 200 }; // replace with the actual data you want to test fetchMock.mockResponseOnce(JSON.stringify(data), { status: 200 }); @@ -2268,3 +2289,54 @@ describe('Experimental options togglesStorageTTL enabled', () => { }); }); }); + +describe('updateToggles', () => { + it('should not update toggles when not started', () => { + const config: IConfig = { + url: 'http://localhost/test', + clientKey: '12', + appName: 'web', + }; + const client = new UnleashClient(config); + + client.updateToggles(); + + expect(fetchMock).not.toHaveBeenCalled(); + }); + + it('should update toggles when started', async () => { + const config: IConfig = { + url: 'http://localhost/test', + clientKey: '12', + appName: 'web', + }; + const client = new UnleashClient(config); + + await client.start(); + fetchMock.mockClear(); + + client.updateToggles(); + + expect(fetchMock).toHaveBeenCalled(); + }); + + it('should wait for client readiness before the toggles update', async () => { + const config: IConfig = { + url: 'http://localhost/test', + clientKey: '12', + appName: 'web', + refreshInterval: 0, + }; + const client = new UnleashClient(config); + + client.start(); + + client.updateToggles(); + + expect(fetchMock).not.toHaveBeenCalled(); + + client.emit(EVENTS.READY); + + expect(fetchMock).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/index.ts b/src/index.ts index cf70075..323208f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -322,7 +322,7 @@ export class UnleashClient extends TinyEmitter { return { ...variant, feature_enabled: enabled }; } - private async updateToggles() { + public async updateToggles() { if (this.timerRef || this.fetchedFromServer) { await this.fetchToggles(); } else if (this.started) { @@ -446,6 +446,10 @@ export class UnleashClient extends TinyEmitter { return this.sdkState === 'error' ? this.lastError : undefined; } + public sendMetrics() { + return this.metrics.sendMetrics(); + } + private async resolveSessionId(): Promise { if (this.context.sessionId) { return this.context.sessionId;