8000 [FSSDK-10993] additional cleanup for project config manager (#977) · optimizely/javascript-sdk@faee6c7 · GitHub
[go: up one dir, main page]

Skip to content

Commit faee6c7

Browse files
authored
[FSSDK-10993] additional cleanup for project config manager (#977)
1 parent e119925 commit faee6c7

12 files changed

+135
-125
lines changed

lib/project_config/config_manager_factory.browser.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ vi.mock('../utils/http_request_handler/browser_request_handler', () => {
3030
import { getPollingConfigManager, PollingConfigManagerConfig, PollingConfigManagerFactoryOptions } from './config_manager_factory';
3131
import { createPollingProjectConfigManager } from './config_manager_factory.browser';
3232
import { BrowserRequestHandler } from '../utils/http_request_handler/browser_request_handler';
33+
import { getMockSyncCache } from '../tests/mock/mock_cache';
3334

3435
describe('createPollingConfigManager', () => {
3536
const mockGetPollingConfigManager = vi.mocked(getPollingConfigManager);
@@ -76,7 +77,7 @@ describe('createPollingConfigManager', () => {
7677
autoUpdate: true,
7778
urlTemplate: 'urlTemplate',
7879
datafileAccessToken: 'datafileAccessToken',
79-
cache: { get: vi.fn(), set: vi.fn(), contains: vi.fn(), remove: vi.fn() },
80+
cache: getMockSyncCache<string>(),
8081
};
8182

8283
const projectConfigManager = createPollingProjectConfigManager(config);

lib/project_config/config_manager_factory.node.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ vi.mock('../utils/http_request_handler/node_request_handler', () => {
3030
import { getPollingConfigManager, PollingConfigManagerConfig } from './config_manager_factory';
3131
import { createPollingProjectConfigManager } from './config_manager_factory.node';
3232
import { NodeRequestHandler } from '../utils/http_request_handler/node_request_handler';
33-
import { DEFAULT_AUTHENTICATED_URL_TEMPLATE, DEFAULT_URL_TEMPLATE } from './constant';
33+
import { getMockSyncCache } from '../tests/mock/mock_cache';
3434

3535
describe('createPollingConfigManager', () => {
3636
const mockGetPollingConfigManager = vi.mocked(getPollingConfigManager);
@@ -77,7 +77,7 @@ describe('createPollingConfigManager', () => {
7777
autoUpdate: false,
7878
urlTemplate: 'urlTemplate',
7979
datafileAccessToken: 'datafileAccessToken',
80-
cache: { get: vi.fn(), set: vi.fn(), contains: vi.fn(), remove: vi.fn() },
80+
cache: getMockSyncCache(),
8181
};
8282

8383
const projectConfigManager = createPollingProjectConfigManager(config);

lib/project_config/config_manager_factory.react_native.spec.ts

Lines changed: 15 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ async function mockRequireAsyncStorage() {
2929
M._load_original = M._load;
3030
M._load = (uri: string, parent: string) => {
3131
if (uri === '@react-native-async-storage/async-storage') {
32-
if (isAsyncStorageAvailable) return {};
32+
if (isAsyncStorageAvailable) return { default: {} };
3333
throw new Error('Module not found: @react-native-async-storage/async-storage');
3434
}
3535
return M._load_original(uri, parent);
@@ -47,25 +47,30 @@ vi.mock('../utils/http_request_handler/browser_request_handler', () => {
4747
return { BrowserRequestHandler };
4848
});
4949

50-
vi.mock('../plugins/key_value_cache/reactNativeAsyncStorageCache', () => {
51-
const ReactNativeAsyncStorageCache = vi.fn();
52-
return { default: ReactNativeAsyncStorageCache };
50+
vi.mock('../utils/cache/async_storage_cache.react_native', async (importOriginal) => {
51+
const original: any = await importOriginal();
52+
const OriginalAsyncStorageCache = original.AsyncStorageCache;
53+
const MockAsyncStorageCache = vi.fn().mockImplementation(function (this: any, ...args) {
54+
Object.setPrototypeOf(this, new OriginalAsyncStorageCache(...args));
55+
});
56+
return { AsyncStorageCache: MockAsyncStorageCache };
5357
});
5458

5559
import { getPollingConfigManager, PollingConfigManagerConfig } from './config_manager_factory';
5660
import { createPollingProjectConfigManager } from './config_manager_factory.react_native';
5761
import { BrowserRequestHandler } from '../utils/http_request_handler/browser_request_handler';
58-
import ReactNativeAsyncStorageCache from '../plugins/key_value_cache/reactNativeAsyncStorageCache';
62+
import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_native';
63+
import { getMockSyncCache } from '../tests/mock/mock_cache';
5964

6065
describe('createPollingConfigManager', () => {
6166
const mockGetPollingConfigManager = vi.mocked(getPollingConfigManager);
6267
const MockBrowserRequestHandler = vi.mocked(BrowserRequestHandler);
63-
const MockReactNativeAsyncStorageCache = vi.mocked(ReactNativeAsyncStorageCache);
68+
const MockAsyncStorageCache = vi.mocked(AsyncStorageCache);
6469

6570
beforeEach(() => {
6671
mockGetPollingConfigManager.mockClear();
6772
MockBrowserRequestHandler.mockClear();
68-
MockReactNativeAsyncStorageCache.mockClear();
73+
MockAsyncStorageCache.mockClear();
6974
});
7075

7176
it('creates and returns the instance by calling getPollingConfigManager', () => {
@@ -110,7 +115,7 @@ describe('createPollingConfigManager', () => {
110115
createPollingProjectConfigManager(config);
111116

112117
expect(
113-
Object.is(mockGetPollingConfigManager.mock.calls[0][0].cache, MockReactNativeAsyncStorageCache.mock.instances[0])
118+
Object.is(mockGetPollingConfigManager.mock.calls[0][0].cache, MockAsyncStorageCache.mock.instances[0])
114119
).toBe(true);
115120
});
116121

@@ -123,7 +128,7 @@ describe('createPollingConfigManager', () => {
123128
autoUpdate: false,
124129
urlTemplate: 'urlTemplate',
125130
datafileAccessToken: 'datafileAccessToken',
126-
cache: { get: vi.fn(), set: vi.fn(), contains: vi.fn(), remove: vi.fn() },
131+
cache: getMockSyncCache(),
127132
};
128133

129134
createPollingProjectConfigManager(config);
@@ -133,38 +138,24 @@ describe('createPollingConfigManager', () => {
133138

134139
it('Should not throw error if a cache is present in the config, and async storage is not available', async () => {
135140
isAsyncStorageAvailable = false;
136-
const { default: ReactNativeAsyncStorageCache } = await vi.importActual<
137-
typeof import('../plugins/key_value_cache/reactNativeAsyncStorageCache')
138-
>('../plugins/key_value_cache/reactNativeAsyncStorageCache');
139141
const config = {
140142
sdkKey: 'sdkKey',
141143
requestHandler: { makeRequest: vi.fn() },
142-
cache: { get: vi.fn(), set: vi.fn(), contains: vi.fn(), remove: vi.fn() },
144+
cache: getMockSyncCache<string>(),
143145
};
144146

145-
MockReactNativeAsyncStorageCache.mockImplementationOnce(() => {
146-
return new ReactNativeAsyncStorageCache();
147-
});
148-
149147
expect(() => createPollingProjectConfigManager(config)).not.toThrow();
150148
isAsyncStorageAvailable = true;
151149
});
152150

153151
it('should throw an error if cache is not present in the config, and async storage is not available', async () => {
154152
isAsyncStorageAvailable = false;
155153

156-
const { default: ReactNativeAsyncStorageCache } = await vi.importActual<
157-
typeof import('../plugins/key_value_cache/reactNativeAsyncStorageCache')
158-
>('../plugins/key_value_cache/reactNativeAsyncStorageCache');
159154
const config = {
160155
sdkKey: 'sdkKey',
161156
requestHandler: { makeRequest: vi.fn() },
162157
};
163158

164-
MockReactNativeAsyncStorageCache.mockImplementationOnce(() => {
165-
return new ReactNativeAsyncStorageCache();
166-
});
167-
168159
expect(() => createPollingProjectConfigManager(config)).toThrowError(
169160
'Module not found: @react-native-async-storage/async-storage'
170161
);

lib/project_config/config_manager_factory.react_native.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@
1717
import { getPollingConfigManager, PollingConfigManagerConfig } from "./config_manager_factory";
1818
import { BrowserRequestHandler } from "../utils/http_request_handler/browser_request_handler";
1919
import { ProjectConfigManager } from "./project_config_manager";
20-
import ReactNativeAsyncStorageCache from "../plugins/key_value_cache/reactNativeAsyncStorageCache";
20+
import { AsyncStorageCache } from "../utils/cache/async_storage_cache.react_native";
2121

2222
export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): ProjectConfigManager => {
2323
const defaultConfig = {
2424
autoUpdate: true,
2525
requestHandler: new BrowserRequestHandler(),
26-
cache: config.cache || new ReactNativeAsyncStorageCache()
26+
cache: config.cache || new AsyncStorageCache(),
2727
};
2828

2929
return getPollingConfigManager({ ...defaultConfig, ...config });

lib/project_config/config_manager_factory.spec.ts

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,9 @@ import { ProjectConfigManagerImpl } from './project_config_manager';
3636
import { PollingDatafileManager } from './polling_datafile_manager';
3737
import { ExponentialBackoff, IntervalRepeater } from '../utils/repeater/repeater';
3838
import { getPollingConfigManager } from './config_manager_factory';
39-
import { DEFAULT_UPDATE_INTERVAL } from './constant';
39+
import { DEFAULT_UPDATE_INTERVAL, UPDATE_INTERVAL_BELOW_MINIMUM_MESSAGE } from './constant';
40+
import { getMockSyncCache } from '../tests/mock/mock_cache';
41+
import { LogLevel } from '../modules/logging';
4042

4143
describe('getPollingConfigManager', () => {
4244
const MockProjectConfigManagerImpl = vi.mocked(ProjectConfigManagerImpl);
@@ -73,7 +75,32 @@ describe('getPollingConfigManager', () => {
7375
};
7476
getPollingConfigManager(config);
7577
expect(MockIntervalRepeater.mock.calls[0][0]).toBe(DEFAULT_UPDATE_INTERVAL);
76-
expect(MockPollingDatafileManager.mock.calls[0][0].updateInterval).toBe(DEFAULT_UPDATE_INTERVAL);
78+
});
79+
80+
it('adds a startup log if the update interval is below the minimum', () => {
81+
const config = {
82+
sdkKey: 'abcd',
83+
requestHandler: { makeRequest: vi.fn() },
84+
updateInterval: 10000,
85+
};
86+
getPollingConfigManager(config);
87+
const startupLogs = MockPollingDatafileManager.mock.calls[0][0].startupLogs;
88+
expect(startupLogs).toEqual(expect.arrayContaining([{
89+
level: LogLevel.WARNING,
90+
message: UPDATE_INTERVAL_BELOW_MINIMUM_MESSAGE,
91+
params: [],
92+
}]));
93+
});
94+
95+
it('does not add any startup log if the update interval above the minimum', () => {
96+
const config = {
97+
sdkKey: 'abcd',
98+
requestHandler: { makeRequest: vi.fn() },
99+
updateInterval: 40000,
100+
};
101+
getPollingConfigManager(config);
102+
const startupLogs = MockPollingDatafileManager.mock.calls[0][0].startupLogs;
103+
expect(startupLogs).toEqual([]);
77104
});
78105

79106
it('uses the provided options', () => {
@@ -86,7 +113,7 @@ describe('getPollingConfigManager', () => {
86113
autoUpdate: true,
87114
urlTemplate: 'urlTemplate',
88115
datafileAccessToken: 'datafileAccessToken',
89-
cache: { get: vi.fn(), set: vi.fn(), contains: vi.fn(), remove: vi.fn() },
116+
cache: getMockSyncCache<string>(),
90117
};
91118

92119
getPollingConfigManager(config);
@@ -96,7 +123,6 @@ describe('getPollingConfigManager', () => {
96123
expect(MockPollingDatafileManager).toHaveBeenNthCalledWith(1, expect.objectContaining({
97124
sdkKey: config.sdkKey,
98125
autoUpdate: config.autoUpdate,
99-
updateInterval: config.updateInterval,
100126
urlTemplate: config.urlTemplate,
101127
datafileAccessToken: config.datafileAccessToken,
102128
requestHandler: config.requestHandler,

lib/project_config/config_manager_factory.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@ import { Transformer } from "../utils/type";
1919
import { DatafileManagerConfig } from "./datafile_manager";
2020
import { ProjectConfigManagerImpl, ProjectConfigManager } from "./project_config_manager";
2121
import { PollingDatafileManager } from "./polling_datafile_manager";
22-
import PersistentKeyValueCache from "../plugins/key_value_cache/persistentKeyValueCache";
22+
import { Cache } from "../utils/cache/cache";
2323
import { DEFAULT_UPDATE_INTERVAL } from './constant';
2424
import { ExponentialBackoff, IntervalRepeater } from "../utils/repeater/repeater";
25+
import { StartupLog } from "../service";
26+
import { MIN_UPDATE_INTERVAL, UPDATE_INTERVAL_BELOW_MINIMUM_MESSAGE } from './constant';
27+
import { LogLevel } from "../modules/logging";
2528

2629
export type StaticConfigManagerConfig = {
2730
datafile: string,
@@ -42,7 +45,7 @@ export type PollingConfigManagerConfig = {
4245
updateInterval?: number;
4346
urlTemplate?: string;
4447
datafileAccessToken?: string;
45-
cache?: PersistentKeyValueCache;
48+
cache?: Cache<string>;
4649
};
4750

4851
export type PollingConfigManagerFactoryOptions = PollingConfigManagerConfig & { requestHandler: RequestHandler };
@@ -55,15 +58,25 @@ export const getPollingConfigManager = (
5558
const backoff = new ExponentialBackoff(1000, updateInterval, 500);
5659
const repeater = new IntervalRepeater(updateInterval, backoff);
5760

61+
const startupLogs: StartupLog[] = []
62+
63+
if (updateInterval < MIN_UPDATE_INTERVAL) {
64+
startupLogs.push({
65+
level: LogLevel.WARNING,
66+
message: UPDATE_INTERVAL_BELOW_MINIMUM_MESSAGE,
67+
params: [],
68+
});
69+
}
70+
5871
const datafileManagerConfig: DatafileManagerConfig = {
5972
sdkKey: opt.sdkKey,
6073
autoUpdate: opt.autoUpdate,
61-
updateInterval: updateInterval,
6274
urlTemplate: opt.urlTemplate,
6375
datafileAccessToken: opt.datafileAccessToken,
6476
requestHandler: opt.requestHandler,
6577
cache: opt.cache,
6678
repeater,
79+
startupLogs,
6780
};
6881

6982
const datafileManager = new PollingDatafileManager(datafileManagerConfig);

lib/project_config/datafile_manager.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import { Service } from '../service';
17-
import PersistentKeyValueCache from '../plugins/key_value_cache/persistentKeyValueCache';
16+
import { Service, StartupLog } from '../service';
17+
import { Cache } from '../utils/cache/cache';
1818
import { RequestHandler } from '../utils/http_request_handler/http';
1919
import { Fn, Consumer } from '../utils/type';
2020
import { Repeater } from '../utils/repeater/repeater';
@@ -30,12 +30,11 @@ export type DatafileManagerConfig = {
3030
requestHandler: RequestHandler;
3131
autoUpdate?: boolean;
3232
sdkKey: string;
33-
/** Polling interval in milliseconds to check for datafile updates. */
34-
updateInterval?: number;
3533
urlTemplate?: string;
36-
cache?: PersistentKeyValueCache;
34+
cache?: Cache<string>;
3735
datafileAccessToken?: string;
3836
initRetry?: number;
3937
repeater: Repeater;
4038
logger?: LoggerFacade;
39+
startupLogs?: StartupLog[];
4140
}

lib/core/optimizely_config/index.tests.js renamed to lib/project_config/optimizely_config.tests.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@ import { assert } from 'chai';
1717
import { cloneDeep } from 'lodash';
1818
import sinon from 'sinon';
1919

20-
import { createOptimizelyConfig, OptimizelyConfig } from './';
21-
import { createProjectConfig } from '../../project_config/project_config';
20+
import { createOptimizelyConfig, OptimizelyConfig } from './optimizely_config';
21+
import { createProjectConfig } from './project_config';
2222
import {
2323
getTestProjectConfigWithFeatures,
2424
getTypedAudiencesConfig,
2525
getSimilarRuleKeyConfig,
2626
getSimilarExperimentKeyConfig,
2727
getDuplicateExperimentKeyConfig,
28-
} from '../../tests/test_data';
28+
} from '../tests/test_data';
2929

3030
var datafile = getTestProjectConfigWithFeatures();
3131
var typedAudienceDatafile = getTypedAudiencesConfig();

lib/core/optimizely_config/index.ts renamed to lib/project_config/optimizely_config.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import { LoggerFacade, getLogger } from '../../modules/logging';
17-
import { ProjectConfig } from '../../project_config/project_config';
18-
import { DEFAULT_OPERATOR_TYPES } from '../condition_tree_evaluator';
16+
import { LoggerFacade, getLogger } from '../modules/logging';
17+
import { ProjectConfig } from '../project_config/project_config';
18+
import { DEFAULT_OPERATOR_TYPES } from '../core/condition_tree_evaluator';
1919
import {
2020
Audience,
2121
Experiment,
@@ -32,7 +32,7 @@ import {
3232
Rollout,
3333
Variation,
3434
VariationVariable,
35-
} from '../../shared_types';
35+
} from '../shared_types';
3636

3737
interface FeatureVariablesMap {
3838
[key: string]: FeatureVariable[];

0 commit comments

Comments
 (0)
0