flagsmith-nodejs 3.2.0 → 3.3.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/.husky/pre-commit +1 -1
- package/README.md +2 -1
- package/build/flagsmith-engine/environments/models.d.ts +2 -5
- package/build/flagsmith-engine/environments/models.js +1 -0
- package/build/flagsmith-engine/environments/util.js +8 -2
- package/build/flagsmith-engine/features/util.js +2 -2
- package/build/flagsmith-engine/index.d.ts +0 -1
- package/build/flagsmith-engine/index.js +11 -13
- package/build/index.d.ts +2 -1
- package/build/index.js +1 -2
- package/build/sdk/index.d.ts +3 -1
- package/build/sdk/index.js +22 -12
- package/flagsmith-engine/environments/models.ts +2 -5
- package/flagsmith-engine/environments/util.ts +6 -0
- package/flagsmith-engine/features/util.ts +14 -13
- package/flagsmith-engine/index.ts +0 -1
- package/index.ts +4 -1
- package/package.json +1 -1
- package/sdk/index.ts +22 -10
- package/tests/sdk/data/environment.json +27 -1
- package/tests/sdk/flagsmith.test.ts +31 -42
- package/build/flagsmith-engine/environments/integrations/models.d.ts +0 -4
- package/build/flagsmith-engine/environments/integrations/models.js +0 -11
- package/examples/README.md +0 -3
- package/flagsmith-engine/environments/integrations/models.ts +0 -4
package/.husky/pre-commit
CHANGED
package/README.md
CHANGED
|
@@ -22,7 +22,8 @@ If you encounter a bug or feature request we would like to hear about it. Before
|
|
|
22
22
|
## Testing
|
|
23
23
|
|
|
24
24
|
To run the local tests you need to run following command beforehand:
|
|
25
|
-
|
|
25
|
+
|
|
26
|
+
```bash
|
|
26
27
|
git submodule add git@github.com:Flagsmith/engine-test-data.git tests/engine/engine-tests/engine-test-data/
|
|
27
28
|
```
|
|
28
29
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FeatureStateModel } from '../features/models';
|
|
2
|
+
import { IdentityModel } from '../identities/models';
|
|
2
3
|
import { ProjectModel } from '../projects/models';
|
|
3
|
-
import { IntegrationModel } from './integrations/models';
|
|
4
4
|
export declare class EnvironmentAPIKeyModel {
|
|
5
5
|
id: number;
|
|
6
6
|
key: string;
|
|
@@ -17,9 +17,6 @@ export declare class EnvironmentModel {
|
|
|
17
17
|
apiKey: string;
|
|
18
18
|
project: ProjectModel;
|
|
19
19
|
featureStates: FeatureStateModel[];
|
|
20
|
-
|
|
21
|
-
segment_config?: IntegrationModel;
|
|
22
|
-
mixpanel_config?: IntegrationModel;
|
|
23
|
-
heap_config?: IntegrationModel;
|
|
20
|
+
identityOverrides: IdentityModel[];
|
|
24
21
|
constructor(id: number, apiKey: string, project: ProjectModel);
|
|
25
22
|
}
|
|
@@ -20,6 +20,7 @@ exports.EnvironmentAPIKeyModel = EnvironmentAPIKeyModel;
|
|
|
20
20
|
var EnvironmentModel = /** @class */ (function () {
|
|
21
21
|
function EnvironmentModel(id, apiKey, project) {
|
|
22
22
|
this.featureStates = [];
|
|
23
|
+
this.identityOverrides = [];
|
|
23
24
|
this.id = id;
|
|
24
25
|
this.apiKey = apiKey;
|
|
25
26
|
this.project = project;
|
|
@@ -2,15 +2,21 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.buildEnvironmentAPIKeyModel = exports.buildEnvironmentModel = void 0;
|
|
4
4
|
var util_1 = require("../features/util");
|
|
5
|
-
var util_2 = require("../
|
|
5
|
+
var util_2 = require("../identities/util");
|
|
6
|
+
var util_3 = require("../projects/util");
|
|
6
7
|
var models_1 = require("./models");
|
|
7
8
|
function buildEnvironmentModel(environmentJSON) {
|
|
8
|
-
var project = (0,
|
|
9
|
+
var project = (0, util_3.buildProjectModel)(environmentJSON.project);
|
|
9
10
|
var featureStates = environmentJSON.feature_states.map(function (fs) {
|
|
10
11
|
return (0, util_1.buildFeatureStateModel)(fs);
|
|
11
12
|
});
|
|
12
13
|
var environmentModel = new models_1.EnvironmentModel(environmentJSON.id, environmentJSON.api_key, project);
|
|
13
14
|
environmentModel.featureStates = featureStates;
|
|
15
|
+
if (!!environmentJSON.identity_overrides) {
|
|
16
|
+
environmentModel.identityOverrides = environmentJSON.identity_overrides.map(function (identityData) {
|
|
17
|
+
return (0, util_2.buildIdentityModel)(identityData);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
14
20
|
return environmentModel;
|
|
15
21
|
}
|
|
16
22
|
exports.buildEnvironmentModel = buildEnvironmentModel;
|
|
@@ -7,14 +7,14 @@ function buildFeatureModel(featuresModelJSON) {
|
|
|
7
7
|
}
|
|
8
8
|
exports.buildFeatureModel = buildFeatureModel;
|
|
9
9
|
function buildFeatureStateModel(featuresStateModelJSON) {
|
|
10
|
-
var featureStateModel = new models_1.FeatureStateModel(buildFeatureModel(featuresStateModelJSON.feature), featuresStateModelJSON.enabled, featuresStateModelJSON.django_id, featuresStateModelJSON.feature_state_value, featuresStateModelJSON.
|
|
10
|
+
var featureStateModel = new models_1.FeatureStateModel(buildFeatureModel(featuresStateModelJSON.feature), featuresStateModelJSON.enabled, featuresStateModelJSON.django_id, featuresStateModelJSON.feature_state_value, featuresStateModelJSON.featurestate_uuid);
|
|
11
11
|
featureStateModel.featureSegment = featuresStateModelJSON.feature_segment ?
|
|
12
12
|
buildFeatureSegment(featuresStateModelJSON.feature_segment) :
|
|
13
13
|
undefined;
|
|
14
14
|
var multivariateFeatureStateValues = featuresStateModelJSON.multivariate_feature_state_values
|
|
15
15
|
? featuresStateModelJSON.multivariate_feature_state_values.map(function (fsv) {
|
|
16
16
|
var featureOption = new models_1.MultivariateFeatureOptionModel(fsv.multivariate_feature_option.value, fsv.multivariate_feature_option.id);
|
|
17
|
-
return new models_1.MultivariateFeatureStateValueModel(featureOption, fsv.percentage_allocation, fsv.id);
|
|
17
|
+
return new models_1.MultivariateFeatureStateValueModel(featureOption, fsv.percentage_allocation, fsv.id, fsv.mv_fs_value_uuid);
|
|
18
18
|
})
|
|
19
19
|
: [];
|
|
20
20
|
featureStateModel.multivariateFeatureStateValues = multivariateFeatureStateValues;
|
|
@@ -3,7 +3,6 @@ import { FeatureStateModel } from './features/models';
|
|
|
3
3
|
import { IdentityModel } from './identities/models';
|
|
4
4
|
import { TraitModel } from './identities/traits/models';
|
|
5
5
|
export { EnvironmentModel } from './environments/models';
|
|
6
|
-
export { IntegrationModel } from './environments/integrations/models';
|
|
7
6
|
export { FeatureStateModel } from './features/models';
|
|
8
7
|
export { IdentityModel } from './identities/models';
|
|
9
8
|
export { TraitModel } from './identities/traits/models';
|
|
@@ -11,23 +11,21 @@ var __values = (this && this.__values) || function(o) {
|
|
|
11
11
|
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
12
12
|
};
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.getEnvironmentFeatureStates = exports.getEnvironmentFeatureState = exports.getIdentityFeatureStates = exports.getIdentityFeatureState = exports.OrganisationModel = exports.SegmentModel = exports.TraitModel = exports.IdentityModel = exports.FeatureStateModel = exports.
|
|
14
|
+
exports.getEnvironmentFeatureStates = exports.getEnvironmentFeatureState = exports.getIdentityFeatureStates = exports.getIdentityFeatureState = exports.OrganisationModel = exports.SegmentModel = exports.TraitModel = exports.IdentityModel = exports.FeatureStateModel = exports.EnvironmentModel = void 0;
|
|
15
15
|
var evaluators_1 = require("./segments/evaluators");
|
|
16
16
|
var errors_1 = require("./utils/errors");
|
|
17
17
|
var models_1 = require("./environments/models");
|
|
18
18
|
Object.defineProperty(exports, "EnvironmentModel", { enumerable: true, get: function () { return models_1.EnvironmentModel; } });
|
|
19
|
-
var models_2 = require("./
|
|
20
|
-
Object.defineProperty(exports, "
|
|
21
|
-
var models_3 = require("./
|
|
22
|
-
Object.defineProperty(exports, "
|
|
23
|
-
var models_4 = require("./identities/models");
|
|
24
|
-
Object.defineProperty(exports, "
|
|
25
|
-
var models_5 = require("./
|
|
26
|
-
Object.defineProperty(exports, "
|
|
27
|
-
var models_6 = require("./
|
|
28
|
-
Object.defineProperty(exports, "
|
|
29
|
-
var models_7 = require("./organisations/models");
|
|
30
|
-
Object.defineProperty(exports, "OrganisationModel", { enumerable: true, get: function () { return models_7.OrganisationModel; } });
|
|
19
|
+
var models_2 = require("./features/models");
|
|
20
|
+
Object.defineProperty(exports, "FeatureStateModel", { enumerable: true, get: function () { return models_2.FeatureStateModel; } });
|
|
21
|
+
var models_3 = require("./identities/models");
|
|
22
|
+
Object.defineProperty(exports, "IdentityModel", { enumerable: true, get: function () { return models_3.IdentityModel; } });
|
|
23
|
+
var models_4 = require("./identities/traits/models");
|
|
24
|
+
Object.defineProperty(exports, "TraitModel", { enumerable: true, get: function () { return models_4.TraitModel; } });
|
|
25
|
+
var models_5 = require("./segments/models");
|
|
26
|
+
Object.defineProperty(exports, "SegmentModel", { enumerable: true, get: function () { return models_5.SegmentModel; } });
|
|
27
|
+
var models_6 = require("./organisations/models");
|
|
28
|
+
Object.defineProperty(exports, "OrganisationModel", { enumerable: true, get: function () { return models_6.OrganisationModel; } });
|
|
31
29
|
function getIdentityFeatureStatesDict(environment, identity, overrideTraits) {
|
|
32
30
|
var e_1, _a, e_2, _b, e_3, _c, e_4, _d;
|
|
33
31
|
// Get feature states from the environment
|
package/build/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
export { AnalyticsProcessor, FlagsmithAPIError, FlagsmithClientError, EnvironmentDataPollingManager, FlagsmithCache, DefaultFlag, Flags, default } from './sdk';
|
|
2
|
-
export {
|
|
2
|
+
export { FlagsmithConfig } from './sdk/types';
|
|
3
|
+
export { EnvironmentModel, FeatureStateModel, IdentityModel, TraitModel, SegmentModel, OrganisationModel } from './flagsmith-engine';
|
package/build/index.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.OrganisationModel = exports.SegmentModel = exports.TraitModel = exports.IdentityModel = exports.FeatureStateModel = exports.
|
|
6
|
+
exports.OrganisationModel = exports.SegmentModel = exports.TraitModel = exports.IdentityModel = exports.FeatureStateModel = exports.EnvironmentModel = exports.default = exports.Flags = exports.DefaultFlag = exports.EnvironmentDataPollingManager = exports.FlagsmithClientError = exports.FlagsmithAPIError = exports.AnalyticsProcessor = void 0;
|
|
7
7
|
var sdk_1 = __importDefault(require("./sdk"));
|
|
8
8
|
var sdk_2 = require("./sdk");
|
|
9
9
|
Object.defineProperty(exports, "AnalyticsProcessor", { enumerable: true, get: function () { return sdk_2.AnalyticsProcessor; } });
|
|
@@ -15,7 +15,6 @@ Object.defineProperty(exports, "Flags", { enumerable: true, get: function () { r
|
|
|
15
15
|
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(sdk_2).default; } });
|
|
16
16
|
var flagsmith_engine_1 = require("./flagsmith-engine");
|
|
17
17
|
Object.defineProperty(exports, "EnvironmentModel", { enumerable: true, get: function () { return flagsmith_engine_1.EnvironmentModel; } });
|
|
18
|
-
Object.defineProperty(exports, "IntegrationModel", { enumerable: true, get: function () { return flagsmith_engine_1.IntegrationModel; } });
|
|
19
18
|
Object.defineProperty(exports, "FeatureStateModel", { enumerable: true, get: function () { return flagsmith_engine_1.FeatureStateModel; } });
|
|
20
19
|
Object.defineProperty(exports, "IdentityModel", { enumerable: true, get: function () { return flagsmith_engine_1.IdentityModel; } });
|
|
21
20
|
Object.defineProperty(exports, "TraitModel", { enumerable: true, get: function () { return flagsmith_engine_1.TraitModel; } });
|
package/build/sdk/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RequestInit } from 'node-fetch';
|
|
2
2
|
import { EnvironmentModel } from '../flagsmith-engine/environments/models';
|
|
3
|
+
import { IdentityModel } from '../flagsmith-engine/identities/models';
|
|
3
4
|
import { BaseOfflineHandler } from './offline_handlers';
|
|
4
5
|
import { DefaultFlag, Flags } from './models';
|
|
5
6
|
import { EnvironmentDataPollingManager } from './polling_manager';
|
|
@@ -30,6 +31,7 @@ export declare class Flagsmith {
|
|
|
30
31
|
environment: EnvironmentModel;
|
|
31
32
|
offlineMode: boolean;
|
|
32
33
|
offlineHandler?: BaseOfflineHandler;
|
|
34
|
+
identitiesWithOverridesByIdentifier?: Map<string, IdentityModel>;
|
|
33
35
|
private cache?;
|
|
34
36
|
private onEnvironmentChange?;
|
|
35
37
|
private analyticsProcessor?;
|
|
@@ -123,6 +125,6 @@ export declare class Flagsmith {
|
|
|
123
125
|
private getIdentityFlagsFromDocument;
|
|
124
126
|
private getEnvironmentFlagsFromApi;
|
|
125
127
|
private getIdentityFlagsFromApi;
|
|
126
|
-
private
|
|
128
|
+
private getIdentityModel;
|
|
127
129
|
}
|
|
128
130
|
export default Flagsmith;
|
package/build/sdk/index.js
CHANGED
|
@@ -307,7 +307,7 @@ var Flagsmith = /** @class */ (function () {
|
|
|
307
307
|
if (this.enableLocalEvaluation) {
|
|
308
308
|
return new Promise(function (resolve, reject) {
|
|
309
309
|
return _this.environmentPromise.then(function () {
|
|
310
|
-
var identityModel = _this.
|
|
310
|
+
var identityModel = _this.getIdentityModel(identifier, Object.keys(traits || {}).map(function (key) { return ({
|
|
311
311
|
key: key,
|
|
312
312
|
value: traits === null || traits === void 0 ? void 0 : traits[key]
|
|
313
313
|
}); }));
|
|
@@ -325,13 +325,14 @@ var Flagsmith = /** @class */ (function () {
|
|
|
325
325
|
* You only need to call this if you wish to bypass environmentRefreshIntervalSeconds.
|
|
326
326
|
*/
|
|
327
327
|
Flagsmith.prototype.updateEnvironment = function () {
|
|
328
|
+
var _a;
|
|
328
329
|
return __awaiter(this, void 0, void 0, function () {
|
|
329
|
-
var request,
|
|
330
|
+
var request, _b, e_1;
|
|
330
331
|
var _this = this;
|
|
331
|
-
return __generator(this, function (
|
|
332
|
-
switch (
|
|
332
|
+
return __generator(this, function (_c) {
|
|
333
|
+
switch (_c.label) {
|
|
333
334
|
case 0:
|
|
334
|
-
|
|
335
|
+
_c.trys.push([0, 5, , 6]);
|
|
335
336
|
request = this.getEnvironmentFromApi();
|
|
336
337
|
if (!!this.environmentPromise) return [3 /*break*/, 2];
|
|
337
338
|
this.environmentPromise = request.then(function (res) {
|
|
@@ -339,21 +340,24 @@ var Flagsmith = /** @class */ (function () {
|
|
|
339
340
|
});
|
|
340
341
|
return [4 /*yield*/, this.environmentPromise];
|
|
341
342
|
case 1:
|
|
342
|
-
|
|
343
|
+
_c.sent();
|
|
343
344
|
return [3 /*break*/, 4];
|
|
344
345
|
case 2:
|
|
345
|
-
|
|
346
|
+
_b = this;
|
|
346
347
|
return [4 /*yield*/, request];
|
|
347
348
|
case 3:
|
|
348
|
-
|
|
349
|
-
|
|
349
|
+
_b.environment = _c.sent();
|
|
350
|
+
_c.label = 4;
|
|
350
351
|
case 4:
|
|
352
|
+
if ((_a = this.environment.identityOverrides) === null || _a === void 0 ? void 0 : _a.length) {
|
|
353
|
+
this.identitiesWithOverridesByIdentifier = new Map(this.environment.identityOverrides.map(function (identity) { return [identity.identifier, identity]; }));
|
|
354
|
+
}
|
|
351
355
|
if (this.onEnvironmentChange) {
|
|
352
356
|
this.onEnvironmentChange(null, this.environment);
|
|
353
357
|
}
|
|
354
358
|
return [3 /*break*/, 6];
|
|
355
359
|
case 5:
|
|
356
|
-
e_1 =
|
|
360
|
+
e_1 = _c.sent();
|
|
357
361
|
if (this.onEnvironmentChange) {
|
|
358
362
|
this.onEnvironmentChange(e_1, this.environment);
|
|
359
363
|
}
|
|
@@ -460,7 +464,7 @@ var Flagsmith = /** @class */ (function () {
|
|
|
460
464
|
return __generator(this, function (_a) {
|
|
461
465
|
switch (_a.label) {
|
|
462
466
|
case 0:
|
|
463
|
-
identityModel = this.
|
|
467
|
+
identityModel = this.getIdentityModel(identifier, Object.keys(traits).map(function (key) { return ({
|
|
464
468
|
key: key,
|
|
465
469
|
value: traits[key]
|
|
466
470
|
}); }));
|
|
@@ -574,8 +578,14 @@ var Flagsmith = /** @class */ (function () {
|
|
|
574
578
|
});
|
|
575
579
|
});
|
|
576
580
|
};
|
|
577
|
-
Flagsmith.prototype.
|
|
581
|
+
Flagsmith.prototype.getIdentityModel = function (identifier, traits) {
|
|
582
|
+
var _a;
|
|
578
583
|
var traitModels = traits.map(function (trait) { return new models_2.TraitModel(trait.key, trait.value); });
|
|
584
|
+
var identityWithOverrides = (_a = this.identitiesWithOverridesByIdentifier) === null || _a === void 0 ? void 0 : _a.get(identifier);
|
|
585
|
+
if (identityWithOverrides) {
|
|
586
|
+
identityWithOverrides.updateTraits(traitModels);
|
|
587
|
+
return identityWithOverrides;
|
|
588
|
+
}
|
|
579
589
|
return new models_1.IdentityModel('0', traitModels, [], this.environment.apiKey, identifier);
|
|
580
590
|
};
|
|
581
591
|
return Flagsmith;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FeatureStateModel } from '../features/models';
|
|
2
|
+
import { IdentityModel } from '../identities/models';
|
|
2
3
|
import { ProjectModel } from '../projects/models';
|
|
3
|
-
import { IntegrationModel } from './integrations/models';
|
|
4
4
|
|
|
5
5
|
export class EnvironmentAPIKeyModel {
|
|
6
6
|
id: number;
|
|
@@ -37,10 +37,7 @@ export class EnvironmentModel {
|
|
|
37
37
|
apiKey: string;
|
|
38
38
|
project: ProjectModel;
|
|
39
39
|
featureStates: FeatureStateModel[] = [];
|
|
40
|
-
|
|
41
|
-
segment_config?: IntegrationModel;
|
|
42
|
-
mixpanel_config?: IntegrationModel;
|
|
43
|
-
heap_config?: IntegrationModel;
|
|
40
|
+
identityOverrides: IdentityModel[] = [];
|
|
44
41
|
|
|
45
42
|
constructor(id: number, apiKey: string, project: ProjectModel) {
|
|
46
43
|
this.id = id;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { buildFeatureStateModel } from '../features/util';
|
|
2
|
+
import { buildIdentityModel } from '../identities/util';
|
|
2
3
|
import { buildProjectModel } from '../projects/util';
|
|
3
4
|
import { EnvironmentAPIKeyModel, EnvironmentModel } from './models';
|
|
4
5
|
|
|
@@ -13,6 +14,11 @@ export function buildEnvironmentModel(environmentJSON: any) {
|
|
|
13
14
|
project
|
|
14
15
|
);
|
|
15
16
|
environmentModel.featureStates = featureStates;
|
|
17
|
+
if (!!environmentJSON.identity_overrides) {
|
|
18
|
+
environmentModel.identityOverrides = environmentJSON.identity_overrides.map((identityData: any) =>
|
|
19
|
+
buildIdentityModel(identityData)
|
|
20
|
+
);
|
|
21
|
+
}
|
|
16
22
|
return environmentModel;
|
|
17
23
|
}
|
|
18
24
|
|
|
@@ -16,25 +16,26 @@ export function buildFeatureStateModel(featuresStateModelJSON: any): FeatureStat
|
|
|
16
16
|
featuresStateModelJSON.enabled,
|
|
17
17
|
featuresStateModelJSON.django_id,
|
|
18
18
|
featuresStateModelJSON.feature_state_value,
|
|
19
|
-
featuresStateModelJSON.
|
|
19
|
+
featuresStateModelJSON.featurestate_uuid
|
|
20
20
|
);
|
|
21
21
|
|
|
22
|
-
featureStateModel.featureSegment = featuresStateModelJSON.feature_segment ?
|
|
23
|
-
buildFeatureSegment(featuresStateModelJSON.feature_segment) :
|
|
22
|
+
featureStateModel.featureSegment = featuresStateModelJSON.feature_segment ?
|
|
23
|
+
buildFeatureSegment(featuresStateModelJSON.feature_segment) :
|
|
24
24
|
undefined;
|
|
25
25
|
|
|
26
26
|
const multivariateFeatureStateValues = featuresStateModelJSON.multivariate_feature_state_values
|
|
27
27
|
? featuresStateModelJSON.multivariate_feature_state_values.map((fsv: any) => {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
28
|
+
const featureOption = new MultivariateFeatureOptionModel(
|
|
29
|
+
fsv.multivariate_feature_option.value,
|
|
30
|
+
fsv.multivariate_feature_option.id
|
|
31
|
+
);
|
|
32
|
+
return new MultivariateFeatureStateValueModel(
|
|
33
|
+
featureOption,
|
|
34
|
+
fsv.percentage_allocation,
|
|
35
|
+
fsv.id,
|
|
36
|
+
fsv.mv_fs_value_uuid
|
|
37
|
+
);
|
|
38
|
+
})
|
|
38
39
|
: [];
|
|
39
40
|
|
|
40
41
|
featureStateModel.multivariateFeatureStateValues = multivariateFeatureStateValues;
|
|
@@ -7,7 +7,6 @@ import { SegmentModel } from './segments/models';
|
|
|
7
7
|
import { FeatureStateNotFound } from './utils/errors';
|
|
8
8
|
|
|
9
9
|
export { EnvironmentModel } from './environments/models';
|
|
10
|
-
export { IntegrationModel } from './environments/integrations/models';
|
|
11
10
|
export { FeatureStateModel } from './features/models';
|
|
12
11
|
export { IdentityModel } from './identities/models';
|
|
13
12
|
export { TraitModel } from './identities/traits/models';
|
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flagsmith-nodejs",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.0",
|
|
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": {
|
package/sdk/index.ts
CHANGED
|
@@ -49,6 +49,8 @@ export class Flagsmith {
|
|
|
49
49
|
offlineMode: boolean = false;
|
|
50
50
|
offlineHandler?: BaseOfflineHandler = undefined;
|
|
51
51
|
|
|
52
|
+
identitiesWithOverridesByIdentifier?: Map<string, IdentityModel>;
|
|
53
|
+
|
|
52
54
|
private cache?: FlagsmithCache;
|
|
53
55
|
private onEnvironmentChange?: (error: Error | null, result: EnvironmentModel) => void;
|
|
54
56
|
private analyticsProcessor?: AnalyticsProcessor;
|
|
@@ -143,13 +145,13 @@ export class Flagsmith {
|
|
|
143
145
|
if (!this.environmentKey) {
|
|
144
146
|
throw new Error('ValueError: environmentKey is required.');
|
|
145
147
|
}
|
|
146
|
-
|
|
148
|
+
|
|
147
149
|
const apiUrl = data.apiUrl || DEFAULT_API_URL;
|
|
148
150
|
this.apiUrl = apiUrl.endsWith('/') ? apiUrl : `${apiUrl}/`;
|
|
149
151
|
this.environmentFlagsUrl = `${this.apiUrl}flags/`;
|
|
150
152
|
this.identitiesUrl = `${this.apiUrl}identities/`;
|
|
151
153
|
this.environmentUrl = `${this.apiUrl}environment-document/`;
|
|
152
|
-
|
|
154
|
+
|
|
153
155
|
if (this.enableLocalEvaluation) {
|
|
154
156
|
if (!this.environmentKey.startsWith('ser.')) {
|
|
155
157
|
console.error(
|
|
@@ -166,11 +168,11 @@ export class Flagsmith {
|
|
|
166
168
|
|
|
167
169
|
this.analyticsProcessor = data.enableAnalytics
|
|
168
170
|
? new AnalyticsProcessor({
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
171
|
+
environmentKey: this.environmentKey,
|
|
172
|
+
baseApiUrl: this.apiUrl,
|
|
173
|
+
requestTimeoutMs: this.requestTimeoutMs,
|
|
174
|
+
logger: this.logger
|
|
175
|
+
})
|
|
174
176
|
: undefined;
|
|
175
177
|
}
|
|
176
178
|
}
|
|
@@ -256,7 +258,7 @@ export class Flagsmith {
|
|
|
256
258
|
if (this.enableLocalEvaluation) {
|
|
257
259
|
return new Promise((resolve, reject) => {
|
|
258
260
|
return this.environmentPromise!.then(() => {
|
|
259
|
-
const identityModel = this.
|
|
261
|
+
const identityModel = this.getIdentityModel(
|
|
260
262
|
identifier,
|
|
261
263
|
Object.keys(traits || {}).map(key => ({
|
|
262
264
|
key,
|
|
@@ -289,6 +291,11 @@ export class Flagsmith {
|
|
|
289
291
|
} else {
|
|
290
292
|
this.environment = await request;
|
|
291
293
|
}
|
|
294
|
+
if (this.environment.identityOverrides?.length) {
|
|
295
|
+
this.identitiesWithOverridesByIdentifier = new Map<string, IdentityModel>(
|
|
296
|
+
this.environment.identityOverrides.map(identity => [identity.identifier, identity]
|
|
297
|
+
));
|
|
298
|
+
}
|
|
292
299
|
if (this.onEnvironmentChange) {
|
|
293
300
|
this.onEnvironmentChange(null, this.environment);
|
|
294
301
|
}
|
|
@@ -370,7 +377,7 @@ export class Flagsmith {
|
|
|
370
377
|
identifier: string,
|
|
371
378
|
traits: { [key: string]: any }
|
|
372
379
|
): Promise<Flags> {
|
|
373
|
-
const identityModel = this.
|
|
380
|
+
const identityModel = this.getIdentityModel(
|
|
374
381
|
identifier,
|
|
375
382
|
Object.keys(traits).map(key => ({
|
|
376
383
|
key,
|
|
@@ -458,8 +465,13 @@ export class Flagsmith {
|
|
|
458
465
|
}
|
|
459
466
|
}
|
|
460
467
|
|
|
461
|
-
private
|
|
468
|
+
private getIdentityModel(identifier: string, traits: { key: string; value: any }[]) {
|
|
462
469
|
const traitModels = traits.map(trait => new TraitModel(trait.key, trait.value));
|
|
470
|
+
let identityWithOverrides = this.identitiesWithOverridesByIdentifier?.get(identifier);
|
|
471
|
+
if (identityWithOverrides) {
|
|
472
|
+
identityWithOverrides.updateTraits(traitModels);
|
|
473
|
+
return identityWithOverrides;
|
|
474
|
+
}
|
|
463
475
|
return new IdentityModel('0', traitModels, [], this.environment.apiKey, identifier);
|
|
464
476
|
}
|
|
465
477
|
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"feature_states": [
|
|
18
18
|
{
|
|
19
19
|
"feature_state_value": "segment_override",
|
|
20
|
+
"featurestate_uuid": "dd77a1ab-08cf-4743-8a3b-19e730444a14",
|
|
20
21
|
"multivariate_feature_state_values": [],
|
|
21
22
|
"django_id": 81027,
|
|
22
23
|
"feature": {
|
|
@@ -88,5 +89,30 @@
|
|
|
88
89
|
"featurestate_uuid": "96fc3503-09d7-48f1-a83b-2dc903d5c08a",
|
|
89
90
|
"enabled": false
|
|
90
91
|
}
|
|
92
|
+
],
|
|
93
|
+
"identity_overrides": [
|
|
94
|
+
{
|
|
95
|
+
"identifier": "overridden-id",
|
|
96
|
+
"identity_uuid": "0f21cde8-63c5-4e50-baca-87897fa6cd01",
|
|
97
|
+
"created_date": "2019-08-27T14:53:45.698555Z",
|
|
98
|
+
"updated_at": "2023-07-14 16:12:00.000000",
|
|
99
|
+
"environment_api_key": "B62qaMZNwfiqT76p38ggrQ",
|
|
100
|
+
"identity_features": [
|
|
101
|
+
{
|
|
102
|
+
"id": 1,
|
|
103
|
+
"feature": {
|
|
104
|
+
"id": 1,
|
|
105
|
+
"name": "some_feature",
|
|
106
|
+
"type": "STANDARD"
|
|
107
|
+
},
|
|
108
|
+
"featurestate_uuid": "1bddb9a5-7e59-42c6-9be9-625fa369749f",
|
|
109
|
+
"feature_state_value": "some-overridden-value",
|
|
110
|
+
"enabled": false,
|
|
111
|
+
"environment": 1,
|
|
112
|
+
"identity": null,
|
|
113
|
+
"feature_segment": null
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
91
117
|
]
|
|
92
|
-
}
|
|
118
|
+
}
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import Flagsmith from '../../sdk';
|
|
2
2
|
import { EnvironmentDataPollingManager } from '../../sdk/polling_manager';
|
|
3
|
-
import fetch, {RequestInit} from 'node-fetch';
|
|
3
|
+
import fetch, { RequestInit } from 'node-fetch';
|
|
4
4
|
import { environmentJSON, environmentModel, flagsJSON, flagsmith, identitiesJSON } from './utils';
|
|
5
5
|
import { DefaultFlag, Flags } from '../../sdk/models';
|
|
6
|
-
import {delay
|
|
7
|
-
import * as utils from '../../sdk/utils';
|
|
6
|
+
import { delay } from '../../sdk/utils';
|
|
8
7
|
import { EnvironmentModel } from '../../flagsmith-engine/environments/models';
|
|
9
8
|
import https from 'https'
|
|
10
9
|
import { BaseOfflineHandler } from '../../sdk/offline_handlers';
|
|
@@ -48,9 +47,6 @@ test('test_update_environment_sets_environment', async () => {
|
|
|
48
47
|
|
|
49
48
|
const model = environmentModel(JSON.parse(environmentJSON()));
|
|
50
49
|
|
|
51
|
-
wipeFeatureStateUUIDs(flg.environment)
|
|
52
|
-
wipeFeatureStateUUIDs(model)
|
|
53
|
-
|
|
54
50
|
expect(flg.environment).toStrictEqual(model);
|
|
55
51
|
});
|
|
56
52
|
|
|
@@ -58,8 +54,8 @@ test('test_set_agent_options', async () => {
|
|
|
58
54
|
const agent = new https.Agent({})
|
|
59
55
|
|
|
60
56
|
// @ts-ignore
|
|
61
|
-
fetch.mockImplementation((url:string, options:RequestInit)=>{
|
|
62
|
-
if(options.agent!==agent) {
|
|
57
|
+
fetch.mockImplementation((url: string, options: RequestInit) => {
|
|
58
|
+
if (options.agent !== agent) {
|
|
63
59
|
throw new Error("Agent has not been set on retry fetch")
|
|
64
60
|
}
|
|
65
61
|
return Promise.resolve(new Response(environmentJSON()))
|
|
@@ -276,7 +272,7 @@ test('getIdentitySegments throws error if identifier is empty string', () => {
|
|
|
276
272
|
})
|
|
277
273
|
|
|
278
274
|
|
|
279
|
-
test('offline_mode', async() => {
|
|
275
|
+
test('offline_mode', async () => {
|
|
280
276
|
// Given
|
|
281
277
|
const environment: EnvironmentModel = environmentModel(JSON.parse(environmentJSON('offline-environment.json')));
|
|
282
278
|
|
|
@@ -311,19 +307,19 @@ test('test_flagsmith_uses_offline_handler_if_set_and_no_api_response', async ()
|
|
|
311
307
|
const environment: EnvironmentModel = environmentModel(JSON.parse(environmentJSON('offline-environment.json')));
|
|
312
308
|
const api_url = 'http://some.flagsmith.com/api/v1/';
|
|
313
309
|
const mock_offline_handler = new BaseOfflineHandler() as jest.Mocked<BaseOfflineHandler>;
|
|
314
|
-
|
|
310
|
+
|
|
315
311
|
jest.spyOn(mock_offline_handler, 'getEnvironment').mockReturnValue(environment);
|
|
316
312
|
|
|
317
313
|
const flagsmith = new Flagsmith({
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
314
|
+
environmentKey: 'some-key',
|
|
315
|
+
apiUrl: api_url,
|
|
316
|
+
offlineHandler: mock_offline_handler,
|
|
321
317
|
});
|
|
322
318
|
|
|
323
319
|
jest.spyOn(flagsmith, 'getEnvironmentFlags');
|
|
324
320
|
jest.spyOn(flagsmith, 'getIdentityFlags');
|
|
325
321
|
|
|
326
|
-
|
|
322
|
+
|
|
327
323
|
flagsmith.environmentFlagsUrl = 'http://some.flagsmith.com/api/v1/environment-flags';
|
|
328
324
|
flagsmith.identitiesUrl = 'http://some.flagsmith.com/api/v1/identities';
|
|
329
325
|
|
|
@@ -337,17 +333,17 @@ test('test_flagsmith_uses_offline_handler_if_set_and_no_api_response', async ()
|
|
|
337
333
|
fetch.mockReturnValue(Promise.resolve(errorResponse));
|
|
338
334
|
|
|
339
335
|
// When
|
|
340
|
-
const environmentFlags:Flags = await flagsmith.getEnvironmentFlags();
|
|
341
|
-
const identityFlags:Flags = await flagsmith.getIdentityFlags('identity', {});
|
|
336
|
+
const environmentFlags: Flags = await flagsmith.getEnvironmentFlags();
|
|
337
|
+
const identityFlags: Flags = await flagsmith.getIdentityFlags('identity', {});
|
|
342
338
|
|
|
343
339
|
// Then
|
|
344
340
|
expect(mock_offline_handler.getEnvironment).toHaveBeenCalledTimes(1);
|
|
345
341
|
expect(flagsmith.getEnvironmentFlags).toHaveBeenCalled();
|
|
346
342
|
expect(flagsmith.getIdentityFlags).toHaveBeenCalled();
|
|
347
|
-
|
|
343
|
+
|
|
348
344
|
expect(environmentFlags.isFeatureEnabled('some_feature')).toBe(true);
|
|
349
345
|
expect(environmentFlags.getFeatureValue('some_feature')).toBe('offline-value');
|
|
350
|
-
|
|
346
|
+
|
|
351
347
|
expect(identityFlags.isFeatureEnabled('some_feature')).toBe(true);
|
|
352
348
|
expect(identityFlags.getFeatureValue('some_feature')).toBe('offline-value');
|
|
353
349
|
});
|
|
@@ -355,18 +351,18 @@ test('test_flagsmith_uses_offline_handler_if_set_and_no_api_response', async ()
|
|
|
355
351
|
test('cannot use offline mode without offline handler', () => {
|
|
356
352
|
// When and Then
|
|
357
353
|
expect(() => new Flagsmith({ offlineMode: true, offlineHandler: undefined })).toThrowError(
|
|
358
|
-
|
|
354
|
+
'ValueError: offlineHandler must be provided to use offline mode.'
|
|
359
355
|
);
|
|
360
356
|
});
|
|
361
|
-
|
|
357
|
+
|
|
362
358
|
test('cannot use both default handler and offline handler', () => {
|
|
363
359
|
// When and Then
|
|
364
360
|
expect(() => new Flagsmith({
|
|
365
|
-
|
|
366
|
-
|
|
361
|
+
offlineHandler: new BaseOfflineHandler(),
|
|
362
|
+
defaultFlagHandler: (flagName) => new DefaultFlag('foo', true)
|
|
367
363
|
})).toThrowError('ValueError: Cannot use both defaultFlagHandler and offlineHandler.');
|
|
368
364
|
});
|
|
369
|
-
|
|
365
|
+
|
|
370
366
|
test('cannot create Flagsmith client in remote evaluation without API key', () => {
|
|
371
367
|
// When and Then
|
|
372
368
|
// @ts-ignore
|
|
@@ -374,27 +370,20 @@ test('cannot create Flagsmith client in remote evaluation without API key', () =
|
|
|
374
370
|
});
|
|
375
371
|
|
|
376
372
|
|
|
377
|
-
async function wipeFeatureStateUUIDs (environmentModel: EnvironmentModel) {
|
|
378
|
-
// TODO: this has been pulled out of tests above as a helper function.
|
|
379
|
-
// I'm not entirely sure why it's necessary, however, we should look to remove.
|
|
380
|
-
environmentModel.featureStates.forEach(fs => {
|
|
381
|
-
// @ts-ignore
|
|
382
|
-
fs.featurestateUUID = undefined;
|
|
383
|
-
fs.multivariateFeatureStateValues.forEach(mvfsv => {
|
|
384
|
-
// @ts-ignore
|
|
385
|
-
mvfsv.mvFsValueUuid = undefined;
|
|
386
|
-
})
|
|
387
|
-
});
|
|
388
|
-
environmentModel.project.segments.forEach(s => {
|
|
389
|
-
s.featureStates.forEach(fs => {
|
|
390
|
-
// @ts-ignore
|
|
391
|
-
fs.featurestateUUID = undefined;
|
|
392
|
-
})
|
|
393
|
-
})
|
|
394
|
-
}
|
|
395
|
-
|
|
396
373
|
function sleep(ms: number) {
|
|
397
374
|
return new Promise((resolve) => {
|
|
398
375
|
setTimeout(resolve, ms);
|
|
399
376
|
});
|
|
400
377
|
}
|
|
378
|
+
test('test_localEvaluation_true__identity_overrides_evaluated', async () => {
|
|
379
|
+
// @ts-ignore
|
|
380
|
+
fetch.mockReturnValue(Promise.resolve(new Response(environmentJSON())));
|
|
381
|
+
|
|
382
|
+
const flg = new Flagsmith({
|
|
383
|
+
environmentKey: 'ser.key',
|
|
384
|
+
enableLocalEvaluation: true,
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
const flags = await flg.getIdentityFlags("overridden-id");
|
|
388
|
+
expect(flags.getFeatureValue("some_feature")).toEqual("some-overridden-value");
|
|
389
|
+
});
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.IntegrationModel = void 0;
|
|
4
|
-
var IntegrationModel = /** @class */ (function () {
|
|
5
|
-
function IntegrationModel() {
|
|
6
|
-
this.api_key = undefined;
|
|
7
|
-
this.base_url = undefined;
|
|
8
|
-
}
|
|
9
|
-
return IntegrationModel;
|
|
10
|
-
}());
|
|
11
|
-
exports.IntegrationModel = IntegrationModel;
|
package/examples/README.md
DELETED