flagsmith-nodejs 2.5.2 → 3.0.1
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 +25 -0
- package/build/flagsmith-engine/identities/models.d.ts +1 -1
- package/build/flagsmith-engine/identities/models.js +2 -1
- package/build/flagsmith-engine/segments/evaluators.js +3 -2
- package/examples/README.md +3 -0
- package/flagsmith-engine/identities/models.ts +3 -1
- package/flagsmith-engine/segments/evaluators.ts +4 -3
- package/package.json +1 -1
- package/tests/engine/unit/segments/segment_evaluators.test.ts +46 -6
- package/.vscode/launch.json +0 -18
- package/examples/api-proxy/.babelrc +0 -3
- package/examples/api-proxy/.eslintrc +0 -8
- package/examples/api-proxy/README.md +0 -12
- package/examples/api-proxy/package-lock.json +0 -10889
- package/examples/api-proxy/package.json +0 -57
- package/examples/api-proxy/src/api/index.js +0 -42
- package/examples/api-proxy/src/index.js +0 -29
- package/examples/basic/.babelrc +0 -3
- package/examples/basic/.eslintrc +0 -8
- package/examples/basic/README.md +0 -10
- package/examples/basic/package-lock.json +0 -10779
- package/examples/basic/package.json +0 -56
- package/examples/basic/src/api/index.js +0 -33
- package/examples/basic/src/index.js +0 -29
- package/examples/caching/.babelrc +0 -3
- package/examples/caching/.eslintrc +0 -8
- package/examples/caching/README.md +0 -9
- package/examples/caching/package-lock.json +0 -6750
- package/examples/caching/package.json +0 -56
- package/examples/caching/src/api/index.js +0 -38
- package/examples/caching/src/index.js +0 -29
- package/examples/custom-fetch-agent/.babelrc +0 -3
- package/examples/custom-fetch-agent/.eslintrc +0 -8
- package/examples/custom-fetch-agent/README.md +0 -12
- package/examples/custom-fetch-agent/package-lock.json +0 -6750
- package/examples/custom-fetch-agent/package.json +0 -56
- package/examples/custom-fetch-agent/src/api/index.js +0 -34
- package/examples/custom-fetch-agent/src/index.js +0 -29
- package/examples/local-evaluation/.babelrc +0 -3
- package/examples/local-evaluation/.eslintrc +0 -8
- package/examples/local-evaluation/README.md +0 -18
- package/examples/local-evaluation/package-lock.json +0 -6668
- package/examples/local-evaluation/package.json +0 -56
- package/examples/local-evaluation/src/api/index.js +0 -39
- package/examples/local-evaluation/src/index.js +0 -29
- package/tests/engine/engine-tests/engine-test-data/data/environment_n9fbf9h3v4fFgH3U3ngWhb.json +0 -12591
- package/tests/engine/engine-tests/engine-test-data/readme.md +0 -30
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: Publish NPM Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- '*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
package:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
name: Publish NPM Package
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- name: Cloning repo
|
|
15
|
+
uses: actions/checkout@v3
|
|
16
|
+
|
|
17
|
+
- uses: actions/setup-node@v3
|
|
18
|
+
with:
|
|
19
|
+
node-version: '16.x'
|
|
20
|
+
registry-url: 'https://registry.npmjs.org'
|
|
21
|
+
|
|
22
|
+
- run: npm ci
|
|
23
|
+
- run: npm run deploy
|
|
24
|
+
env:
|
|
25
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
@@ -8,7 +8,7 @@ export declare class IdentityModel {
|
|
|
8
8
|
identityTraits: TraitModel[];
|
|
9
9
|
identityUuid: string;
|
|
10
10
|
djangoID: number | undefined;
|
|
11
|
-
constructor(created_date: string, identityTraits: TraitModel[], identityFeatures: IdentityFeaturesList, environmentApiKey: string, identifier: string, identityUuid?: string);
|
|
11
|
+
constructor(created_date: string, identityTraits: TraitModel[], identityFeatures: IdentityFeaturesList, environmentApiKey: string, identifier: string, identityUuid?: string, djangoID?: number);
|
|
12
12
|
get compositeKey(): string;
|
|
13
13
|
static generateCompositeKey(env_key: string, identifier: string): string;
|
|
14
14
|
updateTraits(traits: TraitModel[]): void;
|
|
@@ -40,13 +40,14 @@ exports.IdentityModel = void 0;
|
|
|
40
40
|
var collections_1 = require("../utils/collections");
|
|
41
41
|
var uuidv4 = require('uuid').v4;
|
|
42
42
|
var IdentityModel = /** @class */ (function () {
|
|
43
|
-
function IdentityModel(created_date, identityTraits, identityFeatures, environmentApiKey, identifier, identityUuid) {
|
|
43
|
+
function IdentityModel(created_date, identityTraits, identityFeatures, environmentApiKey, identifier, identityUuid, djangoID) {
|
|
44
44
|
this.identityUuid = identityUuid || uuidv4();
|
|
45
45
|
this.createdDate = Date.parse(created_date) || Date.now();
|
|
46
46
|
this.identityTraits = identityTraits;
|
|
47
47
|
this.identityFeatures = new (collections_1.IdentityFeaturesList.bind.apply(collections_1.IdentityFeaturesList, __spreadArray([void 0], __read(identityFeatures), false)))();
|
|
48
48
|
this.environmentApiKey = environmentApiKey;
|
|
49
49
|
this.identifier = identifier;
|
|
50
|
+
this.djangoID = djangoID;
|
|
50
51
|
}
|
|
51
52
|
Object.defineProperty(IdentityModel.prototype, "compositeKey", {
|
|
52
53
|
get: function () {
|
|
@@ -12,7 +12,7 @@ exports.getIdentitySegments = getIdentitySegments;
|
|
|
12
12
|
function evaluateIdentityInSegment(identity, segment, overrideTraits) {
|
|
13
13
|
return (segment.rules.length > 0 &&
|
|
14
14
|
segment.rules.filter(function (rule) {
|
|
15
|
-
return traitsMatchSegmentRule(overrideTraits || identity.identityTraits, rule, segment.id, identity.compositeKey);
|
|
15
|
+
return traitsMatchSegmentRule(overrideTraits || identity.identityTraits, rule, segment.id, identity.djangoID || identity.compositeKey);
|
|
16
16
|
}).length === segment.rules.length);
|
|
17
17
|
}
|
|
18
18
|
exports.evaluateIdentityInSegment = evaluateIdentityInSegment;
|
|
@@ -29,7 +29,8 @@ function traitsMatchSegmentRule(identityTraits, rule, segmentId, identityId) {
|
|
|
29
29
|
}
|
|
30
30
|
function traitsMatchSegmentCondition(identityTraits, condition, segmentId, identityId) {
|
|
31
31
|
if (condition.operator == constants_1.PERCENTAGE_SPLIT) {
|
|
32
|
-
|
|
32
|
+
var hashedPercentage = (0, hashing_1.getHashedPercentateForObjIds)([segmentId, identityId]);
|
|
33
|
+
return hashedPercentage <= parseFloat(String(condition.value));
|
|
33
34
|
}
|
|
34
35
|
var traits = identityTraits.filter(function (t) { return t.traitKey === condition.property_; });
|
|
35
36
|
var trait = traits.length > 0 ? traits[0] : undefined;
|
|
@@ -19,7 +19,8 @@ export class IdentityModel {
|
|
|
19
19
|
identityFeatures: IdentityFeaturesList,
|
|
20
20
|
environmentApiKey: string,
|
|
21
21
|
identifier: string,
|
|
22
|
-
identityUuid?: string
|
|
22
|
+
identityUuid?: string,
|
|
23
|
+
djangoID?: number
|
|
23
24
|
) {
|
|
24
25
|
this.identityUuid = identityUuid || uuidv4();
|
|
25
26
|
this.createdDate = Date.parse(created_date) || Date.now();
|
|
@@ -27,6 +28,7 @@ export class IdentityModel {
|
|
|
27
28
|
this.identityFeatures = new IdentityFeaturesList(...identityFeatures);
|
|
28
29
|
this.environmentApiKey = environmentApiKey;
|
|
29
30
|
this.identifier = identifier;
|
|
31
|
+
this.djangoID = djangoID;
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
get compositeKey() {
|
|
@@ -27,7 +27,7 @@ export function evaluateIdentityInSegment(
|
|
|
27
27
|
overrideTraits || identity.identityTraits,
|
|
28
28
|
rule,
|
|
29
29
|
segment.id,
|
|
30
|
-
identity.compositeKey
|
|
30
|
+
identity.djangoID || identity.compositeKey
|
|
31
31
|
)
|
|
32
32
|
).length === segment.rules.length
|
|
33
33
|
);
|
|
@@ -62,7 +62,8 @@ export function traitsMatchSegmentCondition(
|
|
|
62
62
|
identityId: number | string
|
|
63
63
|
): boolean {
|
|
64
64
|
if (condition.operator == PERCENTAGE_SPLIT) {
|
|
65
|
-
|
|
65
|
+
var hashedPercentage = getHashedPercentateForObjIds([segmentId, identityId]);
|
|
66
|
+
return hashedPercentage <= parseFloat(String(condition.value));
|
|
66
67
|
}
|
|
67
68
|
const traits = identityTraits.filter(t => t.traitKey === condition.property_);
|
|
68
69
|
const trait = traits.length > 0 ? traits[0] : undefined;
|
|
@@ -73,4 +74,4 @@ export function traitsMatchSegmentCondition(
|
|
|
73
74
|
}
|
|
74
75
|
return trait ? condition.matchesTraitValue(trait.traitValue) : false;
|
|
75
76
|
|
|
76
|
-
}
|
|
77
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flagsmith-nodejs",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
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": {
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ALL_RULE,
|
|
2
3
|
CONDITION_OPERATORS,
|
|
4
|
+
PERCENTAGE_SPLIT,
|
|
3
5
|
} from '../../../../flagsmith-engine/segments/constants';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
} from
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
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';
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
// todo: work out how to implement this in a test function or before hook
|
|
15
|
+
jest.mock('../../../../flagsmith-engine/utils/hashing', () => ({
|
|
16
|
+
getHashedPercentateForObjIds: jest.fn(() => 1)
|
|
17
|
+
}));
|
|
18
|
+
|
|
9
19
|
|
|
10
20
|
let traitExistenceTestCases: [string, string | null | undefined, string | null | undefined, TraitModel [],boolean][] = [
|
|
11
21
|
[CONDITION_OPERATORS.IS_SET,'foo', null,[] , false],
|
|
@@ -24,4 +34,34 @@ test('test_traits_match_segment_condition_for_trait_existence_operators', () =>
|
|
|
24
34
|
traitsMatchSegmentCondition (traits, segmentModel, 'any','any')
|
|
25
35
|
).toBe(expectedResult);
|
|
26
36
|
}
|
|
27
|
-
});
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
test('evaluateIdentityInSegment uses django ID for hashed percentage when present', () => {
|
|
41
|
+
var identityModel = new IdentityModel(Date.now().toString(), [], [], environment().apiKey, 'identity_1', undefined, 1);
|
|
42
|
+
const segmentDefinition = {
|
|
43
|
+
id: 1,
|
|
44
|
+
name: 'percentage_split_segment',
|
|
45
|
+
rules: [
|
|
46
|
+
{
|
|
47
|
+
type: ALL_RULE,
|
|
48
|
+
conditions: [
|
|
49
|
+
{
|
|
50
|
+
operator: PERCENTAGE_SPLIT,
|
|
51
|
+
property_: null,
|
|
52
|
+
value: "10"
|
|
53
|
+
}
|
|
54
|
+
],
|
|
55
|
+
rules: []
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
feature_states: []
|
|
59
|
+
};
|
|
60
|
+
const segmentModel = buildSegmentModel(segmentDefinition);
|
|
61
|
+
|
|
62
|
+
var result = evaluateIdentityInSegment(identityModel, segmentModel);
|
|
63
|
+
|
|
64
|
+
expect(result).toBe(true);
|
|
65
|
+
expect(getHashedPercentateForObjIds).toHaveBeenCalledTimes(1)
|
|
66
|
+
expect(getHashedPercentateForObjIds).toHaveBeenCalledWith([segmentModel.id, identityModel.djangoID])
|
|
67
|
+
});
|
package/.vscode/launch.json
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": "1.0.0",
|
|
3
|
-
"configurations": [
|
|
4
|
-
{
|
|
5
|
-
"type": "node",
|
|
6
|
-
"request": "launch",
|
|
7
|
-
"name": "Jest: current file",
|
|
8
|
-
//"env": { "NODE_ENV": "test" },
|
|
9
|
-
"program": "${workspaceFolder}/node_modules/.bin/jest",
|
|
10
|
-
"args": ["${file}", "--config", "jest.config.js"],
|
|
11
|
-
"console": "integratedTerminal",
|
|
12
|
-
"disableOptimisticBPs": true,
|
|
13
|
-
"windows": {
|
|
14
|
-
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
]
|
|
18
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
<img width="100%" src="https://github.com/Flagsmith/flagsmith/raw/main/static-files/hero.png"/>
|
|
2
|
-
|
|
3
|
-
# Flagsmith Node.js example with custom fetch agent
|
|
4
|
-
|
|
5
|
-
This is a simple Flagsmith Node.js example in local evaluation modes with a custom fetch agent. Using a custom fetch agent lets allows you to specify networking related options such as keep alive and socket timeout, read more on this [here](https://github.com/node-fetch/node-fetch#custom-agent).
|
|
6
|
-
|
|
7
|
-
## Running the example
|
|
8
|
-
|
|
9
|
-
```shell
|
|
10
|
-
npm i
|
|
11
|
-
npm run dev
|
|
12
|
-
```
|