flagsmith-nodejs 2.2.2 → 2.4.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.
Files changed (66) hide show
  1. package/build/flagsmith-engine/segments/constants.d.ts +6 -0
  2. package/build/flagsmith-engine/segments/constants.js +8 -2
  3. package/build/flagsmith-engine/segments/evaluators.d.ts +2 -1
  4. package/build/flagsmith-engine/segments/evaluators.js +9 -2
  5. package/build/flagsmith-engine/segments/models.d.ts +3 -3
  6. package/build/flagsmith-engine/segments/models.js +26 -1
  7. package/build/sdk/index.d.ts +5 -17
  8. package/build/sdk/index.js +2 -0
  9. package/build/sdk/types.d.ts +19 -1
  10. package/build/sdk/utils.d.ts +2 -2
  11. package/{es6-example → examples/api-proxy}/.babelrc +0 -0
  12. package/{es6-example → examples/api-proxy}/.eslintrc +0 -0
  13. package/examples/api-proxy/README.md +12 -0
  14. package/examples/api-proxy/package-lock.json +10895 -0
  15. package/examples/api-proxy/package.json +57 -0
  16. package/examples/api-proxy/src/api/index.js +42 -0
  17. package/{es6-example → examples/api-proxy}/src/index.js +0 -0
  18. package/examples/basic/.babelrc +3 -0
  19. package/examples/basic/.eslintrc +8 -0
  20. package/examples/basic/README.md +10 -0
  21. package/{es6-example → examples/basic}/package-lock.json +25 -39
  22. package/{es6-example → examples/basic}/package.json +1 -1
  23. package/examples/basic/src/api/index.js +33 -0
  24. package/{example/server → examples/basic/src}/index.js +4 -4
  25. package/examples/caching/.babelrc +3 -0
  26. package/examples/caching/.eslintrc +8 -0
  27. package/examples/caching/README.md +9 -0
  28. package/examples/caching/package-lock.json +6670 -0
  29. package/examples/caching/package.json +56 -0
  30. package/{example/server → examples/caching/src}/api/index.js +7 -11
  31. package/examples/caching/src/index.js +29 -0
  32. package/examples/custom-fetch-agent/.babelrc +3 -0
  33. package/examples/custom-fetch-agent/.eslintrc +8 -0
  34. package/examples/custom-fetch-agent/README.md +12 -0
  35. package/examples/custom-fetch-agent/package-lock.json +6756 -0
  36. package/examples/custom-fetch-agent/package.json +56 -0
  37. package/examples/custom-fetch-agent/src/api/index.js +34 -0
  38. package/examples/custom-fetch-agent/src/index.js +29 -0
  39. package/examples/local-evaluation/.babelrc +3 -0
  40. package/examples/local-evaluation/.eslintrc +8 -0
  41. package/examples/local-evaluation/README.md +18 -0
  42. package/examples/local-evaluation/package-lock.json +6674 -0
  43. package/examples/local-evaluation/package.json +56 -0
  44. package/{es6-example → examples/local-evaluation}/src/api/index.js +2 -13
  45. package/examples/local-evaluation/src/index.js +29 -0
  46. package/flagsmith-engine/segments/constants.ts +7 -1
  47. package/flagsmith-engine/segments/evaluators.ts +10 -6
  48. package/flagsmith-engine/segments/models.ts +15 -5
  49. package/package.json +1 -1
  50. package/sdk/index.ts +8 -15
  51. package/sdk/types.ts +19 -2
  52. package/sdk/utils.ts +2 -2
  53. package/tests/engine/engine-tests/engine-test-data/data/environment_n9fbf9h3v4fFgH3U3ngWhb.json +12591 -0
  54. package/tests/engine/engine-tests/engine-test-data/readme.md +30 -0
  55. package/tests/engine/unit/segments/segment_evaluators.test.ts +27 -0
  56. package/tests/engine/unit/segments/segments_model.test.ts +8 -1
  57. package/tests/sdk/flagsmith.test.ts +25 -10
  58. package/.idea/flagsmith-nodejs-client.iml +0 -12
  59. package/.idea/modules.xml +0 -8
  60. package/.idea/vcs.xml +0 -6
  61. package/es6-example/README.md +0 -62
  62. package/example/LICENSE +0 -0
  63. package/example/Procfile +0 -1
  64. package/example/README.md +0 -15
  65. package/example/package-lock.json +0 -2387
  66. package/example/package.json +0 -27
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "nodejs-es6-boilerplate",
3
+ "version": "1.0.1",
4
+ "description": "Node.js boilerplate with ES6, ESLint, and Prettier",
5
+ "main": "src/index.js",
6
+ "browserslist": [
7
+ "last 2 Chrome versions"
8
+ ],
9
+ "scripts": {
10
+ "test": "echo \"Error: no test specified\" && exit 1",
11
+ "clean": "rm -rf build && mkdir build",
12
+ "build-babel": "babel -d ./build ./src -s",
13
+ "build": "npm run clean && npm run build-babel",
14
+ "dev": "nodemon --exec npm start --ignore ./build",
15
+ "node-cache": "^5.1.2",
16
+ "ssg-node-express": "4.16.4",
17
+ "start": "npm run build && node ./build/index.js",
18
+ "format": "prettier --write \"src/**/*.js\"",
19
+ "format:check": "prettier --list-different \"src/**/*.js\"",
20
+ "lint": "eslint \"src/**/*.js\"",
21
+ "lint:fix": "eslint --fix \"src/**/*.js\""
22
+ },
23
+ "repository": "github:vferdiansyah/nodejs-es6-boilerplate",
24
+ "keywords": [
25
+ "javascript",
26
+ "node",
27
+ "nodejs",
28
+ "es6",
29
+ "eslint",
30
+ "prettier",
31
+ "boilerplate"
32
+ ],
33
+ "author": {
34
+ "name": "Veri Ferdiansyah",
35
+ "email": "veri.ferdi@gmail.com",
36
+ "url": "https://vferdiansyah.github.io"
37
+ },
38
+ "license": "MIT",
39
+ "bugs": "https://github.com/vferdiansyah/nodejs-es6-boilerplate/issues",
40
+ "homepage": "https://github.com/vferdiansyah/nodejs-es6-boilerplate#readme",
41
+ "devDependencies": {
42
+ "@babel/cli": "^7.18.10",
43
+ "@babel/core": "^7.5.5",
44
+ "@babel/preset-env": "^7.5.5",
45
+ "eslint": "^6.2.1",
46
+ "eslint-config-prettier": "^6.1.0",
47
+ "eslint-plugin-prettier": "^3.1.0",
48
+ "express": "^4.18.1",
49
+ "nodemon": "^2.0.19",
50
+ "prettier": "^1.18.2"
51
+ },
52
+ "dependencies": {
53
+ "flagsmith-nodejs": "^2.2.2",
54
+ "node-cache": "^5.1.2"
55
+ }
56
+ }
@@ -1,8 +1,7 @@
1
1
  import {Router} from 'express'
2
- import Flagsmith from 'flagsmith-nodejs'
2
+ import Flagsmith from '../../../../build'
3
3
 
4
4
  const environmentKey = '';
5
- import nodecache from "node-cache";
6
5
 
7
6
  if (!environmentKey) {
8
7
  throw new Error(
@@ -11,19 +10,9 @@ if (!environmentKey) {
11
10
  }
12
11
  const flagsmith = new Flagsmith({
13
12
  environmentKey,
14
- enableLocalEvaluation: true,
15
- cache: new nodecache({
16
- stdTTL: 10,
17
- checkperiod: 10,
18
- }),
13
+ enableLocalEvaluation: true
19
14
  });
20
15
 
21
-
22
- // solicitor
23
-
24
- // read only
25
- // sra authorisation date
26
-
27
16
  const api = () => {
28
17
  const api = Router();
29
18
 
@@ -0,0 +1,29 @@
1
+ import http from 'http'
2
+ import express from 'express'
3
+ import bodyParser from 'body-parser'
4
+ import api from './api'
5
+ const PORT = process.env.PORT || 3000;
6
+ const app = express();
7
+
8
+ app.server = http.createServer(app);
9
+
10
+ //Apply middleware
11
+ // parse various different custom JSON types as JSON
12
+ app.use(bodyParser.json());
13
+
14
+ // api router
15
+ app.use('/api', api());
16
+
17
+ app.server.listen(PORT);
18
+ console.log('Server started on port ' + PORT);
19
+ console.log();
20
+ console.log('Go to http://localhost:' + PORT + '/api');
21
+ console.log('To get an example response for getFlags');
22
+ console.log();
23
+ console.log('Go to http://localhost:' + PORT + '/api/flagsmith_sample_user');
24
+ console.log('To get an example feature state for a user');
25
+ console.log();
26
+ console.log('Go to http://localhost:' + PORT + '/api/flagsmith_sample_user/segments');
27
+ console.log('To get the segments which the user belongs to');
28
+
29
+ module.exports = app;
@@ -16,6 +16,9 @@ export const NOT_CONTAINS = 'NOT_CONTAINS';
16
16
  export const NOT_EQUAL = 'NOT_EQUAL';
17
17
  export const REGEX = 'REGEX';
18
18
  export const PERCENTAGE_SPLIT = 'PERCENTAGE_SPLIT';
19
+ export const IS_SET = 'IS_SET';
20
+ export const IS_NOT_SET = 'IS_NOT_SET';
21
+ export const MODULO = 'MODULO';
19
22
 
20
23
  export const CONDITION_OPERATORS = {
21
24
  EQUAL,
@@ -27,5 +30,8 @@ export const CONDITION_OPERATORS = {
27
30
  NOT_CONTAINS,
28
31
  NOT_EQUAL,
29
32
  REGEX,
30
- PERCENTAGE_SPLIT
33
+ PERCENTAGE_SPLIT,
34
+ IS_SET,
35
+ IS_NOT_SET,
36
+ MODULO
31
37
  };
@@ -2,7 +2,7 @@ import { EnvironmentModel } from '../environments/models';
2
2
  import { IdentityModel } from '../identities/models';
3
3
  import { TraitModel } from '../identities/traits/models';
4
4
  import { getHashedPercentateForObjIds } from '../utils/hashing';
5
- import { PERCENTAGE_SPLIT } from './constants';
5
+ import { PERCENTAGE_SPLIT, IS_SET, IS_NOT_SET } from './constants';
6
6
  import { SegmentConditionModel, SegmentModel, SegmentRuleModel } from './models';
7
7
 
8
8
  export function getIdentitySegments(
@@ -55,18 +55,22 @@ function traitsMatchSegmentRule(
55
55
  );
56
56
  }
57
57
 
58
- function traitsMatchSegmentCondition(
58
+ export function traitsMatchSegmentCondition(
59
59
  identityTraits: TraitModel[],
60
60
  condition: SegmentConditionModel,
61
61
  segmentId: number | string,
62
62
  identityId: number | string
63
63
  ): boolean {
64
64
  if (condition.operator == PERCENTAGE_SPLIT) {
65
- return getHashedPercentateForObjIds([segmentId, identityId]) <= parseFloat(condition.value);
65
+ return getHashedPercentateForObjIds([segmentId, identityId]) <= parseFloat(String(condition.value));
66
66
  }
67
-
68
67
  const traits = identityTraits.filter(t => t.traitKey === condition.property_);
69
68
  const trait = traits.length > 0 ? traits[0] : undefined;
70
-
69
+ if (condition.operator === IS_SET ) {
70
+ return !!trait;
71
+ } else if (condition.operator === IS_NOT_SET){
72
+ return trait == undefined;
73
+ }
71
74
  return trait ? condition.matchesTraitValue(trait.traitValue) : false;
72
- }
75
+
76
+ }
@@ -8,6 +8,7 @@ import {
8
8
  NONE_RULE,
9
9
  NOT_CONTAINS,
10
10
  REGEX,
11
+ MODULO,
11
12
  CONDITION_OPERATORS
12
13
  } from './constants';
13
14
  import { isSemver } from './util';
@@ -44,14 +45,15 @@ export const getMatchingFunctions = (semver: boolean) => (semver ? semverMatchin
44
45
  export class SegmentConditionModel {
45
46
  EXCEPTION_OPERATOR_METHODS: { [key: string]: string } = {
46
47
  [NOT_CONTAINS]: 'evaluateNotContains',
47
- [REGEX]: 'evaluateRegex'
48
+ [REGEX]: 'evaluateRegex',
49
+ [MODULO]: 'evaluateModulo',
48
50
  };
49
51
 
50
52
  operator: string;
51
- value: string;
52
- property_: string | undefined;
53
+ value: string | null | undefined;
54
+ property_: string | null | undefined;
53
55
 
54
- constructor(operator: string, value: string, property?: string) {
56
+ constructor(operator: string, value?: string | null | undefined, property?: string | null | undefined) {
55
57
  this.operator = operator;
56
58
  this.value = value;
57
59
  this.property_ = property;
@@ -63,7 +65,15 @@ export class SegmentConditionModel {
63
65
  return !traitValue.includes(this.value);
64
66
  },
65
67
  evaluateRegex: (traitValue: any) => {
66
- return !!traitValue.match(new RegExp(this.value));
68
+ return !!this.value && !!traitValue.match(new RegExp(this.value));
69
+ },
70
+ evaluateModulo: (traitValue: any) => {
71
+ if (isNaN(parseFloat(traitValue)) || !this.value) {
72
+ return false
73
+ }
74
+ const parts = (this.value).split("|");
75
+ const [divisor, reminder] = [parseFloat(parts[0]), parseFloat(parts[1])];
76
+ return traitValue % divisor === reminder
67
77
  }
68
78
  };
69
79
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flagsmith-nodejs",
3
- "version": "2.2.2",
3
+ "version": "2.4.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
@@ -1,3 +1,4 @@
1
+ import { RequestInit } from "node-fetch";
1
2
  import { getEnvironmentFeatureStates, getIdentityFeatureStates } from '../flagsmith-engine';
2
3
  import { EnvironmentModel } from '../flagsmith-engine/environments/models';
3
4
  import { buildEnvironmentModel } from '../flagsmith-engine/environments/util';
@@ -12,22 +13,24 @@ import { EnvironmentDataPollingManager } from './polling_manager';
12
13
  import { generateIdentitiesData, retryFetch } from './utils';
13
14
  import { SegmentModel } from '../flagsmith-engine/segments/models';
14
15
  import { getIdentitySegments } from '../flagsmith-engine/segments/evaluators';
15
- import { FlagsmithCache } from './types';
16
+ import { FlagsmithCache, FlagsmithConfig } from './types';
16
17
 
17
18
  export { AnalyticsProcessor } from './analytics';
18
19
  export { FlagsmithAPIError, FlagsmithClientError } from './errors';
19
20
 
20
21
  export { DefaultFlag, Flags } from './models';
21
22
  export { EnvironmentDataPollingManager } from './polling_manager';
22
- export { FlagsmithCache } from './types';
23
+ export { FlagsmithCache, FlagsmithConfig } from './types';
23
24
 
24
25
  const DEFAULT_API_URL = 'https://edge.api.flagsmith.com/api/v1/';
25
26
 
27
+
26
28
  export class Flagsmith {
27
29
  environmentKey?: string;
28
30
  apiUrl: string = DEFAULT_API_URL;
29
31
  customHeaders?: { [key: string]: any };
30
32
  requestTimeoutSeconds?: number;
33
+ agent: RequestInit['agent'];
31
34
  requestTimeoutMs?: number;
32
35
  enableLocalEvaluation?: boolean = false;
33
36
  environmentRefreshIntervalSeconds: number = 60;
@@ -76,19 +79,8 @@ export class Flagsmith {
76
79
  flags cannot be retrieved from the API or a non existent feature is
77
80
  requested
78
81
  */
79
- constructor(data: {
80
- environmentKey: string;
81
- apiUrl?: string;
82
- customHeaders?: { [key: string]: any };
83
- requestTimeoutSeconds?: number;
84
- enableLocalEvaluation?: boolean;
85
- environmentRefreshIntervalSeconds?: number;
86
- retries?: number;
87
- enableAnalytics?: boolean;
88
- defaultFlagHandler?: (featureName: string) => DefaultFlag;
89
- cache?: FlagsmithCache,
90
- onEnvironmentChange?: (error: Error | null, result: EnvironmentModel) => void,
91
- }) {
82
+ constructor(data: FlagsmithConfig) {
83
+ this.agent = data.agent;
92
84
  this.environmentKey = data.environmentKey;
93
85
  this.apiUrl = data.apiUrl || this.apiUrl;
94
86
  this.customHeaders = data.customHeaders;
@@ -276,6 +268,7 @@ export class Flagsmith {
276
268
  const data = await retryFetch(
277
269
  url,
278
270
  {
271
+ agent: this.agent,
279
272
  method: method,
280
273
  timeout: this.requestTimeoutMs || undefined,
281
274
  body: JSON.stringify(body),
package/sdk/types.ts CHANGED
@@ -1,8 +1,25 @@
1
- import { Flags } from "./models";
1
+ import { DefaultFlag, Flags } from "./models";
2
+ import { EnvironmentModel } from "../flagsmith-engine";
3
+ import { RequestInit } from "node-fetch";
2
4
 
3
5
  export interface FlagsmithCache {
4
6
  get(key: string): Promise<Flags|undefined> | undefined;
5
7
  set(key: string, value: Flags, ttl: string | number): boolean | Promise<boolean>;
6
8
  has(key: string): boolean | Promise<boolean>;
7
9
  [key: string]: any;
8
- }
10
+ }
11
+
12
+ export interface FlagsmithConfig {
13
+ environmentKey: string;
14
+ apiUrl?: string;
15
+ agent?:RequestInit['agent'];
16
+ customHeaders?: { [key: string]: any };
17
+ requestTimeoutSeconds?: number;
18
+ enableLocalEvaluation?: boolean;
19
+ environmentRefreshIntervalSeconds?: number;
20
+ retries?: number;
21
+ enableAnalytics?: boolean;
22
+ defaultFlagHandler?: (featureName: string) => DefaultFlag;
23
+ cache?: FlagsmithCache,
24
+ onEnvironmentChange?: (error: Error | null, result: EnvironmentModel) => void,
25
+ }
package/sdk/utils.ts CHANGED
@@ -1,4 +1,4 @@
1
- import fetch, { Response } from 'node-fetch';
1
+ import fetch, { RequestInit, Response } from 'node-fetch';
2
2
  // @ts-ignore
3
3
  if (typeof fetch.default !== 'undefined') fetch = fetch.default;
4
4
 
@@ -18,7 +18,7 @@ export const delay = (ms: number) =>
18
18
 
19
19
  export const retryFetch = (
20
20
  url: string,
21
- fetchOptions: any,
21
+ fetchOptions: RequestInit,
22
22
  retries = 3,
23
23
  timeout?: number // set an overall timeout for this function
24
24
  ): Promise<Response> => {