Skip to content

Commit 7f3d292

Browse files
Todd AndersonTodd Anderson
authored andcommitted
chore: adds applyChanges to LDFeatureStore and impls. Adds TransactionalPersistentStore.
1 parent a3b816b commit 7f3d292

23 files changed

+1264
-173
lines changed

packages/sdk/fastly/src/api/EdgeFeatureStore.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import { InitMetadata } from '@launchdarkly/js-sdk-common/dist/esm/internal';
12
import type {
23
DataKind,
4+
internal,
35
LDFeatureStore,
46
LDFeatureStoreDataStorage,
57
LDFeatureStoreItem,
@@ -113,6 +115,16 @@ export class EdgeFeatureStore implements LDFeatureStore {
113115
}
114116

115117
init(allData: LDFeatureStoreDataStorage, callback: () => void): void {
118+
this.applyChanges(true, allData, callback);
119+
}
120+
121+
applyChanges(
122+
_basis: boolean,
123+
_data: LDFeatureStoreDataStorage,
124+
callback: () => void,
125+
_initMetadata?: internal.InitMetadata,
126+
_selector?: String,
127+
): void {
116128
callback();
117129
}
118130

packages/shared/akamai-edgeworker-sdk/src/featureStore/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,10 @@ export class EdgeFeatureStore implements LDFeatureStore {
128128
}
129129

130130
init(allData: LDFeatureStoreDataStorage, callback: () => void): void {
131+
this.applyChanges(true, allData, callback);
132+
}
133+
134+
applyChanges(basis: boolean, data: LDFeatureStoreDataStorage, callback: () => void): void {
131135
callback();
132136
}
133137

packages/shared/sdk-server-edge/src/api/EdgeFeatureStore.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type {
22
DataKind,
3+
internal,
34
LDFeatureStore,
45
LDFeatureStoreDataStorage,
56
LDFeatureStoreItem,
@@ -114,6 +115,16 @@ export class EdgeFeatureStore implements LDFeatureStore {
114115
}
115116

116117
init(allData: LDFeatureStoreDataStorage, callback: () => void): void {
118+
this.applyChanges(true, allData, callback);
119+
}
120+
121+
applyChanges(
122+
_basis: boolean,
123+
_data: LDFeatureStoreDataStorage,
124+
callback: () => void,
125+
_initMetadata?: internal.InitMetadata,
126+
_selector?: String,
127+
): void {
117128
callback();
118129
}
119130

packages/shared/sdk-server-edge/src/utils/mockFeatureStore.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const mockFeatureStore: LDFeatureStore = {
66
init: jest.fn(),
77
initialized: jest.fn(),
88
upsert: jest.fn(),
9+
applyChanges: jest.fn(),
910
get: jest.fn(),
1011
delete: jest.fn(),
1112
};
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { DataSourceErrorKind, LDPollingError, subsystem } from '../../src';
2+
import OneShotInitializerFDv2 from '../../src/data_sources/OneShotInitializerFDv2';
3+
import PollingProcessorFDv2 from '../../src/data_sources/PollingProcessorFDv2';
4+
import Requestor from '../../src/data_sources/Requestor';
5+
import TestLogger, { LogLevel } from '../Logger';
6+
7+
describe('given a one shot initializer', () => {
8+
const requestor = {
9+
requestAllData: jest.fn(),
10+
};
11+
const allEvents = {
12+
events: [
13+
{
14+
event: 'server-intent',
15+
data: { payloads: [{ code: 'xfer-full', id: 'mockId' }] },
16+
},
17+
{
18+
event: 'put-object',
19+
data: {
20+
kind: 'flag',
21+
key: 'flagA',
22+
version: 123,
23+
object: { objectFieldA: 'objectValueA' },
24+
},
25+
},
26+
{
27+
event: 'payload-transferred',
28+
data: { state: 'mockState', version: 1 },
29+
},
30+
],
31+
};
32+
const jsonData = JSON.stringify(allEvents);
33+
34+
let initializer: OneShotInitializerFDv2;
35+
const mockDataCallback = jest.fn();
36+
const mockStatusCallback = jest.fn();
37+
let testLogger: TestLogger;
38+
39+
beforeEach(() => {
40+
testLogger = new TestLogger();
41+
initializer = new OneShotInitializerFDv2(requestor as unknown as Requestor, testLogger);
42+
});
43+
44+
afterEach(() => {
45+
initializer.stop();
46+
jest.restoreAllMocks();
47+
});
48+
49+
it('makes no requests before being started', () => {
50+
expect(requestor.requestAllData).not.toHaveBeenCalled();
51+
});
52+
53+
it('polls immediately on start', () => {
54+
initializer.start(mockDataCallback, mockStatusCallback);
55+
expect(requestor.requestAllData).toHaveBeenCalledTimes(1);
56+
expect(mockDataCallback).not.toHaveBeenCalled();
57+
expect(mockStatusCallback).toHaveBeenNthCalledWith(1, subsystem.DataSourceState.Initializing);
58+
});
59+
60+
it('calls callback on success', () => {
61+
requestor.requestAllData = jest.fn((cb) => cb(undefined, jsonData));
62+
initializer.start(mockDataCallback, mockStatusCallback);
63+
expect(mockDataCallback).toHaveBeenNthCalledWith(1, true, {
64+
basis: true,
65+
id: `mockId`,
66+
state: `mockState`,
67+
updates: [
68+
{
69+
kind: `flag`,
70+
key: `flagA`,
71+
version: 123,
72+
object: { objectFieldA: 'objectValueA' },
73+
},
74+
],
75+
version: 1,
76+
});
77+
});
78+
});

packages/shared/sdk-server/__tests__/data_sources/PollingProcessor.test.ts

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
1-
import { ClientContext } from '@launchdarkly/js-sdk-common';
2-
31
import { LDFeatureStore } from '../../src';
42
import PollingProcessor from '../../src/data_sources/PollingProcessor';
53
import Requestor from '../../src/data_sources/Requestor';
6-
import Configuration from '../../src/options/Configuration';
74
import AsyncStoreFacade from '../../src/store/AsyncStoreFacade';
85
import InMemoryFeatureStore from '../../src/store/InMemoryFeatureStore';
96
import VersionedDataKinds from '../../src/store/VersionedDataKinds';
10-
import { createBasicPlatform } from '../createBasicPlatform';
117
import TestLogger, { LogLevel } from '../Logger';
128

139
describe('given an event processor', () => {
@@ -23,24 +19,19 @@ describe('given an event processor', () => {
2319

2420
let store: LDFeatureStore;
2521
let storeFacade: AsyncStoreFacade;
26-
let config: Configuration;
2722
let processor: PollingProcessor;
2823
let initSuccessHandler: jest.Mock;
2924

3025
beforeEach(() => {
3126
store = new InMemoryFeatureStore();
3227
storeFacade = new AsyncStoreFacade(store);
33-
config = new Configuration({
34-
featureStore: store,
35-
pollInterval: longInterval,
36-
logger: new TestLogger(),
37-
});
3828
initSuccessHandler = jest.fn();
3929

4030
processor = new PollingProcessor(
41-
config,
4231
requestor as unknown as Requestor,
43-
config.featureStoreFactory(new ClientContext('', config, createBasicPlatform())),
32+
longInterval,
33+
store,
34+
new TestLogger(),
4435
initSuccessHandler,
4536
);
4637
});
@@ -99,27 +90,22 @@ describe('given a polling processor with a short poll duration', () => {
9990
const jsonData = JSON.stringify(allData);
10091

10192
let store: LDFeatureStore;
102-
let config: Configuration;
93+
let testLogger: TestLogger;
10394
let processor: PollingProcessor;
10495
let initSuccessHandler: jest.Mock;
10596
let errorHandler: jest.Mock;
10697

10798
beforeEach(() => {
10899
store = new InMemoryFeatureStore();
109-
config = new Configuration({
110-
featureStore: store,
111-
pollInterval: shortInterval,
112-
logger: new TestLogger(),
113-
});
100+
testLogger = new TestLogger();
114101
initSuccessHandler = jest.fn();
115102
errorHandler = jest.fn();
116103

117-
// Configuration will not let us set this as low as needed for the test.
118-
Object.defineProperty(config, 'pollInterval', { value: 0.1 });
119104
processor = new PollingProcessor(
120-
config,
121105
requestor as unknown as Requestor,
122-
config.featureStoreFactory(new ClientContext('', config, createBasicPlatform())),
106+
shortInterval,
107+
store,
108+
testLogger,
123109
initSuccessHandler,
124110
errorHandler,
125111
);
@@ -158,7 +144,6 @@ describe('given a polling processor with a short poll duration', () => {
158144
expect(errorHandler).not.toBeCalled();
159145
setTimeout(() => {
160146
expect(requestor.requestAllData.mock.calls.length).toBeGreaterThanOrEqual(2);
161-
const testLogger = config.logger as TestLogger;
162147
expect(testLogger.getCount(LogLevel.Error)).toBe(0);
163148
expect(testLogger.getCount(LogLevel.Warn)).toBeGreaterThan(2);
164149
(done as jest.DoneCallback)();
@@ -176,7 +161,6 @@ describe('given a polling processor with a short poll duration', () => {
176161

177162
setTimeout(() => {
178163
expect(requestor.requestAllData.mock.calls.length).toBeGreaterThanOrEqual(2);
179-
const testLogger = config.logger as TestLogger;
180164
expect(testLogger.getCount(LogLevel.Error)).toBeGreaterThan(2);
181165
(done as jest.DoneCallback)();
182166
}, 300);
@@ -199,7 +183,6 @@ describe('given a polling processor with a short poll duration', () => {
199183

200184
setTimeout(() => {
201185
expect(requestor.requestAllData.mock.calls.length).toBe(1);
202-
const testLogger = config.logger as TestLogger;
203186
expect(testLogger.getCount(LogLevel.Error)).toBe(1);
204187
(done as jest.DoneCallback)();
205188
}, 300);

0 commit comments

Comments
 (0)