Skip to content

Commit 8c84e01

Browse files
authored
feat: Add support for client-side prerequisite events. (#606)
This PR contains the server-side implementation for allFlagsState evaluation for bootstrap, server-side LDEvaluationDetail, client-side LDEvaluationDetail, and client-side events for prerequisites. This version only includes direct pre-requisites, and the client-side evaluation uses variation methods versus directly sending events. BEGIN_COMMIT_OVERRIDE feat: Add support for client-side prerequisite events. feat: Add support for prerequisite details to evaluation detail. feat: Add prerequisite information to server-side allFlagsState. END_COMMIT_OVERRIDE SDK-686 SDK-682
1 parent a986478 commit 8c84e01

File tree

18 files changed

+352
-29
lines changed

18 files changed

+352
-29
lines changed

contract-tests/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ app.get('/', (req, res) => {
3838
'anonymous-redaction',
3939
'evaluation-hooks',
4040
'wrapper',
41+
'client-prereq-events',
4142
],
4243
});
4344
});

packages/sdk/browser/contract-tests/entity/src/TestHarnessWebSocket.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export default class TestHarnessWebSocket {
4141
'inline-context',
4242
'anonymous-redaction',
4343
'strongly-typed',
44+
'client-prereq-events',
4445
];
4546

4647
break;

packages/sdk/browser/rollup.config.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ const terserOpts = {
2828
regex: /^_/,
2929
// Do not mangle '_meta', because this is part of our JSON
3030
// data model.
31-
reserved: ['_meta']
31+
reserved: ['_meta'],
3232
},
33-
}
33+
},
3434
};
3535

3636
export default [
@@ -53,12 +53,6 @@ export default [
5353
},
5454
{
5555
...getSharedConfig('cjs', 'dist/index.cjs.js'),
56-
plugins: [
57-
typescript(),
58-
common(),
59-
resolve(),
60-
terser(terserOpts),
61-
json(),
62-
],
56+
plugins: [typescript(), common(), resolve(), terser(terserOpts), json()],
6357
},
6458
];

packages/shared/sdk-client/__tests__/LDCLientImpl.inspections.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,5 +187,7 @@ it('calls flag-details-changed inspectors when all flag values change', async ()
187187
'moonshot-demo': { reason: null, value: true, variationIndex: 0 },
188188
test1: { reason: null, value: 's1', variationIndex: 0 },
189189
'this-is-a-test': { reason: null, value: true, variationIndex: 0 },
190+
'has-prereq-depth-1': { reason: { kind: 'FALLTHROUGH' }, value: true, variationIndex: 0 },
191+
'is-prereq': { reason: { kind: 'FALLTHROUGH' }, value: true, variationIndex: 0 },
190192
});
191193
});

packages/shared/sdk-client/__tests__/LDClientImpl.events.test.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,4 +199,45 @@ describe('sdk-client object', () => {
199199
expect.stringMatching(/was called with a non-numeric/),
200200
);
201201
});
202+
203+
it('sends events for prerequisite flags', async () => {
204+
await ldc.identify({ kind: 'user', key: 'bob' });
205+
ldc.variation('has-prereq-depth-1', false);
206+
ldc.flush();
207+
208+
// Prerequisite evaluation event should be emitted before the evaluation event for the flag
209+
// being evaluated.
210+
expect(mockedSendEvent).toHaveBeenNthCalledWith(
211+
2,
212+
expect.objectContaining({
213+
context: expect.anything(),
214+
creationDate: expect.any(Number),
215+
default: undefined,
216+
key: 'is-prereq',
217+
kind: 'feature',
218+
samplingRatio: 1,
219+
trackEvents: true,
220+
value: true,
221+
variation: 0,
222+
version: 1,
223+
withReasons: false,
224+
}),
225+
);
226+
expect(mockedSendEvent).toHaveBeenNthCalledWith(
227+
3,
228+
expect.objectContaining({
229+
context: expect.anything(),
230+
creationDate: expect.any(Number),
231+
default: false,
232+
key: 'has-prereq-depth-1',
233+
kind: 'feature',
234+
samplingRatio: 1,
235+
trackEvents: true,
236+
value: true,
237+
variation: 0,
238+
version: 4,
239+
withReasons: false,
240+
}),
241+
);
242+
});
202243
});

packages/shared/sdk-client/__tests__/LDClientImpl.storage.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ describe('sdk-client storage', () => {
104104
'easter-i-tunes-special': false,
105105
'easter-specials': 'no specials',
106106
fdsafdsafdsafdsa: true,
107+
'has-prereq-depth-1': true,
108+
'is-prereq': true,
107109
'log-level': 'warn',
108110
'moonshot-demo': true,
109111
test1: 's1',
@@ -156,6 +158,8 @@ describe('sdk-client storage', () => {
156158
'easter-i-tunes-special': false,
157159
'easter-specials': 'no specials',
158160
fdsafdsafdsafdsa: true,
161+
'has-prereq-depth-1': true,
162+
'is-prereq': true,
159163
'log-level': 'warn',
160164
'moonshot-demo': true,
161165
test1: 's1',
@@ -218,6 +222,8 @@ describe('sdk-client storage', () => {
218222
'easter-i-tunes-special': false,
219223
'easter-specials': 'no specials',
220224
fdsafdsafdsafdsa: true,
225+
'has-prereq-depth-1': true,
226+
'is-prereq': true,
221227
'log-level': 'warn',
222228
'moonshot-demo': true,
223229
test1: 's1',
@@ -388,6 +394,8 @@ describe('sdk-client storage', () => {
388394
'easter-i-tunes-special': false,
389395
'easter-specials': 'no specials',
390396
fdsafdsafdsafdsa: true,
397+
'has-prereq-depth-1': true,
398+
'is-prereq': true,
391399
'log-level': 'warn',
392400
'moonshot-demo': true,
393401
test1: 's1',
@@ -517,6 +525,8 @@ describe('sdk-client storage', () => {
517525
'easter-i-tunes-special': false,
518526
'easter-specials': 'no specials',
519527
fdsafdsafdsafdsa: true,
528+
'has-prereq-depth-1': true,
529+
'is-prereq': true,
520530
'log-level': 'warn',
521531
'moonshot-demo': true,
522532
test1: 's1',

packages/shared/sdk-client/__tests__/LDClientImpl.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ describe('sdk-client object', () => {
103103
'easter-i-tunes-special': false,
104104
'easter-specials': 'no specials',
105105
fdsafdsafdsafdsa: true,
106+
'has-prereq-depth-1': true,
107+
'is-prereq': true,
106108
'log-level': 'warn',
107109
'moonshot-demo': true,
108110
test1: 's1',

packages/shared/sdk-client/__tests__/LDClientImpl.timeout.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,8 @@ describe('sdk-client identify timeout', () => {
8989
'easter-i-tunes-special': false,
9090
'easter-specials': 'no specials',
9191
fdsafdsafdsafdsa: true,
92+
'has-prereq-depth-1': true,
93+
'is-prereq': true,
9294
'log-level': 'warn',
9395
'moonshot-demo': true,
9496
test1: 's1',
@@ -112,6 +114,8 @@ describe('sdk-client identify timeout', () => {
112114
'easter-i-tunes-special': false,
113115
'easter-specials': 'no specials',
114116
fdsafdsafdsafdsa: true,
117+
'has-prereq-depth-1': true,
118+
'is-prereq': true,
115119
'log-level': 'warn',
116120
'moonshot-demo': true,
117121
test1: 's1',

packages/shared/sdk-client/__tests__/evaluation/mockResponse.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,24 @@
5454
"value": true,
5555
"variation": 0,
5656
"trackEvents": false
57+
},
58+
"is-prereq": {
59+
"value": true,
60+
"variation": 0,
61+
"reason": {
62+
"kind": "FALLTHROUGH"
63+
},
64+
"version": 1,
65+
"trackEvents": true
66+
},
67+
"has-prereq-depth-1": {
68+
"value": true,
69+
"variation": 0,
70+
"prerequisites": ["is-prereq"],
71+
"reason": {
72+
"kind": "FALLTHROUGH"
73+
},
74+
"version": 4,
75+
"trackEvents": true
5776
}
5877
}

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

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,7 @@ export default class LDClientImpl implements LDClient {
327327
return createErrorEvaluationDetail(ErrorKinds.FlagNotFound, defaultValue);
328328
}
329329

330-
const { reason, value, variation } = foundItem.flag;
330+
const { reason, value, variation, prerequisites } = foundItem.flag;
331331

332332
if (typeChecker) {
333333
const [matched, type] = typeChecker(value);
@@ -355,6 +355,10 @@ export default class LDClientImpl implements LDClient {
355355
this.logger.debug('Result value is null. Providing default value.');
356356
successDetail.value = defaultValue;
357357
}
358+
359+
prerequisites?.forEach((prereqKey) => {
360+
this.variation(prereqKey, undefined);
361+
});
358362
this._eventProcessor?.sendEvent(
359363
eventFactory.evalEventClient(
360364
flagKey,

0 commit comments

Comments
 (0)