flagsmith-nodejs 3.3.3 → 4.0.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/.github/workflows/publish.yml +2 -2
- package/.github/workflows/pull_request.yaml +7 -14
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/environments/models.d.ts +3 -3
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/environments/models.js +20 -13
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/environments/util.d.ts +1 -1
- package/build/cjs/flagsmith-engine/environments/util.js +23 -0
- package/build/cjs/flagsmith-engine/features/models.js +118 -0
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/features/util.d.ts +1 -1
- package/build/cjs/flagsmith-engine/features/util.js +27 -0
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/identities/models.d.ts +2 -2
- package/build/cjs/flagsmith-engine/identities/models.js +48 -0
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/identities/traits/models.js +5 -4
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/identities/util.d.ts +2 -2
- package/build/cjs/flagsmith-engine/identities/util.js +22 -0
- package/build/cjs/flagsmith-engine/index.d.ts +14 -0
- package/build/cjs/flagsmith-engine/index.js +75 -0
- package/build/cjs/flagsmith-engine/organisations/models.js +21 -0
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/organisations/util.d.ts +1 -1
- package/build/cjs/flagsmith-engine/organisations/util.js +8 -0
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/projects/models.d.ts +2 -2
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/projects/models.js +8 -5
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/projects/util.d.ts +1 -1
- package/build/cjs/flagsmith-engine/projects/util.js +15 -0
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/evaluators.d.ts +4 -4
- package/build/cjs/flagsmith-engine/segments/evaluators.js +37 -0
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/models.d.ts +1 -1
- package/build/cjs/flagsmith-engine/segments/models.js +111 -0
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/util.d.ts +1 -1
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/util.js +9 -11
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/collections.d.ts +1 -1
- package/build/cjs/flagsmith-engine/utils/collections.js +6 -0
- package/build/cjs/flagsmith-engine/utils/errors.js +6 -0
- package/build/cjs/flagsmith-engine/utils/hashing/index.js +29 -0
- package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/index.js +5 -5
- package/build/{index.d.ts → cjs/index.d.ts} +3 -3
- package/build/cjs/index.js +18 -0
- package/build/cjs/package.json +1 -0
- package/build/{sdk → cjs/sdk}/analytics.d.ts +3 -0
- package/build/cjs/sdk/analytics.js +73 -0
- package/build/cjs/sdk/errors.js +9 -0
- package/build/{sdk → cjs/sdk}/index.d.ts +19 -18
- package/build/cjs/sdk/index.js +400 -0
- package/build/{sdk → cjs/sdk}/models.d.ts +2 -2
- package/build/cjs/sdk/models.js +101 -0
- package/build/{sdk → cjs/sdk}/offline_handlers.d.ts +1 -1
- package/build/cjs/sdk/offline_handlers.js +23 -0
- package/build/{sdk → cjs/sdk}/polling_manager.d.ts +1 -1
- package/build/cjs/sdk/polling_manager.js +29 -0
- package/build/{sdk → cjs/sdk}/types.d.ts +15 -7
- package/build/cjs/sdk/utils.d.ts +36 -0
- package/build/cjs/sdk/utils.js +63 -0
- package/build/esm/flagsmith-engine/environments/models.d.ts +22 -0
- package/build/esm/flagsmith-engine/environments/models.js +32 -0
- package/build/esm/flagsmith-engine/environments/util.d.ts +3 -0
- package/build/esm/flagsmith-engine/environments/util.js +18 -0
- package/build/esm/flagsmith-engine/features/constants.d.ts +4 -0
- package/build/esm/flagsmith-engine/features/constants.js +4 -0
- package/build/esm/flagsmith-engine/features/models.d.ts +37 -0
- package/build/esm/flagsmith-engine/features/models.js +110 -0
- package/build/esm/flagsmith-engine/features/util.d.ts +4 -0
- package/build/esm/flagsmith-engine/features/util.js +21 -0
- package/build/esm/flagsmith-engine/identities/models.d.ts +15 -0
- package/build/esm/flagsmith-engine/identities/models.js +44 -0
- package/build/esm/flagsmith-engine/identities/traits/models.d.ts +5 -0
- package/build/esm/flagsmith-engine/identities/traits/models.js +8 -0
- package/build/esm/flagsmith-engine/identities/util.d.ts +4 -0
- package/build/esm/flagsmith-engine/identities/util.js +17 -0
- package/build/esm/flagsmith-engine/index.d.ts +14 -0
- package/build/esm/flagsmith-engine/index.js +62 -0
- package/build/esm/flagsmith-engine/organisations/models.d.ts +9 -0
- package/build/esm/flagsmith-engine/organisations/models.js +17 -0
- package/build/esm/flagsmith-engine/organisations/util.d.ts +2 -0
- package/build/esm/flagsmith-engine/organisations/util.js +4 -0
- package/build/esm/flagsmith-engine/projects/models.d.ts +10 -0
- package/build/esm/flagsmith-engine/projects/models.js +13 -0
- package/build/esm/flagsmith-engine/projects/util.d.ts +2 -0
- package/build/esm/flagsmith-engine/projects/util.js +11 -0
- package/build/esm/flagsmith-engine/segments/constants.d.ts +34 -0
- package/build/esm/flagsmith-engine/segments/constants.js +36 -0
- package/build/esm/flagsmith-engine/segments/evaluators.d.ts +7 -0
- package/build/esm/flagsmith-engine/segments/evaluators.js +31 -0
- package/build/esm/flagsmith-engine/segments/models.d.ts +37 -0
- package/build/esm/flagsmith-engine/segments/models.js +102 -0
- package/build/esm/flagsmith-engine/segments/util.d.ts +6 -0
- package/build/esm/flagsmith-engine/segments/util.js +23 -0
- package/build/esm/flagsmith-engine/utils/collections.d.ts +3 -0
- package/build/esm/flagsmith-engine/utils/collections.js +2 -0
- package/build/esm/flagsmith-engine/utils/errors.d.ts +2 -0
- package/build/esm/flagsmith-engine/utils/errors.js +2 -0
- package/build/esm/flagsmith-engine/utils/hashing/index.d.ts +9 -0
- package/build/esm/flagsmith-engine/utils/hashing/index.js +25 -0
- package/build/esm/flagsmith-engine/utils/index.d.ts +1 -0
- package/build/esm/flagsmith-engine/utils/index.js +13 -0
- package/build/esm/index.d.ts +3 -0
- package/build/esm/index.js +2 -0
- package/build/esm/sdk/analytics.d.ts +35 -0
- package/build/esm/sdk/analytics.js +69 -0
- package/build/esm/sdk/errors.d.ts +4 -0
- package/build/esm/sdk/errors.js +4 -0
- package/build/esm/sdk/index.d.ts +131 -0
- package/build/esm/sdk/index.js +390 -0
- package/build/esm/sdk/models.d.ts +55 -0
- package/build/esm/sdk/models.js +94 -0
- package/build/esm/sdk/offline_handlers.d.ts +9 -0
- package/build/esm/sdk/offline_handlers.js +18 -0
- package/build/esm/sdk/polling_manager.d.ts +9 -0
- package/build/esm/sdk/polling_manager.js +25 -0
- package/build/esm/sdk/types.d.ts +38 -0
- package/build/esm/sdk/types.js +1 -0
- package/build/esm/sdk/utils.d.ts +36 -0
- package/build/esm/sdk/utils.js +56 -0
- package/flagsmith-engine/environments/models.ts +3 -3
- package/flagsmith-engine/environments/util.ts +4 -4
- package/flagsmith-engine/features/models.ts +2 -2
- package/flagsmith-engine/features/util.ts +1 -1
- package/flagsmith-engine/identities/models.ts +4 -5
- package/flagsmith-engine/identities/traits/models.ts +0 -1
- package/flagsmith-engine/identities/util.ts +4 -4
- package/flagsmith-engine/index.ts +13 -13
- package/flagsmith-engine/organisations/util.ts +1 -1
- package/flagsmith-engine/projects/models.ts +2 -2
- package/flagsmith-engine/projects/util.ts +4 -4
- package/flagsmith-engine/segments/evaluators.ts +6 -6
- package/flagsmith-engine/segments/models.ts +5 -5
- package/flagsmith-engine/segments/util.ts +3 -3
- package/flagsmith-engine/utils/collections.ts +1 -1
- package/flagsmith-engine/utils/hashing/index.ts +5 -29
- package/flagsmith-engine/utils/index.ts +1 -1
- package/index.ts +4 -8
- package/package.json +21 -16
- package/sdk/analytics.ts +7 -5
- package/sdk/index.ts +55 -46
- package/sdk/models.ts +2 -3
- package/sdk/offline_handlers.ts +2 -2
- package/sdk/polling_manager.ts +2 -3
- package/sdk/types.ts +35 -24
- package/sdk/utils.ts +49 -37
- package/tests/engine/e2e/engine.test.ts +8 -11
- package/tests/engine/unit/engine.test.ts +5 -5
- package/tests/engine/unit/segments/segment_evaluators.test.ts +9 -9
- package/tests/engine/unit/utils/utils.test.ts +2 -2
- package/tests/sdk/analytics.test.ts +8 -13
- package/tests/sdk/data/identity-with-transient-traits.json +41 -0
- package/tests/sdk/data/transient-identity.json +29 -0
- package/tests/sdk/flagsmith-cache.test.ts +16 -32
- package/tests/sdk/flagsmith-environment-flags.test.ts +21 -36
- package/tests/sdk/flagsmith-identity-flags.test.ts +83 -32
- package/tests/sdk/flagsmith.test.ts +67 -99
- package/tests/sdk/offline-handlers.test.ts +5 -6
- package/tests/sdk/polling.test.ts +6 -8
- package/tests/sdk/utils.ts +19 -15
- package/tsconfig.cjs.json +7 -0
- package/tsconfig.esm.json +7 -0
- package/tsconfig.json +8 -4
- package/vitest.config.ts +17 -0
- package/build/flagsmith-engine/environments/util.js +0 -27
- package/build/flagsmith-engine/features/models.js +0 -132
- package/build/flagsmith-engine/features/util.js +0 -27
- package/build/flagsmith-engine/identities/models.js +0 -113
- package/build/flagsmith-engine/identities/util.js +0 -46
- package/build/flagsmith-engine/index.d.ts +0 -14
- package/build/flagsmith-engine/index.js +0 -127
- package/build/flagsmith-engine/organisations/models.js +0 -21
- package/build/flagsmith-engine/organisations/util.js +0 -8
- package/build/flagsmith-engine/projects/util.js +0 -15
- package/build/flagsmith-engine/segments/evaluators.js +0 -45
- package/build/flagsmith-engine/segments/models.js +0 -147
- package/build/flagsmith-engine/utils/collections.js +0 -26
- package/build/flagsmith-engine/utils/errors.js +0 -26
- package/build/flagsmith-engine/utils/hashing/index.js +0 -60
- package/build/index.js +0 -23
- package/build/sdk/analytics.js +0 -120
- package/build/sdk/errors.js +0 -34
- package/build/sdk/index.js +0 -594
- package/build/sdk/models.js +0 -149
- package/build/sdk/offline_handlers.js +0 -66
- package/build/sdk/polling_manager.js +0 -72
- package/build/sdk/utils.d.ts +0 -12
- package/build/sdk/utils.js +0 -107
- package/jest.config.js +0 -5
- package/tests/index.js +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/features/constants.d.ts +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/features/constants.js +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/features/models.d.ts +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/identities/traits/models.d.ts +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/organisations/models.d.ts +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/constants.d.ts +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/segments/constants.js +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/errors.d.ts +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/hashing/index.d.ts +0 -0
- /package/build/{flagsmith-engine → cjs/flagsmith-engine}/utils/index.d.ts +0 -0
- /package/build/{sdk → cjs/sdk}/errors.d.ts +0 -0
- /package/build/{sdk → cjs/sdk}/types.js +0 -0
package/sdk/index.ts
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { getEnvironmentFeatureStates, getIdentityFeatureStates } from '../flagsmith-engine';
|
|
3
|
-
import { EnvironmentModel } from '../flagsmith-engine/
|
|
4
|
-
import { buildEnvironmentModel } from '../flagsmith-engine/environments/util';
|
|
5
|
-
import { IdentityModel } from '../flagsmith-engine/
|
|
6
|
-
import { TraitModel } from '../flagsmith-engine/
|
|
7
|
-
|
|
8
|
-
import { AnalyticsProcessor } from './analytics';
|
|
9
|
-
import { BaseOfflineHandler } from './offline_handlers';
|
|
10
|
-
import { FlagsmithAPIError, FlagsmithClientError } from './errors';
|
|
11
|
-
|
|
12
|
-
import { DefaultFlag, Flags } from './models';
|
|
13
|
-
import { EnvironmentDataPollingManager } from './polling_manager';
|
|
14
|
-
import { generateIdentitiesData, retryFetch } from './utils';
|
|
15
|
-
import { SegmentModel } from '../flagsmith-engine/
|
|
16
|
-
import { getIdentitySegments } from '../flagsmith-engine/segments/evaluators';
|
|
17
|
-
import { FlagsmithCache, FlagsmithConfig } from './types';
|
|
18
|
-
import pino,
|
|
19
|
-
|
|
20
|
-
export { AnalyticsProcessor } from './analytics';
|
|
21
|
-
export { FlagsmithAPIError, FlagsmithClientError } from './errors';
|
|
22
|
-
|
|
23
|
-
export { DefaultFlag, Flags } from './models';
|
|
24
|
-
export { EnvironmentDataPollingManager } from './polling_manager';
|
|
25
|
-
export { FlagsmithCache, FlagsmithConfig } from './types';
|
|
1
|
+
import { Dispatcher } from 'undici-types';
|
|
2
|
+
import { getEnvironmentFeatureStates, getIdentityFeatureStates } from '../flagsmith-engine/index.js';
|
|
3
|
+
import { EnvironmentModel } from '../flagsmith-engine/index.js';
|
|
4
|
+
import { buildEnvironmentModel } from '../flagsmith-engine/environments/util.js';
|
|
5
|
+
import { IdentityModel } from '../flagsmith-engine/index.js';
|
|
6
|
+
import { TraitModel } from '../flagsmith-engine/index.js';
|
|
7
|
+
|
|
8
|
+
import { AnalyticsProcessor } from './analytics.js';
|
|
9
|
+
import { BaseOfflineHandler } from './offline_handlers.js';
|
|
10
|
+
import { FlagsmithAPIError, FlagsmithClientError } from './errors.js';
|
|
11
|
+
|
|
12
|
+
import { DefaultFlag, Flags } from './models.js';
|
|
13
|
+
import { EnvironmentDataPollingManager } from './polling_manager.js';
|
|
14
|
+
import { generateIdentitiesData, retryFetch } from './utils.js';
|
|
15
|
+
import { SegmentModel } from '../flagsmith-engine/index.js';
|
|
16
|
+
import { getIdentitySegments } from '../flagsmith-engine/segments/evaluators.js';
|
|
17
|
+
import { Fetch, FlagsmithCache, FlagsmithConfig, FlagsmithTraitValue, ITraitConfig } from './types.js';
|
|
18
|
+
import { pino, Logger } from 'pino';
|
|
19
|
+
|
|
20
|
+
export { AnalyticsProcessor } from './analytics.js';
|
|
21
|
+
export { FlagsmithAPIError, FlagsmithClientError } from './errors.js';
|
|
22
|
+
|
|
23
|
+
export { DefaultFlag, Flags } from './models.js';
|
|
24
|
+
export { EnvironmentDataPollingManager } from './polling_manager.js';
|
|
25
|
+
export { FlagsmithCache, FlagsmithConfig } from './types.js';
|
|
26
26
|
|
|
27
27
|
const DEFAULT_API_URL = 'https://edge.api.flagsmith.com/api/v1/';
|
|
28
28
|
const DEFAULT_REQUEST_TIMEOUT_SECONDS = 10;
|
|
@@ -31,7 +31,7 @@ export class Flagsmith {
|
|
|
31
31
|
environmentKey?: string = undefined;
|
|
32
32
|
apiUrl?: string = undefined;
|
|
33
33
|
customHeaders?: { [key: string]: any };
|
|
34
|
-
agent
|
|
34
|
+
agent?: Dispatcher;
|
|
35
35
|
requestTimeoutMs?: number;
|
|
36
36
|
enableLocalEvaluation?: boolean = false;
|
|
37
37
|
environmentRefreshIntervalSeconds: number = 60;
|
|
@@ -39,7 +39,6 @@ export class Flagsmith {
|
|
|
39
39
|
enableAnalytics: boolean = false;
|
|
40
40
|
defaultFlagHandler?: (featureName: string) => DefaultFlag;
|
|
41
41
|
|
|
42
|
-
|
|
43
42
|
environmentFlagsUrl?: string;
|
|
44
43
|
identitiesUrl?: string;
|
|
45
44
|
environmentUrl?: string;
|
|
@@ -55,6 +54,7 @@ export class Flagsmith {
|
|
|
55
54
|
private onEnvironmentChange?: (error: Error | null, result: EnvironmentModel) => void;
|
|
56
55
|
private analyticsProcessor?: AnalyticsProcessor;
|
|
57
56
|
private logger: Logger;
|
|
57
|
+
private customFetch: Fetch;
|
|
58
58
|
/**
|
|
59
59
|
* A Flagsmith client.
|
|
60
60
|
*
|
|
@@ -98,6 +98,7 @@ export class Flagsmith {
|
|
|
98
98
|
// }
|
|
99
99
|
|
|
100
100
|
this.agent = data.agent;
|
|
101
|
+
this.customFetch = data.fetch ?? fetch;
|
|
101
102
|
this.environmentKey = data.environmentKey;
|
|
102
103
|
this.apiUrl = data.apiUrl || this.apiUrl;
|
|
103
104
|
this.customHeaders = data.customHeaders;
|
|
@@ -168,11 +169,11 @@ export class Flagsmith {
|
|
|
168
169
|
|
|
169
170
|
this.analyticsProcessor = data.enableAnalytics
|
|
170
171
|
? new AnalyticsProcessor({
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
172
|
+
environmentKey: this.environmentKey,
|
|
173
|
+
baseApiUrl: this.apiUrl,
|
|
174
|
+
requestTimeoutMs: this.requestTimeoutMs,
|
|
175
|
+
logger: this.logger
|
|
176
|
+
})
|
|
176
177
|
: undefined;
|
|
177
178
|
}
|
|
178
179
|
}
|
|
@@ -207,11 +208,15 @@ export class Flagsmith {
|
|
|
207
208
|
*
|
|
208
209
|
* @param {string} identifier a unique identifier for the identity in the current
|
|
209
210
|
environment, e.g. email address, username, uuid
|
|
210
|
-
* @param {{[key:string]:any}} traits? a dictionary of traits to add / update on the identity in
|
|
211
|
-
Flagsmith, e.g. {"num_orders": 10}
|
|
211
|
+
* @param {{[key:string]:any | ITraitConfig}} traits? a dictionary of traits to add / update on the identity in
|
|
212
|
+
Flagsmith, e.g. {"num_orders": 10} or {age: {value: 30, transient: true}}
|
|
212
213
|
* @returns Flags object holding all the flags for the given identity.
|
|
213
214
|
*/
|
|
214
|
-
async getIdentityFlags(
|
|
215
|
+
async getIdentityFlags(
|
|
216
|
+
identifier: string,
|
|
217
|
+
traits?: { [key: string]: FlagsmithTraitValue | ITraitConfig },
|
|
218
|
+
transient: boolean = false
|
|
219
|
+
): Promise<Flags> {
|
|
215
220
|
if (!identifier) {
|
|
216
221
|
throw new Error('`identifier` argument is missing or invalid.');
|
|
217
222
|
}
|
|
@@ -232,7 +237,7 @@ export class Flagsmith {
|
|
|
232
237
|
return this.getIdentityFlagsFromDocument(identifier, traits || {});
|
|
233
238
|
}
|
|
234
239
|
|
|
235
|
-
return this.getIdentityFlagsFromApi(identifier, traits);
|
|
240
|
+
return this.getIdentityFlagsFromApi(identifier, traits, transient);
|
|
236
241
|
}
|
|
237
242
|
|
|
238
243
|
/**
|
|
@@ -293,8 +298,11 @@ export class Flagsmith {
|
|
|
293
298
|
}
|
|
294
299
|
if (this.environment.identityOverrides?.length) {
|
|
295
300
|
this.identitiesWithOverridesByIdentifier = new Map<string, IdentityModel>(
|
|
296
|
-
this.environment.identityOverrides.map(identity => [
|
|
297
|
-
|
|
301
|
+
this.environment.identityOverrides.map(identity => [
|
|
302
|
+
identity.identifier,
|
|
303
|
+
identity
|
|
304
|
+
])
|
|
305
|
+
);
|
|
298
306
|
}
|
|
299
307
|
if (this.onEnvironmentChange) {
|
|
300
308
|
this.onEnvironmentChange(null, this.environment);
|
|
@@ -329,13 +337,14 @@ export class Flagsmith {
|
|
|
329
337
|
const data = await retryFetch(
|
|
330
338
|
url,
|
|
331
339
|
{
|
|
332
|
-
|
|
340
|
+
dispatcher: this.agent,
|
|
333
341
|
method: method,
|
|
334
342
|
body: JSON.stringify(body),
|
|
335
343
|
headers: headers
|
|
336
344
|
},
|
|
337
345
|
this.retries,
|
|
338
|
-
this.requestTimeoutMs
|
|
346
|
+
this.requestTimeoutMs,
|
|
347
|
+
this.customFetch,
|
|
339
348
|
);
|
|
340
349
|
|
|
341
350
|
if (data.status !== 200) {
|
|
@@ -367,7 +376,6 @@ export class Flagsmith {
|
|
|
367
376
|
defaultFlagHandler: this.defaultFlagHandler
|
|
368
377
|
});
|
|
369
378
|
if (!!this.cache) {
|
|
370
|
-
// @ts-ignore node-cache types are incorrect, ttl should be optional
|
|
371
379
|
await this.cache.set('flags', flags);
|
|
372
380
|
}
|
|
373
381
|
return flags;
|
|
@@ -395,7 +403,6 @@ export class Flagsmith {
|
|
|
395
403
|
});
|
|
396
404
|
|
|
397
405
|
if (!!this.cache) {
|
|
398
|
-
// @ts-ignore node-cache types are incorrect, ttl should be optional
|
|
399
406
|
await this.cache.set(`flags-${identifier}`, flags);
|
|
400
407
|
}
|
|
401
408
|
|
|
@@ -414,7 +421,6 @@ export class Flagsmith {
|
|
|
414
421
|
defaultFlagHandler: this.defaultFlagHandler
|
|
415
422
|
});
|
|
416
423
|
if (!!this.cache) {
|
|
417
|
-
// @ts-ignore node-cache types are incorrect, ttl should be optional
|
|
418
424
|
await this.cache.set('flags', flags);
|
|
419
425
|
}
|
|
420
426
|
return flags;
|
|
@@ -433,12 +439,16 @@ export class Flagsmith {
|
|
|
433
439
|
}
|
|
434
440
|
}
|
|
435
441
|
|
|
436
|
-
private async getIdentityFlagsFromApi(
|
|
442
|
+
private async getIdentityFlagsFromApi(
|
|
443
|
+
identifier: string,
|
|
444
|
+
traits: { [key: string]: FlagsmithTraitValue | ITraitConfig },
|
|
445
|
+
transient: boolean = false
|
|
446
|
+
) {
|
|
437
447
|
if (!this.identitiesUrl) {
|
|
438
448
|
throw new Error('`apiUrl` argument is missing or invalid.');
|
|
439
449
|
}
|
|
440
450
|
try {
|
|
441
|
-
const data = generateIdentitiesData(identifier, traits);
|
|
451
|
+
const data = generateIdentitiesData(identifier, traits, transient);
|
|
442
452
|
const jsonResponse = await this.getJSONResponse(this.identitiesUrl, 'POST', data);
|
|
443
453
|
const flags = Flags.fromAPIFlags({
|
|
444
454
|
apiFlags: jsonResponse['flags'],
|
|
@@ -446,7 +456,6 @@ export class Flagsmith {
|
|
|
446
456
|
defaultFlagHandler: this.defaultFlagHandler
|
|
447
457
|
});
|
|
448
458
|
if (!!this.cache) {
|
|
449
|
-
// @ts-ignore node-cache types are incorrect, ttl should be optional
|
|
450
459
|
await this.cache.set(`flags-${identifier}`, flags);
|
|
451
460
|
}
|
|
452
461
|
return flags;
|
package/sdk/models.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { FeatureStateModel } from '../flagsmith-engine/features/models';
|
|
2
|
-
import { AnalyticsProcessor } from './analytics';
|
|
3
|
-
import { FlagsmithClientError } from './errors';
|
|
1
|
+
import { FeatureStateModel } from '../flagsmith-engine/features/models.js';
|
|
2
|
+
import { AnalyticsProcessor } from './analytics.js';
|
|
4
3
|
|
|
5
4
|
export class BaseFlag {
|
|
6
5
|
enabled: boolean;
|
package/sdk/offline_handlers.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
|
-
import { buildEnvironmentModel } from '../flagsmith-engine/environments/util';
|
|
3
|
-
import { EnvironmentModel } from '../flagsmith-engine/environments/models';
|
|
2
|
+
import { buildEnvironmentModel } from '../flagsmith-engine/environments/util.js';
|
|
3
|
+
import { EnvironmentModel } from '../flagsmith-engine/environments/models.js';
|
|
4
4
|
|
|
5
5
|
export class BaseOfflineHandler {
|
|
6
6
|
getEnvironment() : EnvironmentModel {
|
package/sdk/polling_manager.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import Flagsmith from '.';
|
|
1
|
+
import Flagsmith from './index.js';
|
|
2
2
|
|
|
3
3
|
export class EnvironmentDataPollingManager {
|
|
4
|
-
private interval?: NodeJS.
|
|
4
|
+
private interval?: NodeJS.Timeout;
|
|
5
5
|
private main: Flagsmith;
|
|
6
6
|
private refreshIntervalSeconds: number;
|
|
7
7
|
|
|
@@ -17,7 +17,6 @@ export class EnvironmentDataPollingManager {
|
|
|
17
17
|
await this.main.updateEnvironment();
|
|
18
18
|
}, this.refreshIntervalSeconds * 1000);
|
|
19
19
|
};
|
|
20
|
-
this.main.updateEnvironment();
|
|
21
20
|
updateEnvironment();
|
|
22
21
|
}
|
|
23
22
|
|
package/sdk/types.ts
CHANGED
|
@@ -1,30 +1,41 @@
|
|
|
1
|
-
import { DefaultFlag, Flags } from
|
|
2
|
-
import { EnvironmentModel } from
|
|
3
|
-
import {
|
|
4
|
-
import { Logger } from
|
|
5
|
-
import { BaseOfflineHandler } from
|
|
1
|
+
import { DefaultFlag, Flags } from './models.js';
|
|
2
|
+
import { EnvironmentModel } from '../flagsmith-engine/index.js';
|
|
3
|
+
import { Dispatcher } from 'undici-types';
|
|
4
|
+
import { Logger } from 'pino';
|
|
5
|
+
import { BaseOfflineHandler } from './offline_handlers.js';
|
|
6
6
|
|
|
7
|
+
export type IFlagsmithValue<T = string | number | boolean | null> = T;
|
|
7
8
|
export interface FlagsmithCache {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
get(key: string): Promise<Flags | undefined> | undefined;
|
|
10
|
+
set(key: string, value: Flags, ttl?: string | number): boolean | Promise<boolean>;
|
|
11
|
+
has(key: string): boolean | Promise<boolean>;
|
|
12
|
+
[key: string]: any;
|
|
12
13
|
}
|
|
13
14
|
|
|
15
|
+
export type Fetch = typeof fetch
|
|
16
|
+
|
|
14
17
|
export interface FlagsmithConfig {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
18
|
+
environmentKey?: string;
|
|
19
|
+
apiUrl?: string;
|
|
20
|
+
agent?: Dispatcher;
|
|
21
|
+
fetch?: Fetch;
|
|
22
|
+
customHeaders?: { [key: string]: any };
|
|
23
|
+
requestTimeoutSeconds?: number;
|
|
24
|
+
enableLocalEvaluation?: boolean;
|
|
25
|
+
environmentRefreshIntervalSeconds?: number;
|
|
26
|
+
retries?: number;
|
|
27
|
+
enableAnalytics?: boolean;
|
|
28
|
+
defaultFlagHandler?: (featureName: string) => DefaultFlag;
|
|
29
|
+
cache?: FlagsmithCache;
|
|
30
|
+
onEnvironmentChange?: (error: Error | null, result: EnvironmentModel) => void;
|
|
31
|
+
logger?: Logger;
|
|
32
|
+
offlineMode?: boolean;
|
|
33
|
+
offlineHandler?: BaseOfflineHandler;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ITraitConfig {
|
|
37
|
+
value: FlagsmithTraitValue;
|
|
38
|
+
transient?: boolean;
|
|
30
39
|
}
|
|
40
|
+
|
|
41
|
+
export declare type FlagsmithTraitValue = IFlagsmithValue;
|
package/sdk/utils.ts
CHANGED
|
@@ -1,12 +1,36 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
if (typeof fetch.default !== 'undefined') fetch = fetch.default;
|
|
1
|
+
import {Fetch, FlagsmithTraitValue, ITraitConfig} from './types.js';
|
|
2
|
+
import {Dispatcher} from "undici-types";
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
type Traits = { [key: string]: ITraitConfig | FlagsmithTraitValue };
|
|
5
|
+
|
|
6
|
+
export function isTraitConfig(
|
|
7
|
+
traitValue: ITraitConfig | FlagsmithTraitValue
|
|
8
|
+
): traitValue is ITraitConfig {
|
|
9
|
+
return !!traitValue && typeof traitValue == 'object' && traitValue.value !== undefined;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function generateIdentitiesData(identifier: string, traits: Traits, transient: boolean) {
|
|
13
|
+
const traitsGenerated = Object.entries(traits).map(([key, value]) => {
|
|
14
|
+
if (isTraitConfig(value)) {
|
|
15
|
+
return {
|
|
16
|
+
trait_key: key,
|
|
17
|
+
trait_value: value?.value,
|
|
18
|
+
transient: value?.transient,
|
|
19
|
+
};
|
|
20
|
+
} else {
|
|
21
|
+
return {
|
|
22
|
+
trait_key: key,
|
|
23
|
+
trait_value: value,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
if (transient) {
|
|
28
|
+
return {
|
|
29
|
+
identifier: identifier,
|
|
30
|
+
traits: traitsGenerated,
|
|
31
|
+
transient: true
|
|
32
|
+
};
|
|
33
|
+
}
|
|
10
34
|
return {
|
|
11
35
|
identifier: identifier,
|
|
12
36
|
traits: traitsGenerated
|
|
@@ -18,40 +42,28 @@ export const delay = (ms: number) =>
|
|
|
18
42
|
|
|
19
43
|
export const retryFetch = (
|
|
20
44
|
url: string,
|
|
21
|
-
|
|
45
|
+
// built-in RequestInit type doesn't have dispatcher/agent
|
|
46
|
+
fetchOptions: RequestInit & { dispatcher?: Dispatcher },
|
|
22
47
|
retries: number = 3,
|
|
23
|
-
|
|
48
|
+
timeoutMs: number = 10, // set an overall timeout for this function
|
|
49
|
+
customFetch: Fetch,
|
|
24
50
|
): Promise<Response> => {
|
|
25
51
|
return new Promise((resolve, reject) => {
|
|
26
52
|
const retryWrapper = (n: number) => {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
.
|
|
30
|
-
if (n > 0) {
|
|
31
|
-
await delay(1000);
|
|
32
|
-
retryWrapper(--n);
|
|
33
|
-
} else {
|
|
34
|
-
reject(err);
|
|
35
|
-
}
|
|
36
|
-
});
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const requestWrapper = (): Promise<Response> => {
|
|
40
|
-
return new Promise((resolve, reject) => {
|
|
41
|
-
let timeoutId: NodeJS.Timeout;
|
|
42
|
-
if (timeout) {
|
|
43
|
-
timeoutId = setTimeout(() => reject('error: timeout'), timeout);
|
|
44
|
-
}
|
|
45
|
-
return fetch(url, fetchOptions)
|
|
46
|
-
.then(res => resolve(res))
|
|
47
|
-
.catch(err => reject(err))
|
|
48
|
-
.finally(() => {
|
|
49
|
-
if (timeoutId) {
|
|
50
|
-
clearTimeout(timeoutId);
|
|
51
|
-
}
|
|
52
|
-
})
|
|
53
|
+
customFetch(url, {
|
|
54
|
+
...fetchOptions,
|
|
55
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
53
56
|
})
|
|
54
|
-
|
|
57
|
+
.then(res => resolve(res))
|
|
58
|
+
.catch(async err => {
|
|
59
|
+
if (n > 0) {
|
|
60
|
+
await delay(1000);
|
|
61
|
+
retryWrapper(--n);
|
|
62
|
+
} else {
|
|
63
|
+
reject(err);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
};
|
|
55
67
|
|
|
56
68
|
retryWrapper(retries);
|
|
57
69
|
});
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
1
|
+
import { getIdentityFeatureStates } from '../../../flagsmith-engine/index.js';
|
|
2
|
+
import { EnvironmentModel } from '../../../flagsmith-engine/environments/models.js';
|
|
3
|
+
import { buildEnvironmentModel } from '../../../flagsmith-engine/environments/util.js';
|
|
4
|
+
import { IdentityModel } from '../../../flagsmith-engine/identities/models.js';
|
|
5
|
+
import { buildIdentityModel } from '../../../flagsmith-engine/identities/util.js';
|
|
6
|
+
import * as testData from '../engine-tests/engine-test-data/data/environment_n9fbf9h3v4fFgH3U3ngWhb.json'
|
|
7
7
|
|
|
8
8
|
function extractTestCases(
|
|
9
|
-
|
|
9
|
+
data: any
|
|
10
10
|
): {
|
|
11
11
|
environment: EnvironmentModel;
|
|
12
12
|
identity: IdentityModel;
|
|
13
13
|
response: any;
|
|
14
14
|
}[] {
|
|
15
|
-
const data = JSON.parse(readFileSync(filePath, 'utf-8'));
|
|
16
15
|
const environmentModel = buildEnvironmentModel(data['environment']);
|
|
17
16
|
const test_data = data['identities_and_responses'].map((test_case: any) => {
|
|
18
17
|
const identity = buildIdentityModel(test_case['identity']);
|
|
@@ -27,9 +26,7 @@ function extractTestCases(
|
|
|
27
26
|
}
|
|
28
27
|
|
|
29
28
|
test('Test Engine', () => {
|
|
30
|
-
const testCases = extractTestCases(
|
|
31
|
-
__dirname + '/../engine-tests/engine-test-data/data/environment_n9fbf9h3v4fFgH3U3ngWhb.json'
|
|
32
|
-
);
|
|
29
|
+
const testCases = extractTestCases(testData);
|
|
33
30
|
for (const testCase of testCases) {
|
|
34
31
|
const engine_response = getIdentityFeatureStates(testCase.environment, testCase.identity);
|
|
35
32
|
const sortedEngineFlags = engine_response.sort((a, b) =>
|
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
getEnvironmentFeatureStates,
|
|
4
4
|
getIdentityFeatureState,
|
|
5
5
|
getIdentityFeatureStates
|
|
6
|
-
} from '../../../flagsmith-engine';
|
|
7
|
-
import { CONSTANTS } from '../../../flagsmith-engine/features/constants';
|
|
8
|
-
import { FeatureModel, FeatureStateModel } from '../../../flagsmith-engine/features/models';
|
|
9
|
-
import { TraitModel } from '../../../flagsmith-engine/identities/traits/models';
|
|
6
|
+
} from '../../../flagsmith-engine/index.js';
|
|
7
|
+
import { CONSTANTS } from '../../../flagsmith-engine/features/constants.js';
|
|
8
|
+
import { FeatureModel, FeatureStateModel } from '../../../flagsmith-engine/features/models.js';
|
|
9
|
+
import { TraitModel } from '../../../flagsmith-engine/identities/traits/models.js';
|
|
10
10
|
import {
|
|
11
11
|
environment,
|
|
12
12
|
environmentWithSegmentOverride,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
identityInSegment,
|
|
18
18
|
segmentConditionProperty,
|
|
19
19
|
segmentConditionStringValue
|
|
20
|
-
} from './utils';
|
|
20
|
+
} from './utils.js';
|
|
21
21
|
|
|
22
22
|
test('test_identity_get_feature_state_without_any_override', () => {
|
|
23
23
|
const feature_state = getIdentityFeatureState(environment(), identity(), feature1().name);
|
|
@@ -2,18 +2,18 @@ import {
|
|
|
2
2
|
ALL_RULE,
|
|
3
3
|
CONDITION_OPERATORS,
|
|
4
4
|
PERCENTAGE_SPLIT,
|
|
5
|
-
} from '../../../../flagsmith-engine/segments/constants';
|
|
6
|
-
import {SegmentConditionModel} from '../../../../flagsmith-engine/segments/models';
|
|
7
|
-
import {traitsMatchSegmentCondition, evaluateIdentityInSegment} from "../../../../flagsmith-engine/segments/evaluators";
|
|
8
|
-
import {TraitModel, IdentityModel} from "../../../../flagsmith-engine";
|
|
9
|
-
import {environment} from "../utils";
|
|
10
|
-
import { buildSegmentModel } from '../../../../flagsmith-engine/segments/util';
|
|
11
|
-
import { getHashedPercentateForObjIds } from '../../../../flagsmith-engine/utils/hashing';
|
|
5
|
+
} from '../../../../flagsmith-engine/segments/constants.js';
|
|
6
|
+
import {SegmentConditionModel} from '../../../../flagsmith-engine/segments/models.js';
|
|
7
|
+
import {traitsMatchSegmentCondition, evaluateIdentityInSegment} from "../../../../flagsmith-engine/segments/evaluators.js";
|
|
8
|
+
import {TraitModel, IdentityModel} from "../../../../flagsmith-engine/index.js";
|
|
9
|
+
import {environment} from "../utils.js";
|
|
10
|
+
import { buildSegmentModel } from '../../../../flagsmith-engine/segments/util.js';
|
|
11
|
+
import { getHashedPercentateForObjIds } from '../../../../flagsmith-engine/utils/hashing/index.js';
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
// todo: work out how to implement this in a test function or before hook
|
|
15
|
-
|
|
16
|
-
getHashedPercentateForObjIds:
|
|
15
|
+
vi.mock('../../../../flagsmith-engine/utils/hashing', () => ({
|
|
16
|
+
getHashedPercentateForObjIds: vi.fn(() => 1)
|
|
17
17
|
}));
|
|
18
18
|
|
|
19
19
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { getHashedPercentateForObjIds } from '../../../../flagsmith-engine/utils/hashing';
|
|
1
|
+
import { randomUUID as uuidv4 } from 'node:crypto';
|
|
2
|
+
import { getHashedPercentateForObjIds } from '../../../../flagsmith-engine/utils/hashing/index.js';
|
|
3
3
|
|
|
4
4
|
describe('getHashedPercentageForObjIds', () => {
|
|
5
5
|
it.each([
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
import fetch from '
|
|
2
|
-
import { analyticsProcessor } from './utils';
|
|
3
|
-
|
|
4
|
-
jest.mock('node-fetch', () => jest.fn());
|
|
1
|
+
import {analyticsProcessor, fetch} from './utils.js';
|
|
5
2
|
|
|
6
3
|
afterEach(() => {
|
|
7
|
-
|
|
4
|
+
vi.resetAllMocks();
|
|
8
5
|
});
|
|
9
6
|
|
|
10
7
|
test('test_analytics_processor_track_feature_updates_analytics_data', () => {
|
|
@@ -29,15 +26,14 @@ test('test_analytics_processor_flush_post_request_data_match_ananlytics_data', a
|
|
|
29
26
|
aP.trackFeature("myFeature2");
|
|
30
27
|
await aP.flush();
|
|
31
28
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
32
|
-
expect(fetch).toHaveBeenCalledWith('http://testUrlanalytics/flags/', {
|
|
29
|
+
expect(fetch).toHaveBeenCalledWith('http://testUrlanalytics/flags/', expect.objectContaining({
|
|
33
30
|
body: '{"myFeature1":1,"myFeature2":1}',
|
|
34
31
|
headers: { 'Content-Type': 'application/json', 'X-Environment-Key': 'test-key' },
|
|
35
32
|
method: 'POST',
|
|
36
|
-
|
|
37
|
-
});
|
|
33
|
+
}));
|
|
38
34
|
});
|
|
39
35
|
|
|
40
|
-
|
|
36
|
+
vi.useFakeTimers()
|
|
41
37
|
test('test_analytics_processor_flush_post_request_data_match_ananlytics_data_test', async () => {
|
|
42
38
|
const aP = analyticsProcessor();
|
|
43
39
|
aP.trackFeature("myFeature1");
|
|
@@ -45,7 +41,7 @@ test('test_analytics_processor_flush_post_request_data_match_ananlytics_data_tes
|
|
|
45
41
|
aP.trackFeature("myFeature2");
|
|
46
42
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
47
43
|
}, 15000);
|
|
48
|
-
|
|
44
|
+
vi.runOnlyPendingTimers();
|
|
49
45
|
});
|
|
50
46
|
|
|
51
47
|
test('test_analytics_processor_flush_early_exit_if_analytics_data_is_empty', async () => {
|
|
@@ -58,7 +54,7 @@ test('test_analytics_processor_flush_early_exit_if_analytics_data_is_empty', asy
|
|
|
58
54
|
test('errors in fetch sending analytics data are swallowed', async () => {
|
|
59
55
|
// Given
|
|
60
56
|
// we mock the fetch function to throw and error to mimick a network failure
|
|
61
|
-
|
|
57
|
+
fetch.mockRejectedValue('some error');
|
|
62
58
|
|
|
63
59
|
// and create the processor and track a feature so there is some analytics data
|
|
64
60
|
const processor = analyticsProcessor();
|
|
@@ -76,7 +72,6 @@ test('errors in fetch sending analytics data are swallowed', async () => {
|
|
|
76
72
|
test('analytics is only flushed once even if requested concurrently', async () => {
|
|
77
73
|
const processor = analyticsProcessor();
|
|
78
74
|
processor.trackFeature('myFeature');
|
|
79
|
-
// @ts-ignore
|
|
80
75
|
fetch.mockImplementation(() => {
|
|
81
76
|
return new Promise((resolve, _) => {
|
|
82
77
|
setTimeout(resolve, 1000)
|
|
@@ -86,7 +81,7 @@ test('analytics is only flushed once even if requested concurrently', async () =
|
|
|
86
81
|
processor.flush(),
|
|
87
82
|
processor.flush(),
|
|
88
83
|
])
|
|
89
|
-
|
|
84
|
+
vi.runOnlyPendingTimers();
|
|
90
85
|
await flushes;
|
|
91
86
|
expect(fetch).toHaveBeenCalledTimes(1)
|
|
92
87
|
})
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"traits": [
|
|
3
|
+
{
|
|
4
|
+
"id": 1,
|
|
5
|
+
"trait_key": "some_trait",
|
|
6
|
+
"trait_value": "some_value"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"id": 2,
|
|
10
|
+
"trait_key": "transient_key",
|
|
11
|
+
"trait_value": "transient_value",
|
|
12
|
+
"transient": true
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"id": 3,
|
|
16
|
+
"trait_key": "explicitly_non_transient_trait",
|
|
17
|
+
"trait_value": "non_transient_value",
|
|
18
|
+
"transient": false
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"flags": [
|
|
22
|
+
{
|
|
23
|
+
"id": 1,
|
|
24
|
+
"feature": {
|
|
25
|
+
"id": 1,
|
|
26
|
+
"name": "some_feature",
|
|
27
|
+
"created_date": "2019-08-27T14:53:45.698555Z",
|
|
28
|
+
"initial_value": null,
|
|
29
|
+
"description": null,
|
|
30
|
+
"default_enabled": false,
|
|
31
|
+
"type": "STANDARD",
|
|
32
|
+
"project": 1
|
|
33
|
+
},
|
|
34
|
+
"feature_state_value": "some-identity-with-transient-trait-value",
|
|
35
|
+
"enabled": true,
|
|
36
|
+
"environment": 1,
|
|
37
|
+
"identity": null,
|
|
38
|
+
"feature_segment": null
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"traits": [
|
|
3
|
+
{
|
|
4
|
+
"id": 1,
|
|
5
|
+
"trait_key": "some_trait",
|
|
6
|
+
"trait_value": "some_value"
|
|
7
|
+
}
|
|
8
|
+
],
|
|
9
|
+
"flags": [
|
|
10
|
+
{
|
|
11
|
+
"id": 1,
|
|
12
|
+
"feature": {
|
|
13
|
+
"id": 1,
|
|
14
|
+
"name": "some_feature",
|
|
15
|
+
"created_date": "2019-08-27T14:53:45.698555Z",
|
|
16
|
+
"initial_value": null,
|
|
17
|
+
"description": null,
|
|
18
|
+
"default_enabled": false,
|
|
19
|
+
"type": "STANDARD",
|
|
20
|
+
"project": 1
|
|
21
|
+
},
|
|
22
|
+
"feature_state_value": "some-transient-identity-value",
|
|
23
|
+
"enabled": false,
|
|
24
|
+
"environment": 1,
|
|
25
|
+
"identity": null,
|
|
26
|
+
"feature_segment": null
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
}
|