Skip to content

Commit 060cbc3

Browse files
[FSSDK-11003] disposable project config manager
1 parent 51e8c1a commit 060cbc3

11 files changed

+54
-62
lines changed

lib/exception_messages.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ export const DATAFILE_MANAGER_STOPPED = 'Datafile manager stopped before it coul
3131
export const DATAFILE_MANAGER_FAILED_TO_START = 'Datafile manager failed to start';
3232
export const FAILED_TO_FETCH_DATAFILE = 'Failed to fetch datafile';
3333
export const FAILED_TO_STOP = 'Failed to stop';
34-
export const YOU_MUST_PROVIDE_DATAFILE_IN_SSR = 'You must provide datafile in SSR';
3534
export const YOU_MUST_PROVIDE_AT_LEAST_ONE_OF_SDKKEY_OR_DATAFILE = 'You must provide at least one of sdkKey or datafile';
3635
export const RETRY_CANCELLED = 'Retry cancelled';
3736
export const REQUEST_TIMEOUT = 'Request timeout';

lib/odp/odp_manager.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const getMockOdpEventManager = () => {
5151
getState: vi.fn(),
5252
updateConfig: vi.fn(),
5353
sendEvent: vi.fn(),
54+
makeDisposable: vi.fn(),
5455
};
5556
};
5657

lib/optimizely/index.spec.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,10 @@
1616
import { describe, it, expect, vi } from 'vitest';
1717
import Optimizely from '.';
1818
import { getMockProjectConfigManager } from '../tests/mock/mock_project_config_manager';
19-
import * as logger from '../plugins/logger';
2019
import * as jsonSchemaValidator from '../utils/json_schema_validator';
21-
import { LOG_LEVEL } from '../common_exports';
2220
import { createNotificationCenter } from '../notification_center';
2321
import testData from '../tests/test_data';
2422
import { getForwardingEventProcessor } from '../event_processor/forwarding_event_processor';
25-
import { LoggerFacade } from '../modules/logging';
2623
import { createProjectConfig } from '../project_config/project_config';
2724
import { getMockLogger } from '../tests/mock/mock_logger';
2825

@@ -39,12 +36,12 @@ describe('Optimizely', () => {
3936

4037
const notificationCenter = createNotificationCenter({ logger, errorHandler });
4138

42-
it('should pass ssr to the project config manager', () => {
39+
it('should pass disposable option to the project config manager', () => {
4340
const projectConfigManager = getMockProjectConfigManager({
4441
initConfig: createProjectConfig(testData.getTestProjectConfig()),
4542
});
46-
47-
vi.spyOn(projectConfigManager, 'setSsr');
43+
44+
vi.spyOn(projectConfigManager, 'makeDisposable');
4845

4946
const instance = new Optimizely({
5047
clientEngine: 'node-sdk',
@@ -54,16 +51,16 @@ describe('Optimizely', () => {
5451
logger,
5552
notificationCenter,
5653
eventProcessor,
57-
isSsr: true,
54+
disposable: true,
5855
isValidInstance: true,
5956
});
6057

61-
expect(projectConfigManager.setSsr).toHaveBeenCalledWith(true);
58+
expect(projectConfigManager.makeDisposable).toHaveBeenCalled();
6259
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
6360
// @ts-ignore
6461
expect(instance.getProjectConfig()).toBe(projectConfigManager.config);
6562
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
6663
// @ts-ignore
67-
expect(projectConfigManager.isSsr).toBe(true);
64+
expect(projectConfigManager.disposable).toBe(true);
6865
});
6966
});

lib/optimizely/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,10 @@ export default class Optimizely implements Client {
176176
this.updateOdpSettings();
177177
});
178178

179-
this.projectConfigManager.setSsr(config.isSsr)
179+
if(config.disposable) {
180+
this.projectConfigManager.makeDisposable();
181+
}
182+
180183
this.projectConfigManager.start();
181184
const projectConfigManagerRunningPromise = this.projectConfigManager.onRunning();
182185

lib/project_config/polling_datafile_manager.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,25 @@ describe('PollingDatafileManager', () => {
470470
expect(repeater.stop).toHaveBeenCalled();
471471
});
472472

473+
it('stops repeater after successful initialization if disposable is true', async () => {
474+
const repeater = getMockRepeater();
475+
const requestHandler = getMockRequestHandler();
476+
const mockResponse = getMockAbortableRequest(Promise.resolve({ statusCode: 200, body: '{"foo": "bar"}', headers: {} }));
477+
requestHandler.makeRequest.mockReturnValueOnce(mockResponse);
478+
479+
const manager = new PollingDatafileManager({
480+
repeater,
481+
requestHandler,
482+
sdkKey: 'keyThatExists',
483+
});
484+
manager.makeDisposable();
485+
manager.start();
486+
repeater.execute(0);
487+
488+
await expect(manager.onRunning()).resolves.not.toThrow();
489+
expect(repeater.stop).toHaveBeenCalled();
490+
});
491+
473492
it('saves the datafile in cache', async () => {
474493
const repeater = getMockRepeater();
475494
const requestHandler = getMockRequestHandler();

lib/project_config/polling_datafile_manager.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,11 @@ export class PollingDatafileManager extends BaseService implements DatafileManag
8989
return;
9090
}
9191

92+
// If disposable, reset the retry count to 5
93+
if(this.disposable) {
94+
this.initRetryRemaining = 5;
95+
}
96+
9297
super.start();
9398
this.state = ServiceState.Starting;
9499
this.setDatafileFromCacheIfAvailable();
@@ -162,7 +167,8 @@ export class PollingDatafileManager extends BaseService implements DatafileManag
162167
if (datafile) {
163168
this.handleDatafile(datafile);
164169
// if autoUpdate is off, don't need to sync datafile any more
165-
if (!this.autoUpdate) {
170+
// if disposable, stop the repeater after the first successful fetch
171+
if (!this.autoUpdate || this.disposable) {
166172
this.repeater.stop();
167173
}
168174
}

lib/project_config/project_config_manager.spec.ts

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -165,17 +165,6 @@ describe('ProjectConfigManagerImpl', () => {
165165
await manager.onRunning();
166166
expect(manager.getConfig()).toEqual(createProjectConfig(testData.getTestProjectConfig()));
167167
});
168-
169-
it('should not start datafileManager if isSsr is true and return correct config', () => {
170-
const datafileManager = getMockDatafileManager({});
171-
vi.spyOn(datafileManager, 'start');
172-
const manager = new ProjectConfigManagerImpl({ datafile: testData.getTestProjectConfig(), datafileManager });
173-
manager.setSsr(true);
174-
manager.start();
175-
176-
expect(manager.getConfig()).toEqual(createProjectConfig(testData.getTestProjectConfig()));
177-
expect(datafileManager.start).not.toHaveBeenCalled();
178-
});
179168
});
180169

181170
describe('when datafile is invalid', () => {
@@ -409,16 +398,6 @@ describe('ProjectConfigManagerImpl', () => {
409398
expect(logger.error).toHaveBeenCalled();
410399
});
411400

412-
it('should reject onRunning() and log error if isSsr is true and datafile is not provided', async () =>{
413-
const logger = getMockLogger();
414-
const manager = new ProjectConfigManagerImpl({ logger, datafileManager: getMockDatafileManager({})});
415-
manager.setSsr(true);
416-
manager.start();
417-
418-
await expect(manager.onRunning()).rejects.toThrow();
419-
expect(logger.error).toHaveBeenCalled();
420-
});
421-
422401
it('should reject onRunning() and log error if the datafile version is not supported', async () => {
423402
const logger = getMockLogger();
424403
const datafile = testData.getUnsupportedVersionConfig();

lib/project_config/project_config_manager.ts

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import {
2626
DATAFILE_MANAGER_FAILED_TO_START,
2727
DATAFILE_MANAGER_STOPPED,
2828
YOU_MUST_PROVIDE_AT_LEAST_ONE_OF_SDKKEY_OR_DATAFILE,
29-
YOU_MUST_PROVIDE_DATAFILE_IN_SSR,
3029
} from '../exception_messages';
3130

3231
interface ProjectConfigManagerConfig {
@@ -40,7 +39,6 @@ interface ProjectConfigManagerConfig {
4039

4140
export interface ProjectConfigManager extends Service {
4241
setLogger(logger: LoggerFacade): void;
43-
setSsr(isSsr?: boolean): void;
4442
getConfig(): ProjectConfig | undefined;
4543
getOptimizelyConfig(): OptimizelyConfig | undefined;
4644
onUpdate(listener: Consumer<ProjectConfig>): Fn;
@@ -60,7 +58,6 @@ export class ProjectConfigManagerImpl extends BaseService implements ProjectConf
6058
public jsonSchemaValidator?: Transformer<unknown, boolean>;
6159
public datafileManager?: DatafileManager;
6260
private eventEmitter: EventEmitter<{ update: ProjectConfig }> = new EventEmitter();
63-
private isSsr = false;
6461

6562
constructor(config: ProjectConfigManagerConfig) {
6663
super();
@@ -77,24 +74,19 @@ export class ProjectConfigManagerImpl extends BaseService implements ProjectConf
7774

7875
this.state = ServiceState.Starting;
7976

80-
if(this.isSsr) {
81-
// If isSsr is true, we don't need to poll for datafile updates
82-
this.datafileManager = undefined
83-
}
84-
8577
if (!this.datafile && !this.datafileManager) {
86-
const errorMessage = this.isSsr
87-
? YOU_MUST_PROVIDE_DATAFILE_IN_SSR
88-
: YOU_MUST_PROVIDE_AT_LEAST_ONE_OF_SDKKEY_OR_DATAFILE;
89-
90-
this.handleInitError(new Error(errorMessage));
78+
this.handleInitError(new Error(YOU_MUST_PROVIDE_AT_LEAST_ONE_OF_SDKKEY_OR_DATAFILE));
9179
return;
9280
}
9381

9482
if (this.datafile) {
9583
this.handleNewDatafile(this.datafile, true);
9684
}
9785

86+
if(this.disposable) {
87+
this.datafileManager?.makeDisposable();
88+
}
89+
9890
this.datafileManager?.start();
9991

10092
// This handles the case where the datafile manager starts successfully. The
@@ -227,13 +219,4 @@ export class ProjectConfigManagerImpl extends BaseService implements ProjectConf
227219
this.stopPromise.reject(err);
228220
});
229221
}
230-
231-
/**
232-
* Set the isSsr flag to indicate if the project config manager is being used in a server side rendering environment
233-
* @param {Boolean} isSsr
234-
* @returns {void}
235-
*/
236-
setSsr(isSsr: boolean): void {
237-
this.isSsr = isSsr;
238-
}
239222
}

lib/service.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export interface Service {
5151
// either by failing to start or stop.
5252
// It will resolve if the service is stopped successfully.
5353
onTerminated(): Promise<void>;
54+
makeDisposable(): void;
5455
}
5556

5657
export abstract class BaseService implements Service {
@@ -59,7 +60,7 @@ export abstract class BaseService implements Service {
5960
protected stopPromise: ResolvablePromise<void>;
6061
protected logger?: LoggerFacade;
6162
protected startupLogs: StartupLog[];
62-
63+
protected disposable = false;
6364
constructor(startupLogs: StartupLog[] = []) {
6465
this.state = ServiceState.New;
6566
this.startPromise = resolvablePromise();
@@ -71,6 +72,10 @@ export abstract class BaseService implements Service {
7172
this.stopPromise.promise.catch(() => {});
7273
}
7374

75+
makeDisposable(): void {
76+
this.disposable = true;
77+
}
78+
7479
setLogger(logger: LoggerFacade): void {
7580
this.logger = logger;
7681
}

lib/shared_types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,10 +263,10 @@ export interface OptimizelyOptions {
263263
sdkKey?: string;
264264
userProfileService?: UserProfileService | null;
265265
defaultDecideOptions?: OptimizelyDecideOption[];
266-
isSsr?:boolean;
267266
odpManager?: OdpManager;
268267
notificationCenter: DefaultNotificationCenter;
269268
vuidManager?: VuidManager
269+
disposable?:boolean;
270270
}
271271

272272
/**
@@ -384,9 +384,9 @@ export interface Config {
384384
defaultDecideOptions?: OptimizelyDecideOption[];
385385
clientEngine?: string;
386386
clientVersion?: string;
387-
isSsr?: boolean;
388387
odpManager?: OdpManager;
389388
vuidManager?: VuidManager;
389+
disposable?:boolean;
390390
}
391391

392392
export type OptimizelyExperimentsMap = {

0 commit comments

Comments
 (0)