Skip to content

Commit 755f048

Browse files
committed
tests and some restructuring/fixes
1 parent a8efb22 commit 755f048

File tree

5 files changed

+259
-86
lines changed

5 files changed

+259
-86
lines changed

packages/shared/sdk-server/__tests__/options/Configuration.test.ts

Lines changed: 112 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1-
import { LDOptions } from '../../src';
1+
import {
2+
ClientContext,
3+
DataSourceOptions,
4+
isStandardOptions,
5+
LDFeatureStore,
6+
LDOptions,
7+
PollingDataSourceOptions,
8+
StandardDataSourceOptions,
9+
} from '../../src';
210
import Configuration from '../../src/options/Configuration';
11+
import InMemoryFeatureStore from '../../src/store/InMemoryFeatureStore';
312
import TestLogger, { LogLevel } from '../Logger';
413

514
function withLogger(options: LDOptions): LDOptions {
@@ -25,15 +34,16 @@ describe.each([undefined, null, 'potat0', 17, [], {}])('constructed without opti
2534
expect(config.flushInterval).toBe(5);
2635
expect(config.logger).toBeUndefined();
2736
expect(config.offline).toBe(false);
28-
expect(config.pollInterval).toBe(30);
37+
expect((config.dataSystem.dataSource as StandardDataSourceOptions).pollInterval).toEqual(30);
2938
expect(config.privateAttributes).toStrictEqual([]);
3039
expect(config.proxyOptions).toBeUndefined();
3140
expect(config.sendEvents).toBe(true);
3241
expect(config.serviceEndpoints.streaming).toEqual('https://stream.launchdarkly.com');
3342
expect(config.serviceEndpoints.polling).toEqual('https://sdk.launchdarkly.com');
3443
expect(config.serviceEndpoints.events).toEqual('https://events.launchdarkly.com');
35-
expect(config.stream).toBe(true);
36-
expect(config.streamInitialReconnectDelay).toEqual(1);
44+
expect(
45+
(config.dataSystem.dataSource as StandardDataSourceOptions).streamInitialReconnectDelay,
46+
).toEqual(1);
3747
expect(config.tags.value).toBeUndefined();
3848
expect(config.timeout).toEqual(5);
3949
expect(config.tlsParams).toBeUndefined();
@@ -179,7 +189,9 @@ describe('when setting different options', () => {
179189
])('allow setting and validates pollInterval', (value, expected, warnings) => {
180190
// @ts-ignore
181191
const config = new Configuration(withLogger({ pollInterval: value }));
182-
expect(config.pollInterval).toEqual(expected);
192+
expect((config.dataSystem.dataSource as StandardDataSourceOptions).pollInterval).toEqual(
193+
expected,
194+
);
183195
expect(logger(config).getCount()).toEqual(warnings);
184196
});
185197

@@ -207,7 +219,7 @@ describe('when setting different options', () => {
207219
])('allows setting stream and validates stream', (value, expected, warnings) => {
208220
// @ts-ignore
209221
const config = new Configuration(withLogger({ stream: value }));
210-
expect(config.stream).toEqual(expected);
222+
expect(isStandardOptions(config.dataSystem.dataSource)).toEqual(expected);
211223
expect(logger(config).getCount()).toEqual(warnings);
212224
});
213225

@@ -409,7 +421,99 @@ describe('when setting different options', () => {
409421
]);
410422
});
411423

412-
it('drop', () => {
424+
it('drops invalid dataystem data source options and replaces with defaults', () => {
425+
const config = new Configuration(
426+
withLogger({
427+
dataSystem: { dataSource: { bogus: 'myBogusOptions' } as unknown as DataSourceOptions },
428+
}),
429+
);
430+
expect(isStandardOptions(config.dataSystem.dataSource)).toEqual(true);
431+
logger(config).expectMessages([
432+
{
433+
level: LogLevel.Warn,
434+
matches: /Config option "dataSource" should be of type DataSourceOptions/,
435+
},
436+
]);
437+
});
413438

414-
}
439+
it('validates the datasystem persitent store is a factory or object', () => {
440+
const config1 = new Configuration(
441+
withLogger({
442+
dataSystem: {
443+
persistentStore: () => new InMemoryFeatureStore(),
444+
},
445+
}),
446+
);
447+
expect(isStandardOptions(config1.dataSystem.dataSource)).toEqual(true);
448+
expect(logger(config1).getCount()).toEqual(0);
449+
450+
const config2 = new Configuration(
451+
withLogger({
452+
dataSystem: {
453+
persistentStore: 'bogus type' as unknown as LDFeatureStore,
454+
},
455+
}),
456+
);
457+
expect(isStandardOptions(config2.dataSystem.dataSource)).toEqual(true);
458+
logger(config2).expectMessages([
459+
{
460+
level: LogLevel.Warn,
461+
matches: /Config option "persistentStore" should be of type LDFeatureStore/,
462+
},
463+
]);
464+
});
465+
466+
it('it provides reasonable defaults when datasystem is provided, but some options are missing', () => {
467+
const config = new Configuration(
468+
withLogger({
469+
dataSystem: {},
470+
}),
471+
);
472+
expect(isStandardOptions(config.dataSystem.dataSource)).toEqual(true);
473+
expect(logger(config).getCount()).toEqual(0);
474+
});
475+
476+
it('it provides reasonable defaults within the dataSystem.dataSource options when they are missing', () => {
477+
const config = new Configuration(
478+
withLogger({
479+
dataSystem: { dataSource: { type: 'standard' } },
480+
}),
481+
);
482+
expect(isStandardOptions(config.dataSystem.dataSource)).toEqual(true);
483+
expect(logger(config).getCount()).toEqual(0);
484+
});
485+
486+
it('ignores deprecated top level options when dataSystem.dataSource options are provided', () => {
487+
const config = new Configuration(
488+
withLogger({
489+
pollInterval: 501, // should be ignored
490+
streamInitialReconnectDelay: 502, // should be ignored
491+
dataSystem: {
492+
dataSource: { type: 'standard', pollInterval: 100, streamInitialReconnectDelay: 200 }, // should be used
493+
},
494+
}),
495+
);
496+
expect(isStandardOptions(config.dataSystem.dataSource)).toEqual(true);
497+
expect((config.dataSystem.dataSource as StandardDataSourceOptions).pollInterval).toEqual(100);
498+
expect(
499+
(config.dataSystem.dataSource as StandardDataSourceOptions).streamInitialReconnectDelay,
500+
).toEqual(200);
501+
expect(logger(config).getCount()).toEqual(0);
502+
});
503+
504+
it('ignores top level featureStore in favor of the datasystem persitent store', () => {
505+
const shouldNotBeUsed = new InMemoryFeatureStore();
506+
const shouldBeUsed = new InMemoryFeatureStore();
507+
const config = new Configuration(
508+
withLogger({
509+
featureStore: shouldNotBeUsed,
510+
dataSystem: {
511+
persistentStore: shouldBeUsed,
512+
},
513+
}),
514+
);
515+
// @ts-ignore
516+
const result = config.dataSystem.featureStoreFactory(null);
517+
expect(result).toEqual(shouldBeUsed);
518+
});
415519
});

packages/shared/sdk-server/src/LDClientImpl.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ export default class LDClientImpl implements LDClient {
171171
const baseHeaders = defaultHeaders(_sdkKey, _platform.info, config.tags);
172172

173173
const clientContext = new ClientContext(_sdkKey, config, _platform);
174-
const featureStore = config.featureStoreFactory(clientContext);
174+
const featureStore = config.dataSystem.featureStoreFactory(clientContext);
175175

176176
const dataSourceUpdates = new DataSourceUpdates(featureStore, hasEventListeners, onUpdate);
177177

@@ -253,9 +253,9 @@ export default class LDClientImpl implements LDClient {
253253
);
254254
};
255255

256-
if (!(config.offline || config.useLdd)) {
256+
if (!(config.offline || config.dataSystem.useLdd)) {
257257
this._updateProcessor =
258-
config.updateProcessorFactory?.(
258+
config.dataSystem.updateProcessorFactory?.(
259259
clientContext,
260260
dataSourceUpdates,
261261
() => this._initSuccess(),

packages/shared/sdk-server/src/api/options/LDDataSystemOptions.ts

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { LDClientContext, subsystem } from '@launchdarkly/js-sdk-common';
22

33
import { LDDataSourceUpdates, LDFeatureStore } from '../subsystems';
4+
import { PersistentDataStore } from '../interfaces';
45

56
/**
67
* Configuration options for the Data System that the SDK uses to get and maintain flags and other
@@ -42,6 +43,29 @@ export interface LDDataSystemOptions {
4243
*/
4344
dataSource?: DataSourceOptions;
4445

46+
/**
47+
* Before data has arrived from LaunchDarkly, the SDK is able to evaluate flags using
48+
* data from the persistent store. Once fresh data has arrived from LaunchDarkly, the
49+
* SDK will no longer read from the persistent store, although it will keep it up-to-date
50+
* for future startups.
51+
*
52+
* Some implementations provide the store implementation object itself, while others
53+
* provide a factory function that creates the store implementation based on the SDK
54+
* configuration; this property accepts either.
55+
*
56+
* @param clientContext whose properties may be used to influence creation of the persistent store.
57+
*/
58+
persistentStore?: LDFeatureStore | ((clientContext: LDClientContext) => LDFeatureStore);
59+
60+
/**
61+
* Whether you are using the LaunchDarkly relay proxy in daemon mode.
62+
*
63+
* In this configuration, the client will not connect to LaunchDarkly to get feature flags,
64+
* but will instead get feature state from a database (Redis or another supported feature
65+
* store integration) that is populated by the relay. By default, this is false.
66+
*/
67+
useLdd?: boolean;
68+
4569
/**
4670
* A component that obtains feature flag data and puts it in the feature store. Setting
4771
* this supercedes {@link LDDataSystemOptions#dataSource}.
@@ -55,12 +79,6 @@ export interface LDDataSystemOptions {
5579
errorHandler?: (e: Error) => void,
5680
) => subsystem.LDStreamProcessor);
5781

58-
/**
59-
* Before data has arrived from LaunchDarkly, the SDK is able to evaluate flags using
60-
* data from the persistent store. Once fresh data is available, the SDK will no longer
61-
* read from the persistent store, although it will keep it up-to-date for future startups.
62-
*/
63-
persistentStore?: LDFeatureStore | ((clientContext: LDClientContext) => LDFeatureStore);
6482
}
6583

6684
export type DataSourceOptions =
@@ -125,13 +143,13 @@ export interface PollingDataSourceOptions {
125143
}
126144

127145
export function isStandardOptions(u: any): u is StandardDataSourceOptions {
128-
return u.kind === 'standard';
146+
return u.type === 'standard';
129147
}
130148

131149
export function isStreamingOptions(u: any): u is StreamingDataSourceOptions {
132-
return u.kind === 'streaming';
150+
return u.type === 'streaming';
133151
}
134152

135153
export function isPollingOptions(u: any): u is PollingDataSourceOptions {
136-
return u.kind === 'polling';
154+
return u.type === 'polling';
137155
}

packages/shared/sdk-server/src/api/options/LDOptions.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ export interface LDOptions {
6161
/**
6262
* A component that stores feature flags and related data received from LaunchDarkly.
6363
*
64+
* If you specify the {@link LDOptions#dataSystem}, this setting will be ignored.
65+
*
6466
* By default, this is an in-memory data structure. Database integrations are also
6567
* available, as described in the
6668
* [SDK features guide](https://docs.launchdarkly.com/sdk/concepts/data-stores).
@@ -69,8 +71,6 @@ export interface LDOptions {
6971
* provide a factory function that creates the store implementation based on the SDK
7072
* configuration; this property accepts either.
7173
*
72-
* If you specify the {@link LDOptions#dataSystem}, this setting will be ignored.
73-
*
7474
* @deprecated This is now superceded by {@link LDOptions#dataSystem} using property
7575
* {@link LDDataSystemOptions#persistentStore}. The scope of the {@link LDFeatureStore}
7676
* that you provide is being reduced in the next major version to only being responsible
@@ -169,11 +169,11 @@ export interface LDOptions {
169169
/**
170170
* Whether streaming mode should be used to receive flag updates.
171171
*
172+
* If you specify the {@link LDOptions#dataSystem}, this setting will be ignored.
173+
*
172174
* This is true by default. If you set it to false, the client will use polling.
173175
* Streaming should only be disabled on the advice of LaunchDarkly support.
174176
*
175-
* If you specify the {@link LDOptions#dataSystem}, this setting will be ignored.
176-
*
177177
* @deprecated This functionality is now controlled by {@link LDOptions#dataSystem}
178178
* by specifying {@link LDDataSystemOptions#dataSource} options. To opt out of
179179
* streaming, you can use the {@link PollingDataSourceOptions}. Streaming should
@@ -184,13 +184,14 @@ export interface LDOptions {
184184
/**
185185
* Sets the initial reconnect delay for the streaming connection, in seconds.
186186
*
187+
* If you specify the {@link LDOptions#dataSystem}, this setting will be ignored.
188+
*
187189
* The streaming service uses a backoff algorithm (with jitter) every time the connection needs
188190
* to be reestablished. The delay for the first reconnection will start near this value, and then
189191
* increase exponentially for any subsequent connection failures.
190192
*
191193
* The default value is 1.
192194
*
193-
* If you specify the {@link LDOptions#dataSystem}, this setting will be ignored.
194195
*
195196
* @deprecated This functionality is now controlled by {@link LDOptions#dataSystem}
196197
* by specifying {@link LDDataSystemOptions#dataSource} options. Specifying the reconnect
@@ -202,9 +203,14 @@ export interface LDOptions {
202203
/**
203204
* Whether you are using the LaunchDarkly relay proxy in daemon mode.
204205
*
206+
* If you specify the {@link LDOptions#dataSystem}, this setting will be ignored.
207+
*
205208
* In this configuration, the client will not connect to LaunchDarkly to get feature flags,
206209
* but will instead get feature state from a database (Redis or another supported feature
207210
* store integration) that is populated by the relay. By default, this is false.
211+
*
212+
* @deprecated This functionality is now controlled by {@link LDOptions#dataSystem}
213+
* by specifying {@link LDDataSystemOptions#useLdd}.
208214
*/
209215
useLdd?: boolean;
210216

0 commit comments

Comments
 (0)