flagsmith-nodejs 2.0.0-beta.1 → 2.0.0-beta.2
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/build/flagsmith-engine/environments/models.d.ts +2 -2
- package/build/flagsmith-engine/environments/models.js +6 -6
- package/build/flagsmith-engine/features/models.d.ts +2 -2
- package/build/flagsmith-engine/features/models.js +7 -7
- package/build/flagsmith-engine/identities/models.d.ts +1 -1
- package/build/flagsmith-engine/identities/models.js +5 -5
- package/build/flagsmith-engine/identities/util.js +1 -1
- package/build/flagsmith-engine/index.d.ts +1 -1
- package/build/flagsmith-engine/index.js +18 -17
- package/build/flagsmith-engine/organisations/models.d.ts +1 -1
- package/build/flagsmith-engine/organisations/models.js +4 -4
- package/build/flagsmith-engine/projects/models.d.ts +1 -1
- package/build/flagsmith-engine/projects/models.js +2 -3
- package/build/flagsmith-engine/segments/evaluators.js +2 -2
- package/build/flagsmith-engine/utils/errors.d.ts +2 -0
- package/build/flagsmith-engine/utils/errors.js +6 -0
- package/build/flagsmith-engine/utils/hashing/index.d.ts +8 -0
- package/build/flagsmith-engine/utils/hashing/index.js +9 -11
- package/build/index.d.ts +1 -2
- package/build/index.js +5 -1
- package/build/sdk/analytics.js +0 -21
- package/build/sdk/index.d.ts +1 -2
- package/build/sdk/index.js +3 -9
- package/build/sdk/polling_manager.d.ts +1 -1
- package/build/sdk/utils.d.ts +1 -1
- package/build/sdk/utils.js +1 -1
- package/example/package-lock.json +1070 -2
- package/example/package.json +1 -0
- package/example/server/api/index.js +9 -7
- package/flagsmith-engine/environments/models.ts +8 -8
- package/flagsmith-engine/features/models.ts +7 -7
- package/flagsmith-engine/identities/models.ts +8 -8
- package/flagsmith-engine/identities/util.ts +1 -1
- package/flagsmith-engine/index.ts +20 -19
- package/flagsmith-engine/organisations/models.ts +6 -6
- package/flagsmith-engine/projects/models.ts +2 -3
- package/flagsmith-engine/segments/evaluators.ts +2 -2
- package/flagsmith-engine/utils/errors.ts +1 -0
- package/flagsmith-engine/utils/hashing/index.ts +9 -14
- package/index.ts +4 -1
- package/package.json +9 -8
- package/sdk/analytics.ts +0 -28
- package/sdk/index.ts +9 -21
- package/sdk/models.ts +3 -1
- package/sdk/polling_manager.ts +4 -4
- package/sdk/utils.ts +1 -1
- package/tests/sdk/flagsmith.test.ts +1 -1
- package/tests/sdk/polling.test.ts +1 -1
- package/tests/sdk/utils.ts +1 -1
package/example/package.json
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
const Router = require('express').Router;
|
|
2
|
-
const
|
|
3
|
-
const environmentKey =
|
|
4
|
-
if(!environmentKey) {
|
|
5
|
-
throw new Error(
|
|
2
|
+
const Flagsmith = require('../../../build');
|
|
3
|
+
const environmentKey = '';
|
|
4
|
+
if (!environmentKey) {
|
|
5
|
+
throw new Error(
|
|
6
|
+
'Please generate a Server Side SDK Key in environment settings to run the example'
|
|
7
|
+
);
|
|
6
8
|
}
|
|
7
9
|
const flagsmith = new Flagsmith({
|
|
8
10
|
environmentKey,
|
|
9
11
|
enableLocalEvaluation: true,
|
|
10
|
-
defaultFlagHandler:
|
|
11
|
-
return {enabled:false, isDefault:true, value:null}
|
|
12
|
+
defaultFlagHandler: str => {
|
|
13
|
+
return { enabled: false, isDefault: true, value: null };
|
|
12
14
|
}
|
|
13
15
|
});
|
|
14
16
|
|
|
@@ -21,7 +23,7 @@ module.exports = () => {
|
|
|
21
23
|
});
|
|
22
24
|
|
|
23
25
|
api.get('/:user', async (req, res) => {
|
|
24
|
-
const flags = await flagsmith.getIdentityFlags(req.params.user, {checkout_v2: 1});
|
|
26
|
+
const flags = await flagsmith.getIdentityFlags(req.params.user, { checkout_v2: 1 });
|
|
25
27
|
const fontSize = flags.getFeatureValue('font_size');
|
|
26
28
|
const checkoutV2 = flags.isFeatureEnabled('checkout_v2');
|
|
27
29
|
res.json({ fontSize, checkoutV2 });
|
|
@@ -14,17 +14,17 @@ export class EnvironmentAPIKeyModel {
|
|
|
14
14
|
constructor(
|
|
15
15
|
id: number,
|
|
16
16
|
key: string,
|
|
17
|
-
|
|
17
|
+
createdAt: number,
|
|
18
18
|
name: string,
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
clientApiKey: string,
|
|
20
|
+
expiresAt?: number
|
|
21
21
|
) {
|
|
22
22
|
this.id = id;
|
|
23
23
|
this.key = key;
|
|
24
|
-
this.createdAt =
|
|
24
|
+
this.createdAt = createdAt;
|
|
25
25
|
this.name = name;
|
|
26
|
-
this.clientApiKey =
|
|
27
|
-
this.expiresAt =
|
|
26
|
+
this.clientApiKey = clientApiKey;
|
|
27
|
+
this.expiresAt = expiresAt;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
isValid() {
|
|
@@ -42,9 +42,9 @@ export class EnvironmentModel {
|
|
|
42
42
|
mixpanel_config?: IntegrationModel;
|
|
43
43
|
heap_config?: IntegrationModel;
|
|
44
44
|
|
|
45
|
-
constructor(id: number,
|
|
45
|
+
constructor(id: number, apiKey: string, project: ProjectModel) {
|
|
46
46
|
this.id = id;
|
|
47
|
-
this.apiKey =
|
|
47
|
+
this.apiKey = apiKey;
|
|
48
48
|
this.project = project;
|
|
49
49
|
}
|
|
50
50
|
}
|
|
@@ -51,7 +51,7 @@ export class FeatureStateModel {
|
|
|
51
51
|
enabled: boolean;
|
|
52
52
|
djangoID: number;
|
|
53
53
|
featurestateUUID: string = uuidv4();
|
|
54
|
-
|
|
54
|
+
private value: any;
|
|
55
55
|
multivariateFeatureStateValues: MultivariateFeatureStateValueModel[] = [];
|
|
56
56
|
|
|
57
57
|
constructor(
|
|
@@ -59,24 +59,24 @@ export class FeatureStateModel {
|
|
|
59
59
|
enabled: boolean,
|
|
60
60
|
djangoID: number,
|
|
61
61
|
value?: any,
|
|
62
|
-
|
|
62
|
+
featurestateUuid: string = uuidv4()
|
|
63
63
|
) {
|
|
64
64
|
this.feature = feature;
|
|
65
65
|
this.enabled = enabled;
|
|
66
66
|
this.djangoID = djangoID;
|
|
67
|
-
this.
|
|
68
|
-
this.featurestateUUID =
|
|
67
|
+
this.value = value;
|
|
68
|
+
this.featurestateUUID = featurestateUuid;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
setValue(value: any) {
|
|
72
|
-
this.
|
|
72
|
+
this.value = value;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
getValue(identityId?: number | string) {
|
|
76
76
|
if (!!identityId && this.multivariateFeatureStateValues.length > 0) {
|
|
77
77
|
return this.getMultivariateValue(identityId);
|
|
78
78
|
}
|
|
79
|
-
return this.
|
|
79
|
+
return this.value;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
get_feature_state_value() {
|
|
@@ -100,6 +100,6 @@ export class FeatureStateModel {
|
|
|
100
100
|
}
|
|
101
101
|
startPercentage = limit;
|
|
102
102
|
}
|
|
103
|
-
return this.
|
|
103
|
+
return this.value;
|
|
104
104
|
}
|
|
105
105
|
}
|
|
@@ -15,17 +15,17 @@ export class IdentityModel {
|
|
|
15
15
|
|
|
16
16
|
constructor(
|
|
17
17
|
created_date: string,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
identityTraits: TraitModel[],
|
|
19
|
+
identityFeatures: IdentityFeaturesList,
|
|
20
|
+
environmentApiKey: string,
|
|
21
21
|
identifier: string,
|
|
22
|
-
|
|
22
|
+
identityUuid?: string
|
|
23
23
|
) {
|
|
24
|
-
this.identityUuid =
|
|
24
|
+
this.identityUuid = identityUuid || uuidv4();
|
|
25
25
|
this.createdDate = Date.parse(created_date) || Date.now();
|
|
26
|
-
this.identityTraits =
|
|
27
|
-
this.identityFeatures = new IdentityFeaturesList(...
|
|
28
|
-
this.environmentApiKey =
|
|
26
|
+
this.identityTraits = identityTraits;
|
|
27
|
+
this.identityFeatures = new IdentityFeaturesList(...identityFeatures);
|
|
28
|
+
this.environmentApiKey = environmentApiKey;
|
|
29
29
|
this.identifier = identifier;
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -4,56 +4,57 @@ import { IdentityModel } from './identities/models';
|
|
|
4
4
|
import { TraitModel } from './identities/traits/models';
|
|
5
5
|
import { getIdentitySegments } from './segments/evaluators';
|
|
6
6
|
import { SegmentModel } from './segments/models';
|
|
7
|
+
import { FeatureStateNotFound } from './utils/errors';
|
|
7
8
|
|
|
8
9
|
function getIdentityFeatureStatesDict(
|
|
9
10
|
environment: EnvironmentModel,
|
|
10
11
|
identity: IdentityModel,
|
|
11
|
-
|
|
12
|
+
overrideTraits?: TraitModel[]
|
|
12
13
|
) {
|
|
13
14
|
// Get feature states from the environment
|
|
14
|
-
const
|
|
15
|
+
const featureStates: { [key: number]: FeatureStateModel } = {};
|
|
15
16
|
for (const fs of environment.featureStates) {
|
|
16
|
-
|
|
17
|
+
featureStates[fs.feature.id] = fs;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
// Override with any feature states defined by matching segments
|
|
20
|
-
const
|
|
21
|
+
const identitySegments: SegmentModel[] = getIdentitySegments(
|
|
21
22
|
environment,
|
|
22
23
|
identity,
|
|
23
|
-
|
|
24
|
+
overrideTraits
|
|
24
25
|
);
|
|
25
|
-
for (const
|
|
26
|
-
for (const
|
|
26
|
+
for (const matchingSegment of identitySegments) {
|
|
27
|
+
for (const featureState of matchingSegment.featureStates) {
|
|
27
28
|
// note that feature states are stored on the segment in descending priority
|
|
28
29
|
// order so we only care that the last one is added
|
|
29
30
|
// TODO: can we optimise this?
|
|
30
|
-
|
|
31
|
+
featureStates[featureState.feature.id] = featureState;
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
// Override with any feature states defined directly the identity
|
|
35
36
|
for (const fs of identity.identityFeatures || []) {
|
|
36
|
-
if (
|
|
37
|
-
|
|
37
|
+
if (featureStates[fs.feature.id]) {
|
|
38
|
+
featureStates[fs.feature.id] = fs;
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
|
-
return
|
|
41
|
+
return featureStates;
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
export function getIdentityFeatureState(
|
|
44
45
|
environment: EnvironmentModel,
|
|
45
46
|
identity: IdentityModel,
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
featureName: string,
|
|
48
|
+
overrideTraits?: TraitModel[]
|
|
48
49
|
): FeatureStateModel {
|
|
49
|
-
const featureStates = getIdentityFeatureStatesDict(environment, identity,
|
|
50
|
+
const featureStates = getIdentityFeatureStatesDict(environment, identity, overrideTraits);
|
|
50
51
|
|
|
51
52
|
const matchingFeature = Object.values(featureStates).filter(
|
|
52
|
-
f => f.feature.name ===
|
|
53
|
+
f => f.feature.name === featureName
|
|
53
54
|
);
|
|
54
55
|
|
|
55
56
|
if (matchingFeature.length === 0) {
|
|
56
|
-
throw new
|
|
57
|
+
throw new FeatureStateNotFound('Feature State Not Found');
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
return matchingFeature[0];
|
|
@@ -64,14 +65,14 @@ export function getIdentityFeatureStates(
|
|
|
64
65
|
identity: IdentityModel,
|
|
65
66
|
overrideTraits?: TraitModel[]
|
|
66
67
|
): FeatureStateModel[] {
|
|
67
|
-
const
|
|
68
|
+
const featureStates = Object.values(
|
|
68
69
|
getIdentityFeatureStatesDict(environment, identity, overrideTraits)
|
|
69
70
|
);
|
|
70
71
|
|
|
71
72
|
if (environment.project.hideDisabledFlags) {
|
|
72
|
-
return
|
|
73
|
+
return featureStates.filter(fs => !!fs.enabled);
|
|
73
74
|
}
|
|
74
|
-
return
|
|
75
|
+
return featureStates;
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
export function getEnvironmentFeatureState(environment: EnvironmentModel, featureName: string) {
|
|
@@ -8,15 +8,15 @@ export class OrganisationModel {
|
|
|
8
8
|
constructor(
|
|
9
9
|
id: number,
|
|
10
10
|
name: string,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
featureAnalytics: boolean,
|
|
12
|
+
stopServingFlags: boolean,
|
|
13
|
+
persistTraitData: boolean
|
|
14
14
|
) {
|
|
15
15
|
this.id = id;
|
|
16
16
|
this.name = name;
|
|
17
|
-
this.featureAnalytics =
|
|
18
|
-
this.stopServingFlags =
|
|
19
|
-
this.persistTraitData =
|
|
17
|
+
this.featureAnalytics = featureAnalytics;
|
|
18
|
+
this.stopServingFlags = stopServingFlags;
|
|
19
|
+
this.persistTraitData = persistTraitData;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
get unique_slug() {
|
|
@@ -6,18 +6,17 @@ export class ProjectModel {
|
|
|
6
6
|
name: string;
|
|
7
7
|
organisation: OrganisationModel;
|
|
8
8
|
hideDisabledFlags: boolean;
|
|
9
|
-
// FIXME
|
|
10
9
|
segments: SegmentModel[] = [];
|
|
11
10
|
|
|
12
11
|
constructor(
|
|
13
12
|
id: number,
|
|
14
13
|
name: string,
|
|
15
|
-
|
|
14
|
+
hideDisabledFlags: boolean,
|
|
16
15
|
organization: OrganisationModel
|
|
17
16
|
) {
|
|
18
17
|
this.id = id;
|
|
19
18
|
this.name = name;
|
|
20
|
-
this.hideDisabledFlags =
|
|
19
|
+
this.hideDisabledFlags = hideDisabledFlags;
|
|
21
20
|
this.organisation = organization;
|
|
22
21
|
}
|
|
23
22
|
}
|
|
@@ -56,7 +56,7 @@ function traitsMatchSegmentRule(
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
function traitsMatchSegmentCondition(
|
|
59
|
-
|
|
59
|
+
identityTraits: TraitModel[],
|
|
60
60
|
condition: SegmentConditionModel,
|
|
61
61
|
segmentId: number | string,
|
|
62
62
|
identityId: number | string
|
|
@@ -65,7 +65,7 @@ function traitsMatchSegmentCondition(
|
|
|
65
65
|
return getHashedPercentateForObjIds([segmentId, identityId]) <= parseFloat(condition.value);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
const traits =
|
|
68
|
+
const traits = identityTraits.filter(t => t.traitKey === condition.property_);
|
|
69
69
|
const trait = traits.length > 0 ? traits[0] : undefined;
|
|
70
70
|
|
|
71
71
|
return trait ? condition.matchesTraitValue(trait.traitValue) : false;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export class FeatureStateNotFound extends Error {}
|
|
@@ -1,22 +1,10 @@
|
|
|
1
1
|
import md5 from 'md5';
|
|
2
2
|
import bigInt from 'big-integer';
|
|
3
3
|
|
|
4
|
-
// def get_hashed_percentage_for_object_ids(
|
|
5
|
-
// object_ids: typing.Iterable[typing.Any], iterations: int = 1
|
|
6
|
-
// ) -> float:
|
|
7
|
-
// """
|
|
8
|
-
// Given a list of object ids, get a floating point number between 0 and 1 based on
|
|
9
|
-
// the hash of those ids. This should give the same value every time for any
|
|
10
|
-
// list of ids.
|
|
11
|
-
|
|
12
|
-
// :param object_ids: list of object ids to calculate the has for
|
|
13
|
-
// :param iterations: num times to include each id in the generated string to hash
|
|
14
|
-
// :return: (float) number between 0 (inclusive) and 100 (exclusive)
|
|
15
|
-
// """
|
|
16
|
-
|
|
17
4
|
const makeRepeated = (arr: Array<any>, repeats: number) =>
|
|
18
5
|
Array.from({ length: repeats }, () => arr).flat();
|
|
19
6
|
|
|
7
|
+
// https://stackoverflow.com/questions/12532871/how-to-convert-a-very-large-hex-number-to-decimal-in-javascript
|
|
20
8
|
function h2d(s: any): string {
|
|
21
9
|
function add(x: any, y: any) {
|
|
22
10
|
var c = 0,
|
|
@@ -42,7 +30,14 @@ function h2d(s: any): string {
|
|
|
42
30
|
});
|
|
43
31
|
return dec;
|
|
44
32
|
}
|
|
45
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Given a list of object ids, get a floating point number between 0 and 1 based on
|
|
35
|
+
* the hash of those ids. This should give the same value every time for any list of ids.
|
|
36
|
+
*
|
|
37
|
+
* @param {Array<any>} objectIds list of object ids to calculate the has for
|
|
38
|
+
* @param {} iterations=1 num times to include each id in the generated string to hash
|
|
39
|
+
* @returns number number between 0 (inclusive) and 100 (exclusive)
|
|
40
|
+
*/
|
|
46
41
|
export function getHashedPercentateForObjIds(objectIds: Array<any>, iterations = 1): number {
|
|
47
42
|
let to_hash = makeRepeated(objectIds, iterations).join(',');
|
|
48
43
|
const hashedValue = md5(to_hash);
|
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flagsmith-nodejs",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.2",
|
|
4
4
|
"description": "Flagsmith lets you manage features flags and remote config across web, mobile and server side applications. Deliver true Continuous Integration. Get builds out faster. Control who has access to new features.",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
|
-
"url": "https://github.com/
|
|
8
|
+
"url": "https://github.com/Flagsmith/flagsmith-nodejs-client"
|
|
9
9
|
},
|
|
10
10
|
"keywords": [
|
|
11
11
|
"nodejs",
|
|
12
|
-
"bullet train",
|
|
13
12
|
"flagsmith",
|
|
14
|
-
"feature
|
|
13
|
+
"feature flags",
|
|
14
|
+
"feature toggles",
|
|
15
|
+
"remote configuration",
|
|
15
16
|
"continuous deployment"
|
|
16
17
|
],
|
|
17
18
|
"bugs": {
|
|
18
|
-
"url": "https://github.com/
|
|
19
|
+
"url": "https://github.com/Flagsmith/flagsmith-nodejs-client/issues"
|
|
19
20
|
},
|
|
20
|
-
"homepage": "http://
|
|
21
|
-
"author": "
|
|
21
|
+
"homepage": "http://flagsmith.com/",
|
|
22
|
+
"author": "Flagsmith",
|
|
22
23
|
"contributors": [
|
|
23
24
|
{
|
|
24
25
|
"name": "Tom Stuart",
|
|
@@ -26,7 +27,7 @@
|
|
|
26
27
|
},
|
|
27
28
|
{
|
|
28
29
|
"name": "Kyle Johnson",
|
|
29
|
-
"email": "kyle@
|
|
30
|
+
"email": "kyle.johnson@flagsmith.com",
|
|
30
31
|
"url": "https://www.npmjs.com/~kyle-ssg"
|
|
31
32
|
},
|
|
32
33
|
{
|
package/sdk/analytics.ts
CHANGED
|
@@ -7,34 +7,6 @@ const ANALYTICS_TIMER = 10;
|
|
|
7
7
|
|
|
8
8
|
const delay = (ms: number) => new Promise(resolve => setTimeout(() => resolve(undefined), ms));
|
|
9
9
|
|
|
10
|
-
const retryFetch = (
|
|
11
|
-
url: string,
|
|
12
|
-
fetchOptions = {},
|
|
13
|
-
retries = 3,
|
|
14
|
-
retryDelay = 1000,
|
|
15
|
-
timeout: number
|
|
16
|
-
) => {
|
|
17
|
-
return new Promise((resolve, reject) => {
|
|
18
|
-
// check for timeout
|
|
19
|
-
if (timeout) setTimeout(() => reject('error: timeout'), timeout);
|
|
20
|
-
|
|
21
|
-
const wrapper = (n: number) => {
|
|
22
|
-
fetch(url, fetchOptions)
|
|
23
|
-
.then(res => resolve(res))
|
|
24
|
-
.catch(async err => {
|
|
25
|
-
if (n > 0) {
|
|
26
|
-
await delay(retryDelay);
|
|
27
|
-
wrapper(--n);
|
|
28
|
-
} else {
|
|
29
|
-
reject(err);
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
wrapper(retries);
|
|
35
|
-
});
|
|
36
|
-
};
|
|
37
|
-
|
|
38
10
|
export class AnalyticsProcessor {
|
|
39
11
|
private analyticsEndpoint: string;
|
|
40
12
|
private environmentKey: string;
|
package/sdk/index.ts
CHANGED
|
@@ -29,7 +29,7 @@ export class Flagsmith {
|
|
|
29
29
|
environmentUrl: string;
|
|
30
30
|
|
|
31
31
|
environmentDataPollingManager?: EnvironmentDataPollingManager;
|
|
32
|
-
environment
|
|
32
|
+
environment!: EnvironmentModel;
|
|
33
33
|
private analyticsProcessor?: AnalyticsProcessor;
|
|
34
34
|
/**
|
|
35
35
|
* A Flagsmith client.
|
|
@@ -149,7 +149,7 @@ export class Flagsmith {
|
|
|
149
149
|
method: string,
|
|
150
150
|
body?: { [key: string]: any }
|
|
151
151
|
): Promise<any> {
|
|
152
|
-
const headers: { [key: string]: any } = {};
|
|
152
|
+
const headers: { [key: string]: any } = {'Content-Type': 'application/json'};
|
|
153
153
|
if (this.environmentKey) {
|
|
154
154
|
headers['X-Environment-Key'] = this.environmentKey as string;
|
|
155
155
|
}
|
|
@@ -188,12 +188,6 @@ export class Flagsmith {
|
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
private getEnvironmentFlagsFromDocument() {
|
|
191
|
-
if (!this.environment) {
|
|
192
|
-
throw new FlagsmithClientError(
|
|
193
|
-
'Unable to build identity model when no local environment present.'
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
191
|
return Flags.fromFeatureStateModels({
|
|
198
192
|
featureStates: getEnvironmentFeatureStates(this.environment),
|
|
199
193
|
analyticsProcessor: this.analyticsProcessor,
|
|
@@ -202,18 +196,13 @@ export class Flagsmith {
|
|
|
202
196
|
}
|
|
203
197
|
|
|
204
198
|
private getIdentityFlagsFromDocument(identifier: string, traits: { [key: string]: any }) {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const identityModel = this.buildIdentityModel(identifier, Object.keys(traits).map((key)=>(
|
|
212
|
-
{
|
|
199
|
+
const identityModel = this.buildIdentityModel(
|
|
200
|
+
identifier,
|
|
201
|
+
Object.keys(traits).map(key => ({
|
|
213
202
|
key,
|
|
214
203
|
value: traits[key]
|
|
215
|
-
}
|
|
216
|
-
)
|
|
204
|
+
}))
|
|
205
|
+
);
|
|
217
206
|
|
|
218
207
|
const featureStates = getIdentityFeatureStates(this.environment, identityModel);
|
|
219
208
|
|
|
@@ -265,7 +254,7 @@ export class Flagsmith {
|
|
|
265
254
|
}
|
|
266
255
|
}
|
|
267
256
|
|
|
268
|
-
private buildIdentityModel(identifier: string, traits: { key:string
|
|
257
|
+
private buildIdentityModel(identifier: string, traits: { key: string; value: any }[]) {
|
|
269
258
|
if (!this.environment) {
|
|
270
259
|
throw new FlagsmithClientError(
|
|
271
260
|
'Unable to build identity model when no local environment present.'
|
|
@@ -275,8 +264,7 @@ export class Flagsmith {
|
|
|
275
264
|
const traitModels = traits.map(trait => new TraitModel(trait.key, trait.value));
|
|
276
265
|
return new IdentityModel('0', traitModels, [], this.environment.apiKey, identifier);
|
|
277
266
|
}
|
|
278
|
-
|
|
279
|
-
stop() {}
|
|
280
267
|
}
|
|
281
268
|
|
|
282
269
|
export default Flagsmith;
|
|
270
|
+
// export = Flagsmith;
|
package/sdk/models.ts
CHANGED
|
@@ -123,7 +123,9 @@ export class Flags {
|
|
|
123
123
|
if (this.defaultFlagHandler) {
|
|
124
124
|
return this.defaultFlagHandler(featureName);
|
|
125
125
|
}
|
|
126
|
-
throw new FlagsmithClientError(
|
|
126
|
+
throw new FlagsmithClientError(
|
|
127
|
+
`Feature does not exist: ${featureName}, implement defaultFlagHandler to handle this case.`
|
|
128
|
+
);
|
|
127
129
|
}
|
|
128
130
|
|
|
129
131
|
if (this.analyticsProcessor && flag.featureId) {
|
package/sdk/polling_manager.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import Flagsmith from '.';
|
|
2
2
|
|
|
3
3
|
export class EnvironmentDataPollingManager {
|
|
4
4
|
private interval?: NodeJS.Timer;
|
|
@@ -12,14 +12,14 @@ export class EnvironmentDataPollingManager {
|
|
|
12
12
|
|
|
13
13
|
start() {
|
|
14
14
|
const updateEnvironment = () => {
|
|
15
|
-
if(this.interval) clearInterval(this.interval)
|
|
15
|
+
if (this.interval) clearInterval(this.interval);
|
|
16
16
|
this.interval = setInterval(async () => {
|
|
17
17
|
await this.main.updateEnvironment();
|
|
18
18
|
}, this.refreshIntervalSeconds * 1000);
|
|
19
19
|
};
|
|
20
20
|
// todo: this call should be awaited for getIdentityFlags/getEnvironmentFlags when enableLocalEvaluation is true
|
|
21
|
-
this.main.updateEnvironment()
|
|
22
|
-
updateEnvironment()
|
|
21
|
+
this.main.updateEnvironment();
|
|
22
|
+
updateEnvironment();
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
stop() {
|
package/sdk/utils.ts
CHANGED
|
@@ -3,7 +3,7 @@ import fetch, { Response } from 'node-fetch';
|
|
|
3
3
|
if (typeof fetch.default !== 'undefined') fetch = fetch.default;
|
|
4
4
|
|
|
5
5
|
export function generateIdentitiesData(identifier: string, traits?: { [key: string]: any }) {
|
|
6
|
-
const traitsGenerated = Object.
|
|
6
|
+
const traitsGenerated = Object.entries(traits || {}).map(trait => ({
|
|
7
7
|
trait_key: trait[0],
|
|
8
8
|
trait_value: trait[1]
|
|
9
9
|
}));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import Flagsmith from '../../sdk';
|
|
2
2
|
import { EnvironmentDataPollingManager } from '../../sdk/polling_manager';
|
|
3
3
|
import fetch, { Headers } from 'node-fetch';
|
|
4
4
|
import { environmentJSON, environmentModel, flagsJSON, flagsmith, identitiesJSON } from './utils';
|
package/tests/sdk/utils.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from 'fs';
|
|
2
2
|
import { buildEnvironmentModel } from '../../flagsmith-engine/environments/util';
|
|
3
3
|
import { AnalyticsProcessor } from '../../sdk/analytics';
|
|
4
|
-
import
|
|
4
|
+
import Flagsmith from '../../sdk';
|
|
5
5
|
|
|
6
6
|
const DATA_DIR = __dirname + '/data/';
|
|
7
7
|
|