Skip to content

Commit 49f19a6

Browse files
authored
validate config manager cache (#1064)
1 parent 8fa0c9b commit 49f19a6

File tree

6 files changed

+92
-24
lines changed

6 files changed

+92
-24
lines changed

lib/event_processor/event_processor_factory.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,14 +79,14 @@ describe('getBatchEventProcessor', () => {
7979
defaultFlushInterval: 10000,
8080
defaultBatchSize: 10,
8181
eventStore: 'abc' as any,
82-
})).toThrow('Invalid event store');
82+
})).toThrow('Invalid store');
8383

8484
expect(() => getBatchEventProcessor({
8585
eventDispatcher: getMockEventDispatcher(),
8686
defaultFlushInterval: 10000,
8787
defaultBatchSize: 10,
8888
eventStore: 123 as any,
89-
})).toThrow('Invalid event store');
89+
})).toThrow('Invalid store');
9090

9191
expect(() => getBatchEventProcessor({
9292
eventDispatcher: getMockEventDispatcher(),

lib/event_processor/event_processor_factory.ts

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,13 @@ import { ForwardingEventProcessor } from "./forwarding_event_processor";
2323
import { BatchEventProcessor, DEFAULT_MAX_BACKOFF, DEFAULT_MIN_BACKOFF, EventWithId, RetryConfig } from "./batch_event_processor";
2424
import { AsyncPrefixStore, Store, SyncPrefixStore } from "../utils/cache/store";
2525
import { Maybe } from "../utils/type";
26+
import { validateStore } from "../utils/cache/store_validator";
2627

2728
export const INVALID_EVENT_DISPATCHER = 'Invalid event dispatcher';
2829

2930
export const FAILED_EVENT_RETRY_INTERVAL = 20 * 1000;
3031
export const EVENT_STORE_PREFIX = 'optly_event:';
3132

32-
export const INVALID_STORE = 'Invalid event store';
33-
export const INVALID_STORE_METHOD = 'Invalid store method %s';
34-
3533
export const getPrefixEventStore = (store: Store<string>): Store<EventWithId> => {
3634
if (store.operation === 'async') {
3735
return new AsyncPrefixStore<string, EventWithId>(
@@ -84,23 +82,6 @@ export const validateEventDispatcher = (eventDispatcher: EventDispatcher): void
8482
}
8583
}
8684

87-
const validateStore = (store: any) => {
88-
const errors = [];
89-
if (!store || typeof store !== 'object') {
90-
throw new Error(INVALID_STORE);
91-
}
92-
93-
for (const method of ['set', 'get', 'remove', 'getKeys']) {
94-
if (typeof store[method] !== 'function') {
95-
errors.push(INVALID_STORE_METHOD.replace('%s', method));
96-
}
97-
}
98-
99-
if (errors.length > 0) {
100-
throw new Error(errors.join(', '));
101-
}
102-
}
103-
10485
export const getBatchEventProcessor = (
10586
options: BatchEventProcessorFactoryOptions,
10687
EventProcessorConstructor: typeof BatchEventProcessor = BatchEventProcessor

lib/project_config/config_manager_factory.spec.ts

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2024, Optimizely
2+
* Copyright 2024-2025, Optimizely
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -53,6 +53,52 @@ describe('getPollingConfigManager', () => {
5353
MockExponentialBackoff.mockClear();
5454
});
5555

56+
it('should throw an error if the passed cache is not valid', () => {
57+
expect(() => getPollingConfigManager({
58+
sdkKey: 'sdkKey',
59+
requestHandler: { makeRequest: vi.fn() },
60+
cache: 1 as any,
61+
})).toThrow('Invalid store');
62+
63+
expect(() => getPollingConfigManager({
64+
sdkKey: 'sdkKey',
65+
requestHandler: { makeRequest: vi.fn() },
66+
cache: 'abc' as any,
67+
})).toThrow('Invalid store');
68+
69+
expect(() => getPollingConfigManager({
70+
sdkKey: 'sdkKey',
71+
requestHandler: { makeRequest: vi.fn() },
72+
cache: {} as any,
73+
})).toThrow('Invalid store method set, Invalid store method get, Invalid store method remove, Invalid store method getKeys');
74+
75+
expect(() => getPollingConfigManager({
76+
sdkKey: 'sdkKey',
77+
requestHandler: { makeRequest: vi.fn() },
78+
cache: { set: 'abc', get: 'abc', remove: 'abc', getKeys: 'abc' } as any,
79+
})).toThrow('Invalid store method set, Invalid store method get, Invalid store method remove, Invalid store method getKeys');
80+
81+
const noop = () => {};
82+
83+
expect(() => getPollingConfigManager({
84+
sdkKey: 'sdkKey',
85+
requestHandler: { makeRequest: vi.fn() },
86+
cache: { set: noop, get: 'abc', remove: 'abc', getKeys: 'abc' } as any,
87+
})).toThrow('Invalid store method get, Invalid store method remove, Invalid store method getKeys');
88+
89+
expect(() => getPollingConfigManager({
90+
sdkKey: 'sdkKey',
91+
requestHandler: { makeRequest: vi.fn() },
92+
cache: { set: noop, get: noop, remove: 'abc', getKeys: 'abc' } as any,
93+
})).toThrow('Invalid store method remove, Invalid store method getKeys');
94+
95+
expect(() => getPollingConfigManager({
96+
sdkKey: 'sdkKey',
97+
requestHandler: { makeRequest: vi.fn() },
98+
cache: { set: noop, get: noop, remove: noop, getKeys: 'abc' } as any,
99+
})).toThrow('Invalid store method getKeys');
100+
});
101+
56102
it('uses a repeater with exponential backoff for the datafileManager', () => {
57103
const config = {
58104
sdkKey: 'sdkKey',

lib/project_config/config_manager_factory.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { StartupLog } from "../service";
2525
import { MIN_UPDATE_INTERVAL, UPDATE_INTERVAL_BELOW_MINIMUM_MESSAGE } from './constant';
2626
import { LogLevel } from '../logging/logger'
2727
import { Store } from "../utils/cache/store";
28+
import { validateStore } from "../utils/cache/store_validator";
2829

2930
export const INVALID_CONFIG_MANAGER = "Invalid config manager";
3031

@@ -63,6 +64,10 @@ export type PollingConfigManagerFactoryOptions = PollingConfigManagerConfig & {
6364
export const getPollingConfigManager = (
6465
opt: PollingConfigManagerFactoryOptions
6566
): ProjectConfigManager => {
67+
if (opt.cache) {
68+
validateStore(opt.cache);
69+
}
70+
6671
const updateInterval = opt.updateInterval ?? DEFAULT_UPDATE_INTERVAL;
6772

6873
const backoff = new ExponentialBackoff(1000, updateInterval, 500);

lib/utils/cache/store_validator.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
2+
/**
3+
* Copyright 2025, Optimizely
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
export const INVALID_STORE = 'Invalid store';
18+
export const INVALID_STORE_METHOD = 'Invalid store method %s';
19+
20+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
21+
export const validateStore = (store: any): void => {
22+
const errors = [];
23+
if (!store || typeof store !== 'object') {
24+
throw new Error(INVALID_STORE);
25+
}
26+
27+
for (const method of ['set', 'get', 'remove', 'getKeys']) {
28+
if (typeof store[method] !== 'function') {
29+
errors.push(INVALID_STORE_METHOD.replace('%s', method));
30+
}
31+
}
32+
33+
if (errors.length > 0) {
34+
throw new Error(errors.join(', '));
35+
}
36+
}

vitest.config.mts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2024 Optimizely
2+
* Copyright 2024-2025, Optimizely
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.

0 commit comments

Comments
 (0)