Skip to content

Commit b8b12cd

Browse files
Update data loader to support memberships
1 parent 248b1b1 commit b8b12cd

File tree

5 files changed

+70
-34
lines changed

5 files changed

+70
-34
lines changed

src/sdkFactory/index.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { IBasicClient, SplitIO } from '../types';
77
import { validateAndTrackApiKey } from '../utils/inputValidation/apiKey';
88
import { createLoggerAPI } from '../logger/sdkLogger';
99
import { NEW_FACTORY, RETRIEVE_MANAGER } from '../logger/constants';
10-
import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED } from '../readiness/constants';
10+
import { SDK_SPLITS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_SPLITS_CACHE_LOADED } from '../readiness/constants';
1111
import { objectAssign } from '../utils/lang/objectAssign';
1212
import { strategyDebugFactory } from '../trackers/strategy/strategyDebug';
1313
import { strategyOptimizedFactory } from '../trackers/strategy/strategyOptimized';
@@ -43,7 +43,7 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
4343

4444
const storage = storageFactory({
4545
settings,
46-
onReadyCb: (error) => {
46+
onReadyCb(error) {
4747
if (error) {
4848
// If storage fails to connect, SDK_READY_TIMED_OUT event is emitted immediately. Review when timeout and non-recoverable errors are reworked
4949
readiness.timeout();
@@ -52,6 +52,9 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ICsSDK | SplitIO.
5252
readiness.splits.emit(SDK_SPLITS_ARRIVED);
5353
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
5454
},
55+
onReadyFromCacheCb() {
56+
readiness.splits.emit(SDK_SPLITS_CACHE_LOADED);
57+
}
5558
});
5659

5760
const clients: Record<string, IBasicClient> = {};

src/storages/__tests__/dataLoader.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ test('loadData & getSnapshot', () => {
1111
const serverStorage = InMemoryStorageFactory({ settings: fullSettings });
1212
serverStorage.splits.setChangeNumber(123); // @ts-expect-error
1313
serverStorage.splits.addSplits([['split1', { name: 'split1' }]]);
14-
serverStorage.segments.addToSegment('segment1', [fullSettings.core.key as string]);
14+
serverStorage.segments.update('segment1', [fullSettings.core.key as string], [], 123);
1515

1616
const preloadedData = dataLoader.getSnapshot(serverStorage, [fullSettings.core.key as string]);
1717

@@ -25,7 +25,7 @@ test('loadData & getSnapshot', () => {
2525
expect(preloadedData).toEqual({
2626
since: 123,
2727
splitsData: [{ name: 'split1' }],
28-
mySegmentsData: { [fullSettings.core.key as string]: ['segment1'] },
28+
membershipsData: { [fullSettings.core.key as string]: { ms: { k: [{ n: 'segment1' }] }, ls: { k: [] } } },
2929
segmentsData: undefined
3030
});
3131
});

src/storages/dataLoader.ts

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { SplitIO } from '../types';
22
import { ISegmentsCacheSync, ISplitsCacheSync, IStorageSync } from './types';
3-
import { setToArray, ISet } from '../utils/lang/sets';
3+
import { setToArray } from '../utils/lang/sets';
44
import { getMatching } from '../utils/key';
5+
import { IMembershipsResponse, IMySegmentsResponse } from '../dtos/types';
56

67
/**
78
* Storage-agnostic adaptation of `loadDataIntoLocalStorage` function
@@ -37,19 +38,26 @@ export function loadData(preloadedData: SplitIO.PreloadedData, storage: { splits
3738
}
3839

3940
if (matchingKey) { // add mySegments data (client-side)
40-
let mySegmentsData = preloadedData.mySegmentsData && preloadedData.mySegmentsData[matchingKey];
41-
if (!mySegmentsData) {
42-
// segmentsData in an object where the property is the segment name and the pertaining value is a stringified object that contains the `added` array of userIds
43-
mySegmentsData = Object.keys(segmentsData).filter(segmentName => {
44-
const matchingKeys = segmentsData[segmentName];
45-
return matchingKeys.indexOf(matchingKey) > -1;
46-
});
41+
let membershipsData = preloadedData.membershipsData && preloadedData.membershipsData[matchingKey];
42+
if (!membershipsData && segmentsData) {
43+
membershipsData = {
44+
ms: {
45+
k: Object.keys(segmentsData).filter(segmentName => {
46+
const segmentKeys = segmentsData[segmentName];
47+
return segmentKeys.indexOf(matchingKey) > -1;
48+
}).map(segmentName => ({ n: segmentName }))
49+
}
50+
};
4751
}
48-
storage.segments.resetSegments({ k: mySegmentsData.map(s => ({ n: s })) });
52+
if (membershipsData) {
53+
if (membershipsData.ms) storage.segments.resetSegments(membershipsData.ms);
54+
if (membershipsData.ls && storage.largeSegments) storage.largeSegments.resetSegments(membershipsData.ls);
55+
}
56+
4957
} else { // add segments data (server-side)
50-
Object.keys(segmentsData).filter(segmentName => {
51-
const matchingKeys = segmentsData[segmentName];
52-
storage.segments.addToSegment(segmentName, matchingKeys);
58+
Object.keys(segmentsData).forEach(segmentName => {
59+
const segmentKeys = segmentsData[segmentName];
60+
storage.segments.update(segmentName, segmentKeys, [], -1);
5361
});
5462
}
5563
}
@@ -62,22 +70,43 @@ export function getSnapshot(storage: IStorageSync, userKeys?: SplitIO.SplitKey[]
6270
segmentsData: userKeys ?
6371
undefined : // @ts-ignore accessing private prop
6472
Object.keys(storage.segments.segmentCache).reduce((prev, cur) => { // @ts-ignore accessing private prop
65-
prev[cur] = setToArray(storage.segments.segmentCache[cur] as ISet<string>);
73+
prev[cur] = setToArray(storage.segments.segmentCache[cur] as Set<string>);
6674
return prev;
6775
}, {}),
68-
mySegmentsData: userKeys ?
69-
userKeys.reduce<Record<string, string[]>>((prev, userKey) => {
70-
prev[getMatching(userKey)] = storage.shared ?
76+
membershipsData: userKeys ?
77+
userKeys.reduce<Record<string, IMembershipsResponse>>((prev, userKey) => {
78+
if (storage.shared) {
7179
// Client-side segments
7280
// @ts-ignore accessing private prop
73-
Object.keys(storage.shared(userKey).segments.segmentCache) :
74-
// Server-side segments
75-
// @ts-ignore accessing private prop
76-
Object.keys(storage.segments.segmentCache).reduce<string[]>((prev, segmentName) => { // @ts-ignore accessing private prop
77-
return storage.segments.segmentCache[segmentName].has(userKey) ?
78-
prev.concat(segmentName) :
79-
prev;
80-
}, []);
81+
const sharedStorage = storage.shared(userKey);
82+
prev[getMatching(userKey)] = {
83+
ms: {
84+
// @ts-ignore accessing private prop
85+
k: Object.keys(sharedStorage.segments.segmentCache).map(segmentName => ({ n: segmentName })),
86+
// cn: sharedStorage.segments.getChangeNumber()
87+
},
88+
ls: sharedStorage.largeSegments ? {
89+
// @ts-ignore accessing private prop
90+
k: Object.keys(sharedStorage.largeSegments.segmentCache).map(segmentName => ({ n: segmentName })),
91+
// cn: sharedStorage.largeSegments.getChangeNumber()
92+
} : undefined
93+
};
94+
} else {
95+
prev[getMatching(userKey)] = {
96+
ms: {
97+
// Server-side segments
98+
// @ts-ignore accessing private prop
99+
k: Object.keys(storage.segments.segmentCache).reduce<IMySegmentsResponse['k']>((prev, segmentName) => { // @ts-ignore accessing private prop
100+
return storage.segments.segmentCache[segmentName].has(userKey) ?
101+
prev!.concat({ n: segmentName }) :
102+
prev;
103+
}, [])
104+
},
105+
ls: {
106+
k: []
107+
}
108+
};
109+
}
81110
return prev;
82111
}, {}) :
83112
undefined

src/storages/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,10 @@ export interface IStorageFactoryParams {
495495
* It is meant for emitting SDK_READY event in consumer mode, and waiting before using the storage in the synchronizer.
496496
*/
497497
onReadyCb: (error?: any) => void,
498+
/**
499+
* It is meant for emitting SDK_READY_FROM_CACHE event in standalone mode with preloaded data
500+
*/
501+
onReadyFromCacheCb: () => void,
498502
}
499503

500504
export type StorageType = 'MEMORY' | 'LOCALSTORAGE' | 'REDIS' | 'PLUGGABLE';

src/types.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ISplit, ISplitFiltersValidation } from './dtos/types';
1+
import { IMembershipsResponse, ISplit, ISplitFiltersValidation } from './dtos/types';
22
import { IIntegration, IIntegrationFactoryParams } from './integrations/types';
33
import { ILogger } from './logger/types';
44
import { ISdkFactoryContext } from './sdkFactory/types';
@@ -783,15 +783,15 @@ export namespace SplitIO {
783783
*/
784784
splitsData: ISplit[],
785785
/**
786-
* Optional map of user keys to their list of segments.
786+
* Optional map of user keys to their memberships.
787787
* @TODO rename to memberships
788788
*/
789-
mySegmentsData?: {
790-
[key: string]: string[]
789+
membershipsData?: {
790+
[key: string]: IMembershipsResponse
791791
},
792792
/**
793-
* Optional map of segments to their stringified definitions.
794-
* This property is ignored if `mySegmentsData` was provided.
793+
* Optional map of segments to their list of keys.
794+
* This property is ignored if `membershipsData` was provided.
795795
* @TODO rename to segments
796796
*/
797797
segmentsData?: {

0 commit comments

Comments
 (0)