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 CHANGED
@@ -2,5 +2,5 @@
2
2
  . "$(dirname "$0")/_/husky.sh"
3
3
 
4
4
  npm run lint
5
- git add ./flagsmith-engine ./sdk ./tests ./example ./index.ts ./.github
5
+ git add ./flagsmith-engine ./sdk ./tests ./index.ts ./.github
6
6
  npm run test
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
- amplitude_config?: IntegrationModel;
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("../projects/util");
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, util_2.buildProjectModel)(environmentJSON.project);
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.uuid);
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.IntegrationModel = exports.EnvironmentModel = void 0;
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("./environments/integrations/models");
20
- Object.defineProperty(exports, "IntegrationModel", { enumerable: true, get: function () { return models_2.IntegrationModel; } });
21
- var models_3 = require("./features/models");
22
- Object.defineProperty(exports, "FeatureStateModel", { enumerable: true, get: function () { return models_3.FeatureStateModel; } });
23
- var models_4 = require("./identities/models");
24
- Object.defineProperty(exports, "IdentityModel", { enumerable: true, get: function () { return models_4.IdentityModel; } });
25
- var models_5 = require("./identities/traits/models");
26
- Object.defineProperty(exports, "TraitModel", { enumerable: true, get: function () { return models_5.TraitModel; } });
27
- var models_6 = require("./segments/models");
28
- Object.defineProperty(exports, "SegmentModel", { enumerable: true, get: function () { return models_6.SegmentModel; } });
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 { EnvironmentModel, IntegrationModel, FeatureStateModel, IdentityModel, TraitModel, SegmentModel, OrganisationModel } from './flagsmith-engine';
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.IntegrationModel = exports.EnvironmentModel = exports.default = exports.Flags = exports.DefaultFlag = exports.EnvironmentDataPollingManager = exports.FlagsmithClientError = exports.FlagsmithAPIError = exports.AnalyticsProcessor = void 0;
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; } });
@@ -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 buildIdentityModel;
128
+ private getIdentityModel;
127
129
  }
128
130
  export default Flagsmith;
@@ -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.buildIdentityModel(identifier, Object.keys(traits || {}).map(function (key) { return ({
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, _a, e_1;
330
+ var request, _b, e_1;
330
331
  var _this = this;
331
- return __generator(this, function (_b) {
332
- switch (_b.label) {
332
+ return __generator(this, function (_c) {
333
+ switch (_c.label) {
333
334
  case 0:
334
- _b.trys.push([0, 5, , 6]);
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
- _b.sent();
343
+ _c.sent();
343
344
  return [3 /*break*/, 4];
344
345
  case 2:
345
- _a = this;
346
+ _b = this;
346
347
  return [4 /*yield*/, request];
347
348
  case 3:
348
- _a.environment = _b.sent();
349
- _b.label = 4;
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 = _b.sent();
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.buildIdentityModel(identifier, Object.keys(traits).map(function (key) { return ({
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.buildIdentityModel = function (identifier, traits) {
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
- amplitude_config?: IntegrationModel;
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.uuid
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
- 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
- );
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
@@ -11,9 +11,12 @@ export {
11
11
  default
12
12
  } from './sdk';
13
13
 
14
+ export {
15
+ FlagsmithConfig
16
+ } from './sdk/types'
17
+
14
18
  export {
15
19
  EnvironmentModel,
16
- IntegrationModel,
17
20
  FeatureStateModel,
18
21
  IdentityModel,
19
22
  TraitModel,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flagsmith-nodejs",
3
- "version": "3.2.0",
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
- environmentKey: this.environmentKey,
170
- baseApiUrl: this.apiUrl,
171
- requestTimeoutMs: this.requestTimeoutMs,
172
- logger: this.logger
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.buildIdentityModel(
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.buildIdentityModel(
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 buildIdentityModel(identifier: string, traits: { key: string; value: any }[]) {
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, retryFetch} from '../../sdk/utils';
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
- environmentKey: 'some-key',
319
- apiUrl: api_url,
320
- offlineHandler: mock_offline_handler,
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
- 'ValueError: offlineHandler must be provided to use offline mode.'
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
- offlineHandler: new BaseOfflineHandler(),
366
- defaultFlagHandler: (flagName) => new DefaultFlag('foo', true)
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,4 +0,0 @@
1
- export declare class IntegrationModel {
2
- api_key?: string;
3
- base_url?: string;
4
- }
@@ -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;
@@ -1,3 +0,0 @@
1
- # Flagsmith NodeJS Examples
2
-
3
- Check out our [NodeJS Examples repository](https://github.com/Flagsmith/flagsmith-nodejs-examples).
@@ -1,4 +0,0 @@
1
- export class IntegrationModel {
2
- api_key?: string = undefined;
3
- base_url?: string = undefined;
4
- }