@depup/launchdarkly-node-server-sdk 7.0.4-depup.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +16 -0
- package/.circleci/config.yml +89 -0
- package/.eslintignore +5 -0
- package/.eslintrc.yaml +114 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/.github/pull_request_template.md +21 -0
- package/.github/workflows/stale.yml +8 -0
- package/.hound.yml +33 -0
- package/.ldrelease/config.yml +28 -0
- package/.prettierrc +6 -0
- package/CHANGELOG.md +603 -0
- package/CODEOWNERS +2 -0
- package/CONTRIBUTING.md +55 -0
- package/LICENSE.txt +13 -0
- package/README.md +36 -0
- package/SECURITY.md +5 -0
- package/attribute_reference.js +217 -0
- package/big_segments.js +117 -0
- package/caching_store_wrapper.js +240 -0
- package/changes.json +30 -0
- package/configuration.js +235 -0
- package/context.js +98 -0
- package/context_filter.js +137 -0
- package/contract-tests/README.md +7 -0
- package/contract-tests/index.js +109 -0
- package/contract-tests/log.js +23 -0
- package/contract-tests/package.json +15 -0
- package/contract-tests/sdkClientEntity.js +110 -0
- package/contract-tests/testharness-suppressions.txt +2 -0
- package/diagnostic_events.js +151 -0
- package/docs/typedoc.js +10 -0
- package/errors.js +26 -0
- package/evaluator.js +822 -0
- package/event_factory.js +121 -0
- package/event_processor.js +320 -0
- package/event_summarizer.js +101 -0
- package/feature_store.js +120 -0
- package/feature_store_event_wrapper.js +258 -0
- package/file_data_source.js +192 -0
- package/flags_state.js +46 -0
- package/index.d.ts +2426 -0
- package/index.js +452 -0
- package/integrations.js +7 -0
- package/interfaces.js +2 -0
- package/loggers.js +125 -0
- package/messages.js +31 -0
- package/operators.js +106 -0
- package/package.json +105 -0
- package/polling.js +70 -0
- package/requestor.js +62 -0
- package/scripts/better-audit.sh +76 -0
- package/sharedtest/big_segment_store_tests.js +86 -0
- package/sharedtest/feature_store_tests.js +177 -0
- package/sharedtest/persistent_feature_store_tests.js +183 -0
- package/sharedtest/store_tests.js +7 -0
- package/streaming.js +179 -0
- package/test/LDClient-big-segments-test.js +92 -0
- package/test/LDClient-end-to-end-test.js +218 -0
- package/test/LDClient-evaluation-all-flags-test.js +226 -0
- package/test/LDClient-evaluation-test.js +204 -0
- package/test/LDClient-events-test.js +502 -0
- package/test/LDClient-listeners-test.js +180 -0
- package/test/LDClient-test.js +96 -0
- package/test/LDClient-tls-test.js +110 -0
- package/test/attribute_reference-test.js +494 -0
- package/test/big_segments-test.js +182 -0
- package/test/caching_store_wrapper-test.js +434 -0
- package/test/configuration-test.js +249 -0
- package/test/context-test.js +93 -0
- package/test/context_filter-test.js +424 -0
- package/test/diagnostic_events-test.js +152 -0
- package/test/evaluator-big-segments-test.js +301 -0
- package/test/evaluator-bucketing-test.js +333 -0
- package/test/evaluator-clause-test.js +277 -0
- package/test/evaluator-flag-test.js +452 -0
- package/test/evaluator-pre-conditions-test.js +105 -0
- package/test/evaluator-rule-test.js +131 -0
- package/test/evaluator-segment-match-test.js +310 -0
- package/test/evaluator_helpers.js +106 -0
- package/test/event_processor-test.js +680 -0
- package/test/event_summarizer-test.js +146 -0
- package/test/feature_store-test.js +42 -0
- package/test/feature_store_event_wrapper-test.js +182 -0
- package/test/feature_store_test_base.js +60 -0
- package/test/file_data_source-test.js +255 -0
- package/test/loggers-test.js +126 -0
- package/test/operators-test.js +102 -0
- package/test/polling-test.js +158 -0
- package/test/requestor-test.js +60 -0
- package/test/store_tests_big_segments-test.js +61 -0
- package/test/streaming-test.js +323 -0
- package/test/stubs.js +107 -0
- package/test/test_data-test.js +341 -0
- package/test/update_queue-test.js +61 -0
- package/test-types.ts +210 -0
- package/test_data.js +323 -0
- package/tsconfig.json +14 -0
- package/update_queue.js +28 -0
- package/utils/__tests__/httpUtils-test.js +39 -0
- package/utils/__tests__/wrapPromiseCallback-test.js +33 -0
- package/utils/asyncUtils.js +32 -0
- package/utils/httpUtils.js +105 -0
- package/utils/stringifyAttrs.js +14 -0
- package/utils/wrapPromiseCallback.js +36 -0
- package/versioned_data_kind.js +34 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
|
|
2
|
+
const { Evaluator } = require('../evaluator');
|
|
3
|
+
const {
|
|
4
|
+
basicUser,
|
|
5
|
+
basicSingleKindUser,
|
|
6
|
+
basicMultiKindUser,
|
|
7
|
+
eventFactory,
|
|
8
|
+
prepareQueries,
|
|
9
|
+
makeFlagWithSegmentMatch,
|
|
10
|
+
asyncEvaluate,
|
|
11
|
+
makeClauseThatMatchesUser,
|
|
12
|
+
makeClauseThatDoesNotMatchUser,
|
|
13
|
+
} = require('./evaluator_helpers');
|
|
14
|
+
|
|
15
|
+
// Tests of flag evaluation at the segment-matching level - for simple segments, not big segments.
|
|
16
|
+
|
|
17
|
+
describe('Evaluator - segment match for user contexts', () => {
|
|
18
|
+
const matchClause = makeClauseThatMatchesUser(basicUser);
|
|
19
|
+
|
|
20
|
+
it.each([basicUser, basicSingleKindUser, basicMultiKindUser])
|
|
21
|
+
('matches segment with explicitly included user', async (user) => {
|
|
22
|
+
const segment = {
|
|
23
|
+
key: 'test',
|
|
24
|
+
included: [ basicUser.key ],
|
|
25
|
+
version: 1
|
|
26
|
+
};
|
|
27
|
+
const e = Evaluator(prepareQueries({ segments: [segment] }));
|
|
28
|
+
const flag = makeFlagWithSegmentMatch(segment);
|
|
29
|
+
const [ err, detail, events ] = await asyncEvaluate(e, flag, user, eventFactory);
|
|
30
|
+
expect(detail.value).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it.each([basicUser, basicSingleKindUser, basicMultiKindUser])
|
|
34
|
+
('does not match segment with explicitly excluded user', async (user) => {
|
|
35
|
+
const segment = {
|
|
36
|
+
key: 'test',
|
|
37
|
+
excluded: [ basicUser.key ],
|
|
38
|
+
version: 1
|
|
39
|
+
};
|
|
40
|
+
const e = Evaluator(prepareQueries({ segments: [segment] }));
|
|
41
|
+
const flag = makeFlagWithSegmentMatch(segment);
|
|
42
|
+
const [ err, detail, events ] = await asyncEvaluate(e, flag, user, eventFactory);
|
|
43
|
+
expect(detail.value).toBe(false);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
it('does not match a segment that does not exist', async () => {
|
|
48
|
+
const segment = {
|
|
49
|
+
key: 'test',
|
|
50
|
+
included: [ basicUser.key ],
|
|
51
|
+
version: 1
|
|
52
|
+
};
|
|
53
|
+
const e = Evaluator(prepareQueries({ segments: [] }));
|
|
54
|
+
const flag = makeFlagWithSegmentMatch(segment);
|
|
55
|
+
const [ err, detail, events ] = await asyncEvaluate(e, flag, basicUser, eventFactory);
|
|
56
|
+
expect(detail.value).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('does not match segment with unknown user', async () => {
|
|
60
|
+
const segment = {
|
|
61
|
+
key: 'test',
|
|
62
|
+
included: [ 'foo' ],
|
|
63
|
+
version: 1
|
|
64
|
+
};
|
|
65
|
+
const e = Evaluator(prepareQueries({ segments: [segment] }));
|
|
66
|
+
const flag = makeFlagWithSegmentMatch(segment);
|
|
67
|
+
const user = { key: 'bar' };
|
|
68
|
+
const [ err, detail, events ] = await asyncEvaluate(e, flag, user, eventFactory);
|
|
69
|
+
expect(detail.value).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it.each([basicUser, basicSingleKindUser, basicMultiKindUser])
|
|
73
|
+
('matches segment with user who is both included and excluded', async (user) => {
|
|
74
|
+
const segment = {
|
|
75
|
+
key: 'test',
|
|
76
|
+
included: [ basicUser.key ],
|
|
77
|
+
excluded: [ basicUser.key ],
|
|
78
|
+
version: 1
|
|
79
|
+
};
|
|
80
|
+
const e = Evaluator(prepareQueries({ segments: [segment] }));
|
|
81
|
+
const flag = makeFlagWithSegmentMatch(segment);
|
|
82
|
+
const [ err, detail, events ] = await asyncEvaluate(e, flag, user, eventFactory);
|
|
83
|
+
expect(detail.value).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it.each([basicUser, basicSingleKindUser, basicMultiKindUser])
|
|
87
|
+
('matches segment with rule with full rollout', async (user) => {
|
|
88
|
+
const segment = {
|
|
89
|
+
key: 'test',
|
|
90
|
+
rules: [
|
|
91
|
+
{
|
|
92
|
+
clauses: [ matchClause ],
|
|
93
|
+
weight: 100000
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
version: 1
|
|
97
|
+
};
|
|
98
|
+
const e = Evaluator(prepareQueries({ segments: [segment] }));
|
|
99
|
+
const flag = makeFlagWithSegmentMatch(segment);
|
|
100
|
+
const [ err, detail, events ] = await asyncEvaluate(e, flag, user, eventFactory);
|
|
101
|
+
expect(detail.value).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('handles an invalid reference for bucketBy', async () => {
|
|
105
|
+
const segment = {
|
|
106
|
+
key: 'test',
|
|
107
|
+
rules: [
|
|
108
|
+
{
|
|
109
|
+
clauses: [ matchClause ],
|
|
110
|
+
weight: 100000,
|
|
111
|
+
bucketBy: '//',
|
|
112
|
+
contextKind: 'user'
|
|
113
|
+
}
|
|
114
|
+
],
|
|
115
|
+
version: 1
|
|
116
|
+
};
|
|
117
|
+
const e = Evaluator(prepareQueries({ segments: [segment] }));
|
|
118
|
+
const flag = makeFlagWithSegmentMatch(segment);
|
|
119
|
+
const [ err, detail, events ] = await asyncEvaluate(e, flag, basicUser, eventFactory);
|
|
120
|
+
expect(detail.reason).toEqual({ kind: 'ERROR', errorKind: 'MALFORMED_FLAG' });
|
|
121
|
+
expect(detail.value).toBe(null);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it.each([basicUser, basicSingleKindUser, basicMultiKindUser])
|
|
125
|
+
('does not match segment with rule with zero rollout', async (user) => {
|
|
126
|
+
const segment = {
|
|
127
|
+
key: 'test',
|
|
128
|
+
rules: [
|
|
129
|
+
{
|
|
130
|
+
clauses: [ matchClause ],
|
|
131
|
+
weight: 0
|
|
132
|
+
}
|
|
133
|
+
],
|
|
134
|
+
version: 1
|
|
135
|
+
};
|
|
136
|
+
const e = Evaluator(prepareQueries({ segments: [segment] }));
|
|
137
|
+
const flag = makeFlagWithSegmentMatch(segment);
|
|
138
|
+
const [ err, detail, events ] = await asyncEvaluate(e, flag, user, eventFactory);
|
|
139
|
+
expect(detail.value).toBe(false);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('matches segment with multiple matching clauses', async () => {
|
|
143
|
+
|
|
144
|
+
const user = { key: 'foo', email: 'test@example.com', name: 'bob' };
|
|
145
|
+
const segment = {
|
|
146
|
+
key: 'test',
|
|
147
|
+
rules: [
|
|
148
|
+
{
|
|
149
|
+
clauses: [
|
|
150
|
+
{
|
|
151
|
+
attribute: 'email',
|
|
152
|
+
op: 'in',
|
|
153
|
+
values: [ user.email ]
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
attribute: 'name',
|
|
157
|
+
op: 'in',
|
|
158
|
+
values: [ user.name ]
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
}
|
|
162
|
+
],
|
|
163
|
+
version: 1
|
|
164
|
+
};
|
|
165
|
+
const e = Evaluator(prepareQueries({ segments: [segment] }));
|
|
166
|
+
const flag = makeFlagWithSegmentMatch(segment);
|
|
167
|
+
const [ err, detail, events ] = await asyncEvaluate(e, flag, user, eventFactory);
|
|
168
|
+
expect(detail.value).toBe(true);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('does not match segment if one clause does not match', async () => {
|
|
172
|
+
const user = { key: 'foo', email: 'test@example.com', name: 'bob' };
|
|
173
|
+
const segment = {
|
|
174
|
+
key: 'test',
|
|
175
|
+
rules: [
|
|
176
|
+
{
|
|
177
|
+
clauses: [ makeClauseThatMatchesUser(user), makeClauseThatDoesNotMatchUser(user) ],
|
|
178
|
+
}
|
|
179
|
+
],
|
|
180
|
+
version: 1
|
|
181
|
+
};
|
|
182
|
+
const e = Evaluator(prepareQueries({ segments: [segment] }));
|
|
183
|
+
const flag = makeFlagWithSegmentMatch(segment);
|
|
184
|
+
const [ err, detail, events ] = await asyncEvaluate(e, flag, user, eventFactory);
|
|
185
|
+
expect(detail.value).toBe(false);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const singleKind = { kind: 'org', key: 'orgKey' };
|
|
190
|
+
const multiKind = { kind: 'multi', org: { key: 'orgKey' } };
|
|
191
|
+
|
|
192
|
+
describe('Evaluator - segment match for non-user contexts', () => {
|
|
193
|
+
it.each([singleKind, multiKind])
|
|
194
|
+
('matches segment with explicitly included context', async (context) => {
|
|
195
|
+
const segment = {
|
|
196
|
+
key: 'test',
|
|
197
|
+
includedContexts: [ {contextKind: 'org', values: [singleKind.key]} ],
|
|
198
|
+
version: 1
|
|
199
|
+
};
|
|
200
|
+
const e = Evaluator(prepareQueries({ segments: [segment] }));
|
|
201
|
+
const flag = makeFlagWithSegmentMatch(segment);
|
|
202
|
+
const [ err, detail, events ] = await asyncEvaluate(e, flag, context, eventFactory);
|
|
203
|
+
expect(detail.value).toBe(true);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it.each([singleKind, multiKind])
|
|
207
|
+
('matches nested segments', async (context) => {
|
|
208
|
+
const segment1 = {
|
|
209
|
+
key: 'segment1',
|
|
210
|
+
includedContexts: [ {contextKind: 'org', values: [singleKind.key]} ],
|
|
211
|
+
version: 1
|
|
212
|
+
};
|
|
213
|
+
const segment2 = {
|
|
214
|
+
key: 'segment2',
|
|
215
|
+
rules: [
|
|
216
|
+
{
|
|
217
|
+
clauses: [ { attribute: '', op: 'segmentMatch', values: [segment1.key] } ],
|
|
218
|
+
weight: 100000
|
|
219
|
+
}
|
|
220
|
+
],
|
|
221
|
+
version: 1
|
|
222
|
+
};
|
|
223
|
+
const e = Evaluator(prepareQueries({ segments: [segment1, segment2] }));
|
|
224
|
+
const flag = makeFlagWithSegmentMatch(segment2);
|
|
225
|
+
const [ err, detail, events ] = await asyncEvaluate(e, flag, context, eventFactory);
|
|
226
|
+
expect(detail.value).toBe(true);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it('does not exceed callstack side for circular segments', async () => {
|
|
230
|
+
const segment = {
|
|
231
|
+
key: 'segment',
|
|
232
|
+
rules: [
|
|
233
|
+
{
|
|
234
|
+
clauses: [ { attribute: '', op: 'segmentMatch', values: ['segment'] } ],
|
|
235
|
+
weight: 100000
|
|
236
|
+
}
|
|
237
|
+
],
|
|
238
|
+
version: 1
|
|
239
|
+
};
|
|
240
|
+
const e = Evaluator(prepareQueries({ segments: [segment] }));
|
|
241
|
+
const flag = makeFlagWithSegmentMatch(segment);
|
|
242
|
+
const [ err, detail, events ] = await asyncEvaluate(e, flag, singleKind, eventFactory);
|
|
243
|
+
expect(detail.reason).toEqual({ kind: 'ERROR', errorKind: 'MALFORMED_FLAG' });
|
|
244
|
+
expect(detail.value).toBe(null);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('allows for the same segment to be references in multiple clauses', async () => {
|
|
248
|
+
const segment1 = {
|
|
249
|
+
key: 'segment1',
|
|
250
|
+
includedContexts: [ {contextKind: 'org', values: [singleKind.key]} ],
|
|
251
|
+
version: 1
|
|
252
|
+
};
|
|
253
|
+
const segment2 = {
|
|
254
|
+
key: 'segment2',
|
|
255
|
+
rules: [
|
|
256
|
+
{
|
|
257
|
+
clauses: [
|
|
258
|
+
{ attribute: '', op: 'segmentMatch', values: [segment1.key] },
|
|
259
|
+
{ attribute: '', op: 'segmentMatch', values: [segment1.key] }
|
|
260
|
+
],
|
|
261
|
+
weight: 100000
|
|
262
|
+
}
|
|
263
|
+
],
|
|
264
|
+
version: 1
|
|
265
|
+
};
|
|
266
|
+
const e = Evaluator(prepareQueries({ segments: [segment1, segment2] }));
|
|
267
|
+
const flag = makeFlagWithSegmentMatch(segment2);
|
|
268
|
+
const [ err, detail, events ] = await asyncEvaluate(e, flag, singleKind, eventFactory);
|
|
269
|
+
expect(detail.value).toBe(true);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it.each([singleKind, multiKind])
|
|
273
|
+
('does not match segment for matching kind but missing key', async (context) => {
|
|
274
|
+
const segment = {
|
|
275
|
+
key: 'test',
|
|
276
|
+
includedContexts: [ {kind: 'org', values: ['otherKey']} ],
|
|
277
|
+
version: 1
|
|
278
|
+
};
|
|
279
|
+
const e = Evaluator(prepareQueries({ segments: [segment] }));
|
|
280
|
+
const flag = makeFlagWithSegmentMatch(segment);
|
|
281
|
+
const [ err, detail, events ] = await asyncEvaluate(e, flag, context, eventFactory);
|
|
282
|
+
expect(detail.value).toBe(false);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it.each([singleKind, multiKind])
|
|
286
|
+
('does not match segment with explicitly excluded context', async (context) => {
|
|
287
|
+
const segment = {
|
|
288
|
+
key: 'test',
|
|
289
|
+
excludedContexts: [ {kind: 'org', values: [singleKind.key]} ],
|
|
290
|
+
version: 1
|
|
291
|
+
};
|
|
292
|
+
const e = Evaluator(prepareQueries({ segments: [segment] }));
|
|
293
|
+
const flag = makeFlagWithSegmentMatch(segment);
|
|
294
|
+
const [ err, detail, events ] = await asyncEvaluate(e, flag, context, eventFactory);
|
|
295
|
+
expect(detail.value).toBe(false);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
it.each([singleKind, multiKind])
|
|
299
|
+
('does not match segment for wrong kind', async (context) => {
|
|
300
|
+
const segment = {
|
|
301
|
+
key: 'test',
|
|
302
|
+
includedContexts: [ {kind: 'notOrg', values: [singleKind.key]} ],
|
|
303
|
+
version: 1
|
|
304
|
+
};
|
|
305
|
+
const e = Evaluator(prepareQueries({ segments: [segment] }));
|
|
306
|
+
const flag = makeFlagWithSegmentMatch(segment);
|
|
307
|
+
const [ err, detail, events ] = await asyncEvaluate(e, flag, context, eventFactory);
|
|
308
|
+
expect(detail.value).toBe(false);
|
|
309
|
+
});
|
|
310
|
+
});
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
const { EventFactory } = require('../event_factory');
|
|
2
|
+
|
|
3
|
+
// All three of these users should represent the same user.
|
|
4
|
+
const basicUser = { key: 'userkey' };
|
|
5
|
+
const basicSingleKindUser = { kind: 'user', key: 'userkey' };
|
|
6
|
+
const basicMultiKindUser = { kind: 'multi', user: { key: 'userkey' } };
|
|
7
|
+
|
|
8
|
+
const eventFactory = EventFactory(false);
|
|
9
|
+
|
|
10
|
+
// Evaluator.evaluate uses a callback instead of a promise because it's a slightly more efficient
|
|
11
|
+
// way to pass multiple return values. But for the purposes of our tests, it's much easier to use
|
|
12
|
+
// a promise and async/await, so we'll transform it with this helper. Unlike usual Node promise
|
|
13
|
+
// semantics, here we treat "err" as just another return parameter rather than throwing an error
|
|
14
|
+
// (because the other parameters can still be non-null even if there's an error).
|
|
15
|
+
function asyncEvaluate(evaluator, flag, user, eventFactory) {
|
|
16
|
+
return new Promise(resolve => {
|
|
17
|
+
evaluator.evaluate(flag, user, eventFactory, (err, detail, events) => resolve([err, detail, events]));
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function makeFlagWithRules(rules, fallthrough) {
|
|
22
|
+
if (!fallthrough) {
|
|
23
|
+
fallthrough = { variation: 0 };
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
key: 'feature',
|
|
27
|
+
on: true,
|
|
28
|
+
rules: rules,
|
|
29
|
+
targets: [],
|
|
30
|
+
fallthrough: fallthrough,
|
|
31
|
+
offVariation: 1,
|
|
32
|
+
variations: ['a', 'b', 'c']
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function makeBooleanFlagWithRules(rules) {
|
|
37
|
+
return {
|
|
38
|
+
key: 'feature',
|
|
39
|
+
on: true,
|
|
40
|
+
prerequisites: [],
|
|
41
|
+
rules: rules,
|
|
42
|
+
targets: [],
|
|
43
|
+
salt: '',
|
|
44
|
+
fallthrough: { variation: 0 },
|
|
45
|
+
offVariation: 0,
|
|
46
|
+
variations: [false, true],
|
|
47
|
+
version: 1
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function makeBooleanFlagWithOneClause(clause) {
|
|
52
|
+
return makeBooleanFlagWithRules([{ clauses: [clause], variation: 1 }]);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function makeFlagWithSegmentMatch(segment) {
|
|
56
|
+
return makeBooleanFlagWithOneClause(makeSegmentMatchClause(segment));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function makeClauseThatMatchesUser(user) {
|
|
60
|
+
return { attribute: 'key', op: 'in', values: [user.key] };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function makeClauseThatDoesNotMatchUser(user) {
|
|
64
|
+
return { attribute: 'key', op: 'in', values: ['not-' + user.key] };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function makeSegmentMatchClause(segment) {
|
|
68
|
+
return { attribute: '', op: 'segmentMatch', values: [segment.key] };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function prepareQueries(data) {
|
|
72
|
+
let flagsMap = {}, segmentsMap = {};
|
|
73
|
+
for (const f of (data.flags || [])) {
|
|
74
|
+
flagsMap[f.key] = f;
|
|
75
|
+
}
|
|
76
|
+
for (const s of (data.segments || [])) {
|
|
77
|
+
segmentsMap[s.key] = s;
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
getFlag: (key, cb) => cb(flagsMap[key]),
|
|
81
|
+
getSegment: (key, cb) => cb(segmentsMap[key]),
|
|
82
|
+
getBigSegmentsMembership: (key, cb) => {
|
|
83
|
+
if (data.bigSegments) {
|
|
84
|
+
cb([data.bigSegments[key], 'HEALTHY']);
|
|
85
|
+
} else {
|
|
86
|
+
cb(null);
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = {
|
|
93
|
+
basicUser,
|
|
94
|
+
basicSingleKindUser,
|
|
95
|
+
basicMultiKindUser,
|
|
96
|
+
eventFactory,
|
|
97
|
+
asyncEvaluate,
|
|
98
|
+
makeFlagWithRules,
|
|
99
|
+
makeBooleanFlagWithRules,
|
|
100
|
+
makeBooleanFlagWithOneClause,
|
|
101
|
+
makeFlagWithSegmentMatch,
|
|
102
|
+
makeClauseThatMatchesUser,
|
|
103
|
+
makeClauseThatDoesNotMatchUser,
|
|
104
|
+
makeSegmentMatchClause,
|
|
105
|
+
prepareQueries,
|
|
106
|
+
};
|