diff --git a/lib/project_config/config_manager_factory.browser.spec.ts b/lib/project_config/config_manager_factory.browser.spec.ts index bbabfb0ac..7141cc16c 100644 --- a/lib/project_config/config_manager_factory.browser.spec.ts +++ b/lib/project_config/config_manager_factory.browser.spec.ts @@ -30,6 +30,7 @@ vi.mock('../utils/http_request_handler/browser_request_handler', () => { import { getPollingConfigManager, PollingConfigManagerConfig, PollingConfigManagerFactoryOptions } from './config_manager_factory'; import { createPollingProjectConfigManager } from './config_manager_factory.browser'; import { BrowserRequestHandler } from '../utils/http_request_handler/browser_request_handler'; +import { getMockSyncCache } from '../tests/mock/mock_cache'; describe('createPollingConfigManager', () => { const mockGetPollingConfigManager = vi.mocked(getPollingConfigManager); @@ -76,7 +77,7 @@ describe('createPollingConfigManager', () => { autoUpdate: true, urlTemplate: 'urlTemplate', datafileAccessToken: 'datafileAccessToken', - cache: { get: vi.fn(), set: vi.fn(), contains: vi.fn(), remove: vi.fn() }, + cache: getMockSyncCache(), }; const projectConfigManager = createPollingProjectConfigManager(config); diff --git a/lib/project_config/config_manager_factory.node.spec.ts b/lib/project_config/config_manager_factory.node.spec.ts index 2667e5cf5..6ef8e04e0 100644 --- a/lib/project_config/config_manager_factory.node.spec.ts +++ b/lib/project_config/config_manager_factory.node.spec.ts @@ -30,7 +30,7 @@ vi.mock('../utils/http_request_handler/node_request_handler', () => { import { getPollingConfigManager, PollingConfigManagerConfig } from './config_manager_factory'; import { createPollingProjectConfigManager } from './config_manager_factory.node'; import { NodeRequestHandler } from '../utils/http_request_handler/node_request_handler'; -import { DEFAULT_AUTHENTICATED_URL_TEMPLATE, DEFAULT_URL_TEMPLATE } from './constant'; +import { getMockSyncCache } from '../tests/mock/mock_cache'; describe('createPollingConfigManager', () => { const mockGetPollingConfigManager = vi.mocked(getPollingConfigManager); @@ -77,7 +77,7 @@ describe('createPollingConfigManager', () => { autoUpdate: false, urlTemplate: 'urlTemplate', datafileAccessToken: 'datafileAccessToken', - cache: { get: vi.fn(), set: vi.fn(), contains: vi.fn(), remove: vi.fn() }, + cache: getMockSyncCache(), }; const projectConfigManager = createPollingProjectConfigManager(config); diff --git a/lib/project_config/config_manager_factory.react_native.spec.ts b/lib/project_config/config_manager_factory.react_native.spec.ts index 0ead808de..b047af03a 100644 --- a/lib/project_config/config_manager_factory.react_native.spec.ts +++ b/lib/project_config/config_manager_factory.react_native.spec.ts @@ -29,7 +29,7 @@ async function mockRequireAsyncStorage() { M._load_original = M._load; M._load = (uri: string, parent: string) => { if (uri === '@react-native-async-storage/async-storage') { - if (isAsyncStorageAvailable) return {}; + if (isAsyncStorageAvailable) return { default: {} }; throw new Error('Module not found: @react-native-async-storage/async-storage'); } return M._load_original(uri, parent); @@ -47,25 +47,30 @@ vi.mock('../utils/http_request_handler/browser_request_handler', () => { return { BrowserRequestHandler }; }); -vi.mock('../plugins/key_value_cache/reactNativeAsyncStorageCache', () => { - const ReactNativeAsyncStorageCache = vi.fn(); - return { default: ReactNativeAsyncStorageCache }; +vi.mock('../utils/cache/async_storage_cache.react_native', async (importOriginal) => { + const original: any = await importOriginal(); + const OriginalAsyncStorageCache = original.AsyncStorageCache; + const MockAsyncStorageCache = vi.fn().mockImplementation(function (this: any, ...args) { + Object.setPrototypeOf(this, new OriginalAsyncStorageCache(...args)); + }); + return { AsyncStorageCache: MockAsyncStorageCache }; }); import { getPollingConfigManager, PollingConfigManagerConfig } from './config_manager_factory'; import { createPollingProjectConfigManager } from './config_manager_factory.react_native'; import { BrowserRequestHandler } from '../utils/http_request_handler/browser_request_handler'; -import ReactNativeAsyncStorageCache from '../plugins/key_value_cache/reactNativeAsyncStorageCache'; +import { AsyncStorageCache } from '../utils/cache/async_storage_cache.react_native'; +import { getMockSyncCache } from '../tests/mock/mock_cache'; describe('createPollingConfigManager', () => { const mockGetPollingConfigManager = vi.mocked(getPollingConfigManager); const MockBrowserRequestHandler = vi.mocked(BrowserRequestHandler); - const MockReactNativeAsyncStorageCache = vi.mocked(ReactNativeAsyncStorageCache); + const MockAsyncStorageCache = vi.mocked(AsyncStorageCache); beforeEach(() => { mockGetPollingConfigManager.mockClear(); MockBrowserRequestHandler.mockClear(); - MockReactNativeAsyncStorageCache.mockClear(); + MockAsyncStorageCache.mockClear(); }); it('creates and returns the instance by calling getPollingConfigManager', () => { @@ -110,7 +115,7 @@ describe('createPollingConfigManager', () => { createPollingProjectConfigManager(config); expect( - Object.is(mockGetPollingConfigManager.mock.calls[0][0].cache, MockReactNativeAsyncStorageCache.mock.instances[0]) + Object.is(mockGetPollingConfigManager.mock.calls[0][0].cache, MockAsyncStorageCache.mock.instances[0]) ).toBe(true); }); @@ -123,7 +128,7 @@ describe('createPollingConfigManager', () => { autoUpdate: false, urlTemplate: 'urlTemplate', datafileAccessToken: 'datafileAccessToken', - cache: { get: vi.fn(), set: vi.fn(), contains: vi.fn(), remove: vi.fn() }, + cache: getMockSyncCache(), }; createPollingProjectConfigManager(config); @@ -133,19 +138,12 @@ describe('createPollingConfigManager', () => { it('Should not throw error if a cache is present in the config, and async storage is not available', async () => { isAsyncStorageAvailable = false; - const { default: ReactNativeAsyncStorageCache } = await vi.importActual< - typeof import('../plugins/key_value_cache/reactNativeAsyncStorageCache') - >('../plugins/key_value_cache/reactNativeAsyncStorageCache'); const config = { sdkKey: 'sdkKey', requestHandler: { makeRequest: vi.fn() }, - cache: { get: vi.fn(), set: vi.fn(), contains: vi.fn(), remove: vi.fn() }, + cache: getMockSyncCache(), }; - MockReactNativeAsyncStorageCache.mockImplementationOnce(() => { - return new ReactNativeAsyncStorageCache(); - }); - expect(() => createPollingProjectConfigManager(config)).not.toThrow(); isAsyncStorageAvailable = true; }); @@ -153,18 +151,11 @@ describe('createPollingConfigManager', () => { it('should throw an error if cache is not present in the config, and async storage is not available', async () => { isAsyncStorageAvailable = false; - const { default: ReactNativeAsyncStorageCache } = await vi.importActual< - typeof import('../plugins/key_value_cache/reactNativeAsyncStorageCache') - >('../plugins/key_value_cache/reactNativeAsyncStorageCache'); const config = { sdkKey: 'sdkKey', requestHandler: { makeRequest: vi.fn() }, }; - MockReactNativeAsyncStorageCache.mockImplementationOnce(() => { - return new ReactNativeAsyncStorageCache(); - }); - expect(() => createPollingProjectConfigManager(config)).toThrowError( 'Module not found: @react-native-async-storage/async-storage' ); diff --git a/lib/project_config/config_manager_factory.react_native.ts b/lib/project_config/config_manager_factory.react_native.ts index 984f3c9d0..17e71f045 100644 --- a/lib/project_config/config_manager_factory.react_native.ts +++ b/lib/project_config/config_manager_factory.react_native.ts @@ -17,13 +17,13 @@ import { getPollingConfigManager, PollingConfigManagerConfig } from "./config_manager_factory"; import { BrowserRequestHandler } from "../utils/http_request_handler/browser_request_handler"; import { ProjectConfigManager } from "./project_config_manager"; -import ReactNativeAsyncStorageCache from "../plugins/key_value_cache/reactNativeAsyncStorageCache"; +import { AsyncStorageCache } from "../utils/cache/async_storage_cache.react_native"; export const createPollingProjectConfigManager = (config: PollingConfigManagerConfig): ProjectConfigManager => { const defaultConfig = { autoUpdate: true, requestHandler: new BrowserRequestHandler(), - cache: config.cache || new ReactNativeAsyncStorageCache() + cache: config.cache || new AsyncStorageCache(), }; return getPollingConfigManager({ ...defaultConfig, ...config }); diff --git a/lib/project_config/config_manager_factory.spec.ts b/lib/project_config/config_manager_factory.spec.ts index a79b3ae1a..e30cbf33e 100644 --- a/lib/project_config/config_manager_factory.spec.ts +++ b/lib/project_config/config_manager_factory.spec.ts @@ -36,7 +36,9 @@ import { ProjectConfigManagerImpl } from './project_config_manager'; import { PollingDatafileManager } from './polling_datafile_manager'; import { ExponentialBackoff, IntervalRepeater } from '../utils/repeater/repeater'; import { getPollingConfigManager } from './config_manager_factory'; -import { DEFAULT_UPDATE_INTERVAL } from './constant'; +import { DEFAULT_UPDATE_INTERVAL, UPDATE_INTERVAL_BELOW_MINIMUM_MESSAGE } from './constant'; +import { getMockSyncCache } from '../tests/mock/mock_cache'; +import { LogLevel } from '../modules/logging'; describe('getPollingConfigManager', () => { const MockProjectConfigManagerImpl = vi.mocked(ProjectConfigManagerImpl); @@ -73,7 +75,32 @@ describe('getPollingConfigManager', () => { }; getPollingConfigManager(config); expect(MockIntervalRepeater.mock.calls[0][0]).toBe(DEFAULT_UPDATE_INTERVAL); - expect(MockPollingDatafileManager.mock.calls[0][0].updateInterval).toBe(DEFAULT_UPDATE_INTERVAL); + }); + + it('adds a startup log if the update interval is below the minimum', () => { + const config = { + sdkKey: 'abcd', + requestHandler: { makeRequest: vi.fn() }, + updateInterval: 10000, + }; + getPollingConfigManager(config); + const startupLogs = MockPollingDatafileManager.mock.calls[0][0].startupLogs; + expect(startupLogs).toEqual(expect.arrayContaining([{ + level: LogLevel.WARNING, + message: UPDATE_INTERVAL_BELOW_MINIMUM_MESSAGE, + params: [], + }])); + }); + + it('does not add any startup log if the update interval above the minimum', () => { + const config = { + sdkKey: 'abcd', + requestHandler: { makeRequest: vi.fn() }, + updateInterval: 40000, + }; + getPollingConfigManager(config); + const startupLogs = MockPollingDatafileManager.mock.calls[0][0].startupLogs; + expect(startupLogs).toEqual([]); }); it('uses the provided options', () => { @@ -86,7 +113,7 @@ describe('getPollingConfigManager', () => { autoUpdate: true, urlTemplate: 'urlTemplate', datafileAccessToken: 'datafileAccessToken', - cache: { get: vi.fn(), set: vi.fn(), contains: vi.fn(), remove: vi.fn() }, + cache: getMockSyncCache(), }; getPollingConfigManager(config); @@ -96,7 +123,6 @@ describe('getPollingConfigManager', () => { expect(MockPollingDatafileManager).toHaveBeenNthCalledWith(1, expect.objectContaining({ sdkKey: config.sdkKey, autoUpdate: config.autoUpdate, - updateInterval: config.updateInterval, urlTemplate: config.urlTemplate, datafileAccessToken: config.datafileAccessToken, requestHandler: config.requestHandler, diff --git a/lib/project_config/config_manager_factory.ts b/lib/project_config/config_manager_factory.ts index 4d1977663..8cde539fa 100644 --- a/lib/project_config/config_manager_factory.ts +++ b/lib/project_config/config_manager_factory.ts @@ -19,9 +19,12 @@ import { Transformer } from "../utils/type"; import { DatafileManagerConfig } from "./datafile_manager"; import { ProjectConfigManagerImpl, ProjectConfigManager } from "./project_config_manager"; import { PollingDatafileManager } from "./polling_datafile_manager"; -import PersistentKeyValueCache from "../plugins/key_value_cache/persistentKeyValueCache"; +import { Cache } from "../utils/cache/cache"; import { DEFAULT_UPDATE_INTERVAL } from './constant'; import { ExponentialBackoff, IntervalRepeater } from "../utils/repeater/repeater"; +import { StartupLog } from "../service"; +import { MIN_UPDATE_INTERVAL, UPDATE_INTERVAL_BELOW_MINIMUM_MESSAGE } from './constant'; +import { LogLevel } from "../modules/logging"; export type StaticConfigManagerConfig = { datafile: string, @@ -42,7 +45,7 @@ export type PollingConfigManagerConfig = { updateInterval?: number; urlTemplate?: string; datafileAccessToken?: string; - cache?: PersistentKeyValueCache; + cache?: Cache; }; export type PollingConfigManagerFactoryOptions = PollingConfigManagerConfig & { requestHandler: RequestHandler }; @@ -55,15 +58,25 @@ export const getPollingConfigManager = ( const backoff = new ExponentialBackoff(1000, updateInterval, 500); const repeater = new IntervalRepeater(updateInterval, backoff); + const startupLogs: StartupLog[] = [] + + if (updateInterval < MIN_UPDATE_INTERVAL) { + startupLogs.push({ + level: LogLevel.WARNING, + message: UPDATE_INTERVAL_BELOW_MINIMUM_MESSAGE, + params: [], + }); + } + const datafileManagerConfig: DatafileManagerConfig = { sdkKey: opt.sdkKey, autoUpdate: opt.autoUpdate, - updateInterval: updateInterval, urlTemplate: opt.urlTemplate, datafileAccessToken: opt.datafileAccessToken, requestHandler: opt.requestHandler, cache: opt.cache, repeater, + startupLogs, }; const datafileManager = new PollingDatafileManager(datafileManagerConfig); diff --git a/lib/project_config/datafile_manager.ts b/lib/project_config/datafile_manager.ts index 32798495e..3f38ea53c 100644 --- a/lib/project_config/datafile_manager.ts +++ b/lib/project_config/datafile_manager.ts @@ -13,8 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Service } from '../service'; -import PersistentKeyValueCache from '../plugins/key_value_cache/persistentKeyValueCache'; +import { Service, StartupLog } from '../service'; +import { Cache } from '../utils/cache/cache'; import { RequestHandler } from '../utils/http_request_handler/http'; import { Fn, Consumer } from '../utils/type'; import { Repeater } from '../utils/repeater/repeater'; @@ -30,12 +30,11 @@ export type DatafileManagerConfig = { requestHandler: RequestHandler; autoUpdate?: boolean; sdkKey: string; - /** Polling interval in milliseconds to check for datafile updates. */ - updateInterval?: number; urlTemplate?: string; - cache?: PersistentKeyValueCache; + cache?: Cache; datafileAccessToken?: string; initRetry?: number; repeater: Repeater; logger?: LoggerFacade; + startupLogs?: StartupLog[]; } diff --git a/lib/core/optimizely_config/index.tests.js b/lib/project_config/optimizely_config.tests.js similarity index 99% rename from lib/core/optimizely_config/index.tests.js rename to lib/project_config/optimizely_config.tests.js index d4100e0da..22d2b95f3 100644 --- a/lib/core/optimizely_config/index.tests.js +++ b/lib/project_config/optimizely_config.tests.js @@ -17,15 +17,15 @@ import { assert } from 'chai'; import { cloneDeep } from 'lodash'; import sinon from 'sinon'; -import { createOptimizelyConfig, OptimizelyConfig } from './'; -import { createProjectConfig } from '../../project_config/project_config'; +import { createOptimizelyConfig, OptimizelyConfig } from './optimizely_config'; +import { createProjectConfig } from './project_config'; import { getTestProjectConfigWithFeatures, getTypedAudiencesConfig, getSimilarRuleKeyConfig, getSimilarExperimentKeyConfig, getDuplicateExperimentKeyConfig, -} from '../../tests/test_data'; +} from '../tests/test_data'; var datafile = getTestProjectConfigWithFeatures(); var typedAudienceDatafile = getTypedAudiencesConfig(); diff --git a/lib/core/optimizely_config/index.ts b/lib/project_config/optimizely_config.ts similarity index 98% rename from lib/core/optimizely_config/index.ts rename to lib/project_config/optimizely_config.ts index d8987b6c7..52eeb016c 100644 --- a/lib/core/optimizely_config/index.ts +++ b/lib/project_config/optimizely_config.ts @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { LoggerFacade, getLogger } from '../../modules/logging'; -import { ProjectConfig } from '../../project_config/project_config'; -import { DEFAULT_OPERATOR_TYPES } from '../condition_tree_evaluator'; +import { LoggerFacade, getLogger } from '../modules/logging'; +import { ProjectConfig } from '../project_config/project_config'; +import { DEFAULT_OPERATOR_TYPES } from '../core/condition_tree_evaluator'; import { Audience, Experiment, @@ -32,7 +32,7 @@ import { Rollout, Variation, VariationVariable, -} from '../../shared_types'; +} from '../shared_types'; interface FeatureVariablesMap { [key: string]: FeatureVariable[]; diff --git a/lib/project_config/polling_datafile_manager.spec.ts b/lib/project_config/polling_datafile_manager.spec.ts index 8e12ac3f5..3efae54d7 100644 --- a/lib/project_config/polling_datafile_manager.spec.ts +++ b/lib/project_config/polling_datafile_manager.spec.ts @@ -18,67 +18,45 @@ import { describe, it, expect, vi } from 'vitest'; import { PollingDatafileManager} from './polling_datafile_manager'; import { getMockRepeater } from '../tests/mock/mock_repeater'; import { getMockAbortableRequest, getMockRequestHandler } from '../tests/mock/mock_request_handler'; -import PersistentKeyValueCache from '../../lib/plugins/key_value_cache/persistentKeyValueCache'; import { getMockLogger } from '../tests/mock/mock_logger'; import { DEFAULT_AUTHENTICATED_URL_TEMPLATE, DEFAULT_URL_TEMPLATE, MIN_UPDATE_INTERVAL, UPDATE_INTERVAL_BELOW_MINIMUM_MESSAGE } from './constant'; import { resolvablePromise } from '../utils/promise/resolvablePromise'; -import { ServiceState } from '../service'; -import exp from 'constants'; - -const testCache = (): PersistentKeyValueCache => ({ - get(key: string): Promise { - let val = undefined; - switch (key) { - case 'opt-datafile-keyThatExists': - val = JSON.stringify({ name: 'keyThatExists' }); - break; - } - return Promise.resolve(val); - }, - - set(): Promise { - return Promise.resolve(); - }, - - contains(): Promise { - return Promise.resolve(false); - }, - - remove(): Promise { - return Promise.resolve(false); - }, -}); +import { ServiceState, StartupLog } from '../service'; +import { getMockSyncCache, getMockAsyncCache } from '../tests/mock/mock_cache'; +import { LogLevel } from '../modules/logging'; describe('PollingDatafileManager', () => { it('should log polling interval below MIN_UPDATE_INTERVAL', () => { const repeater = getMockRepeater(); const requestHandler = getMockRequestHandler(); const logger = getMockLogger(); - const manager = new PollingDatafileManager({ - repeater, - requestHandler, - sdkKey: '123', - logger, - updateInterval: MIN_UPDATE_INTERVAL - 1000, - }); - manager.start(); - expect(logger.warn).toHaveBeenCalledWith(UPDATE_INTERVAL_BELOW_MINIMUM_MESSAGE); - }); - it('should not log polling interval above MIN_UPDATE_INTERVAL', () => { - const repeater = getMockRepeater(); - const requestHandler = getMockRequestHandler(); - const logger = getMockLogger(); + const startupLogs: StartupLog[] = [ + { + level: LogLevel.WARNING, + message: 'warn message', + params: [1, 2] + }, + { + level: LogLevel.ERROR, + message: 'error message', + params: [3, 4] + }, + ]; + const manager = new PollingDatafileManager({ repeater, requestHandler, sdkKey: '123', logger, - updateInterval: MIN_UPDATE_INTERVAL + 1000, + startupLogs, }); + manager.start(); - expect(logger.warn).not.toHaveBeenCalled(); + expect(logger.log).toHaveBeenNthCalledWith(1, LogLevel.WARNING, 'warn message', 1, 2); + expect(logger.log).toHaveBeenNthCalledWith(2, LogLevel.ERROR, 'error message', 3, 4); }); + it('starts the repeater with immediateExecution on start', () => { const repeater = getMockRepeater(); @@ -96,11 +74,14 @@ describe('PollingDatafileManager', () => { it('uses cached version of datafile, resolves onRunning() and calls onUpdate handlers while datafile fetch request waits', async () => { const repeater = getMockRepeater(); const requestHandler = getMockRequestHandler(); // response promise is pending + const cache = getMockAsyncCache(); + await cache.set('opt-datafile-keyThatExists', JSON.stringify({ name: 'keyThatExists' })); + const manager = new PollingDatafileManager({ repeater, requestHandler, sdkKey: 'keyThatExists', - cache: testCache(), + cache, }); manager.start(); @@ -117,12 +98,15 @@ describe('PollingDatafileManager', () => { const requestHandler = getMockRequestHandler(); const mockResponse = getMockAbortableRequest(Promise.reject('test error')); requestHandler.makeRequest.mockReturnValueOnce(mockResponse); - + + const cache = getMockAsyncCache(); + await cache.set('opt-datafile-keyThatExists', JSON.stringify({ name: 'keyThatExists' })); + const manager = new PollingDatafileManager({ repeater, requestHandler, sdkKey: 'keyThatExists', - cache: testCache(), + cache, }); manager.start(); @@ -139,14 +123,17 @@ describe('PollingDatafileManager', () => { it('uses cached version of datafile, then calls onUpdate when fetch request succeeds after the cache read', async () => { const repeater = getMockRepeater(); const requestHandler = getMockRequestHandler(); + const cache = getMockAsyncCache(); + await cache.set('opt-datafile-keyThatExists', JSON.stringify({ name: 'keyThatExists' })); const mockResponse = getMockAbortableRequest(); requestHandler.makeRequest.mockReturnValueOnce(mockResponse); + const manager = new PollingDatafileManager({ repeater, requestHandler, sdkKey: 'keyThatExists', - cache: testCache(), + cache, }); manager.start(); @@ -170,10 +157,11 @@ describe('PollingDatafileManager', () => { const mockResponse = getMockAbortableRequest(Promise.resolve({ statusCode: 200, body: '{"foo": "bar"}', headers: {} })); requestHandler.makeRequest.mockReturnValueOnce(mockResponse); - const cache = testCache(); + const cache = getMockAsyncCache(); // this will be resolved after the requestHandler response is resolved const cachePromise = resolvablePromise(); - cache.get = () => cachePromise.promise; + const getSpy = vi.spyOn(cache, 'get'); + getSpy.mockReturnValueOnce(cachePromise.promise); const manager = new PollingDatafileManager({ repeater, @@ -337,7 +325,7 @@ describe('PollingDatafileManager', () => { requestHandler, sdkKey: 'keyThatDoesNotExists', initRetry: 5, - cache: testCache(), + cache: getMockAsyncCache(), }); manager.start(); @@ -488,7 +476,7 @@ describe('PollingDatafileManager', () => { const mockResponse = getMockAbortableRequest(Promise.resolve({ statusCode: 200, body: '{"foo": "bar"}', headers: {} })); requestHandler.makeRequest.mockReturnValueOnce(mockResponse); - const cache = testCache(); + const cache = getMockAsyncCache(); const spy = vi.spyOn(cache, 'set'); const manager = new PollingDatafileManager({ @@ -551,7 +539,7 @@ describe('PollingDatafileManager', () => { requestHandler.makeRequest.mockReturnValueOnce(mockResponse1) .mockReturnValueOnce(mockResponse2).mockReturnValueOnce(mockResponse3); - const cache = testCache(); + const cache = getMockAsyncCache(); const spy = vi.spyOn(cache, 'set'); const manager = new PollingDatafileManager({ diff --git a/lib/project_config/polling_datafile_manager.ts b/lib/project_config/polling_datafile_manager.ts index 585cb0949..f7223fc00 100644 --- a/lib/project_config/polling_datafile_manager.ts +++ b/lib/project_config/polling_datafile_manager.ts @@ -13,23 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import { LoggerFacade } from '../modules/logging'; import { sprintf } from '../utils/fns'; import { DatafileManager, DatafileManagerConfig } from './datafile_manager'; import { EventEmitter } from '../utils/event_emitter/event_emitter'; -import { DEFAULT_AUTHENTICATED_URL_TEMPLATE, DEFAULT_URL_TEMPLATE, MIN_UPDATE_INTERVAL, UPDATE_INTERVAL_BELOW_MINIMUM_MESSAGE } from './constant'; -import PersistentKeyValueCache from '../plugins/key_value_cache/persistentKeyValueCache'; - +import { DEFAULT_AUTHENTICATED_URL_TEMPLATE, DEFAULT_URL_TEMPLATE } from './constant'; +import { Cache } from '../utils/cache/cache'; import { BaseService, ServiceState } from '../service'; import { RequestHandler, AbortableRequest, Headers, Response } from '../utils/http_request_handler/http'; import { Repeater } from '../utils/repeater/repeater'; import { Consumer, Fn } from '../utils/type'; -import { url } from 'inspector'; - -function isSuccessStatusCode(statusCode: number): boolean { - return statusCode >= 200 && statusCode < 400; -} +import { isSuccessStatusCode } from '../utils/http_request_handler/http_util'; export class PollingDatafileManager extends BaseService implements DatafileManager { private requestHandler: RequestHandler; @@ -38,18 +31,16 @@ export class PollingDatafileManager extends BaseService implements DatafileManag private autoUpdate: boolean; private initRetryRemaining?: number; private repeater: Repeater; - private updateInterval?: number; - private lastResponseLastModified?: string; private datafileUrl: string; private currentRequest?: AbortableRequest; private cacheKey: string; - private cache?: PersistentKeyValueCache; + private cache?: Cache; private sdkKey: string; private datafileAccessToken?: string; constructor(config: DatafileManagerConfig) { - super(); + super(config.startupLogs); const { autoUpdate = false, sdkKey, @@ -59,7 +50,6 @@ export class PollingDatafileManager extends BaseService implements DatafileManag initRetry, repeater, requestHandler, - updateInterval, logger, } = config; this.cache = cache; @@ -71,7 +61,6 @@ export class PollingDatafileManager extends BaseService implements DatafileManag this.autoUpdate = autoUpdate; this.initRetryRemaining = initRetry; this.repeater = repeater; - this.updateInterval = updateInterval; this.logger = logger; const urlTemplateToUse = urlTemplate || @@ -92,10 +81,7 @@ export class PollingDatafileManager extends BaseService implements DatafileManag return; } - if (this.updateInterval !== undefined && this.updateInterval < MIN_UPDATE_INTERVAL) { - this.logger?.warn(UPDATE_INTERVAL_BELOW_MINIMUM_MESSAGE); - } - + super.start(); this.state = ServiceState.Starting; this.setDatafileFromCacheIfAvailable(); this.repeater.setTask(this.syncDatafile.bind(this)); @@ -230,11 +216,17 @@ export class PollingDatafileManager extends BaseService implements DatafileManag } } - private setDatafileFromCacheIfAvailable(): void { - this.cache?.get(this.cacheKey).then(datafile => { - if (datafile && this.isStarting()) { + private async setDatafileFromCacheIfAvailable(): Promise { + if (!this.cache) { + return; + } + try { + const datafile = await this.cache.get(this.cacheKey); + if (datafile && this.isStarting()) { this.handleDatafile(datafile); } - }).catch(() => {}); + } catch { + // ignore error + } } } diff --git a/lib/project_config/project_config_manager.ts b/lib/project_config/project_config_manager.ts index 46c79238c..b71ef1f39 100644 --- a/lib/project_config/project_config_manager.ts +++ b/lib/project_config/project_config_manager.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import { LoggerFacade } from '../modules/logging'; -import { createOptimizelyConfig } from '../core/optimizely_config'; +import { createOptimizelyConfig } from './optimizely_config'; import { OptimizelyConfig } from '../shared_types'; import { DatafileManager } from './datafile_manager'; import { ProjectConfig, toDatafile, tryCreatingProjectConfig } from './project_config';