From 28e22ef974026e308114822c76d8485a590d4b74 Mon Sep 17 00:00:00 2001 From: Richard Vowles Date: Wed, 31 Jan 2024 21:23:14 +1300 Subject: [PATCH] - Support for feature properties (if any) - Updated API to 1.8 --- Dockerfile | 2 +- examples/todo-backend-typescript/package.json | 4 +- examples/todo-server-tests/package.json | 4 +- featurehub-javascript-client-sdk/CHANGELOG.md | 3 + .../app/client_feature_repository.ts | 2 - .../app/feature_state.ts | 2 + .../app/feature_state_holders.ts | 12 ++-- .../app/middleware.ts | 5 ++ .../app/models/.openapi-generator/FILES | 6 +- .../sse.yaml-featurehub-api.sha256 | 2 +- .../app/models/index.ts | 2 +- .../models/models/application-version-info.ts | 2 +- .../models/base-rollout-strategy-all-of.ts | 31 ++++++++++ .../models/models/base-rollout-strategy.ts | 43 +++---------- .../app/models/models/base-strategy.ts | 44 +++++++++++++ .../app/models/models/base-uistrategy.ts | 49 +++++++++++++++ .../models/feature-environment-collection.ts | 2 +- .../models/feature-rollout-strategy-all-of.ts | 10 ++- .../feature-rollout-strategy-attribute.ts | 6 +- .../models/models/feature-rollout-strategy.ts | 2 +- .../app/models/models/feature-state-update.ts | 6 +- .../app/models/models/feature-state.ts | 18 +++++- .../app/models/models/feature-value-type.ts | 2 +- .../app/models/models/index.ts | 5 +- .../app/models/models/role-type.ts | 5 +- .../rollout-strategy-attribute-conditional.ts | 2 +- .../models/rollout-strategy-attribute.ts | 57 +++++++++++++++++ .../models/rollout-strategy-field-type.ts | 2 +- .../app/models/models/sseresult-state.ts | 2 +- .../models/strategy-attribute-country-name.ts | 2 +- .../models/strategy-attribute-device-name.ts | 2 +- .../strategy-attribute-platform-name.ts | 2 +- .../strategy-attribute-well-known-names.ts | 2 +- .../app/strategy_matcher.ts | 4 +- featurehub-javascript-client-sdk/package.json | 5 +- .../test/apply_feature_spec.ts | 3 + .../test/client_context_spec.ts | 10 +-- .../test/percentage_spec.ts | 8 +-- .../test/repository_delete_feature_spec.ts | 2 +- .../test/repository_single_feature_spec.ts | 3 +- .../test/ripple_feature_update_spec.ts | 62 +++++++++---------- featurehub-javascript-node-sdk/package.json | 13 ++-- 42 files changed, 321 insertions(+), 129 deletions(-) create mode 100644 featurehub-javascript-client-sdk/app/models/models/base-rollout-strategy-all-of.ts create mode 100644 featurehub-javascript-client-sdk/app/models/models/base-strategy.ts create mode 100644 featurehub-javascript-client-sdk/app/models/models/base-uistrategy.ts create mode 100644 featurehub-javascript-client-sdk/app/models/models/rollout-strategy-attribute.ts diff --git a/Dockerfile b/Dockerfile index e031bdc..43f83a9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,7 +3,7 @@ FROM node:18-buster-slim as build ADD . /app WORKDIR /app RUN cd /app/featurehub-javascript-client-sdk && npm install && npm run compile -RUN cd /app/featurehub-javascript-node-sdk && npm install && npm run link && npm run compile +RUN cd /app/featurehub-javascript-node-sdk && npm install && npm run setup && npm run compile && npm run link RUN cd /app/examples/todo-server-tests && npm install && npm run compile RUN cd /app/examples/todo-backend-typescript && npm install && npm run compile diff --git a/examples/todo-backend-typescript/package.json b/examples/todo-backend-typescript/package.json index 80d28b0..6c1505a 100644 --- a/examples/todo-backend-typescript/package.json +++ b/examples/todo-backend-typescript/package.json @@ -6,8 +6,8 @@ "license": "MIT", "scripts": { "build": "node_modules/.bin/tsc", - "link": "npm link featurehub-javascript-client-sdk featurehub-javascript-node-sdk", - "compile": "npm run link && npm run build", + "setup": "npm link featurehub-javascript-client-sdk featurehub-javascript-node-sdk", + "compile": "npm run setup && npm run build", "start": "npm run build && node --trace-deprecation --trace-warnings dist/app.js", "run": "npm run build && node --trace-deprecation --trace-warnings dist/app.js" }, diff --git a/examples/todo-server-tests/package.json b/examples/todo-server-tests/package.json index 96e806e..1cb516f 100644 --- a/examples/todo-server-tests/package.json +++ b/examples/todo-server-tests/package.json @@ -4,8 +4,8 @@ "description": "Integration tests for FeatureHub SDKs (server-side)", "scripts": { "build": "node ./node_modules/typescript/bin/tsc", - "link": "npm link featurehub-javascript-client-sdk featurehub-javascript-node-sdk", - "compile": "npm run link && npm run build", + "setup": "npm link featurehub-javascript-client-sdk featurehub-javascript-node-sdk", + "compile": "npm run setup && npm run build", "test": "cucumber-js --require-module ts-node/register --require 'features/support/*.ts' --publish", "generate:specs": "openapi-generator-cli generate -g typescript-axios -i ../todo-api/todo-api.yaml -o ./src/client-axios" }, diff --git a/featurehub-javascript-client-sdk/CHANGELOG.md b/featurehub-javascript-client-sdk/CHANGELOG.md index f755100..b33d1e5 100644 --- a/featurehub-javascript-client-sdk/CHANGELOG.md +++ b/featurehub-javascript-client-sdk/CHANGELOG.md @@ -1,3 +1,6 @@ +#### 1.3.4 +- support for Extended Feature Properties from FHOS 1.8.0, see xxx insert documentation link. + #### 1.3.3 - listeners were not being fired when added to contexts that matched strategies. [bugfix](https://github.com/featurehub-io/featurehub-javascript-sdk/issues/196) - all the getX methods on the Context now have defaults, so you can say fhContext.getFlag("feature", false) and if it isn't set or doesn't exist, it will return false. This is an optional field so it doesn't break existing code. (feature) diff --git a/featurehub-javascript-client-sdk/app/client_feature_repository.ts b/featurehub-javascript-client-sdk/app/client_feature_repository.ts index 0e8500e..44b3c80 100644 --- a/featurehub-javascript-client-sdk/app/client_feature_repository.ts +++ b/featurehub-javascript-client-sdk/app/client_feature_repository.ts @@ -356,8 +356,6 @@ export class ClientFeatureRepository implements InternalFeatureRepository { const fState = holder.getFeatureState()!; if (fs.version! < fState.version!) { return false; - } else if (fs.version === fState.version && fs.value === fState.value) { - return false; } } diff --git a/featurehub-javascript-client-sdk/app/feature_state.ts b/featurehub-javascript-client-sdk/app/feature_state.ts index 803f6e2..25bd4ed 100644 --- a/featurehub-javascript-client-sdk/app/feature_state.ts +++ b/featurehub-javascript-client-sdk/app/feature_state.ts @@ -117,4 +117,6 @@ export interface FeatureStateHolder { get type(): FeatureValueType | undefined; withContext(param: ClientContext): FeatureStateHolder; + + get featureProperties(): Record | undefined; } diff --git a/featurehub-javascript-client-sdk/app/feature_state_holders.ts b/featurehub-javascript-client-sdk/app/feature_state_holders.ts index cc7468c..422e00f 100644 --- a/featurehub-javascript-client-sdk/app/feature_state_holders.ts +++ b/featurehub-javascript-client-sdk/app/feature_state_holders.ts @@ -3,7 +3,7 @@ import { FeatureState, FeatureValueType } from './models'; import { ClientContext } from './client_context'; import { InternalFeatureRepository } from './internal_feature_repository'; import { ListenerUtils } from './listener_utils'; -import {fhLog} from "./feature_hub_config"; +import { fhLog } from './feature_hub_config'; interface ListenerTracker { listener: FeatureListener; @@ -88,7 +88,7 @@ export class FeatureStateBaseHolder implements FeatureStateHolder { if (this._ctx !== undefined) { this.listeners.set(pos, { listener: () => listener(this), holder: this - } ); + }); } else { this.listeners.set(pos, { listener: listener, holder: this @@ -146,7 +146,7 @@ export class FeatureStateBaseHolder implements FeatureStateHolder { this.listeners.forEach((value, key) => { listenerValues.set(key, { value: value.holder.value - }) + }); }); this.internalFeatureState = fs; @@ -164,7 +164,7 @@ export class FeatureStateBaseHolder implements FeatureStateHolder { try { value.listener(value.holder); } catch (e) { - fhLog.error(`Failed to trigger listener`, e); + fhLog.error('Failed to trigger listener', e); } } }); @@ -293,4 +293,8 @@ export class FeatureStateBaseHolder implements FeatureStateHolder { get value(): T { return this._getValue(this.getType(), true); } + + get featureProperties(): Record | undefined { + return this.featureState()?.fp ?? undefined; + } } diff --git a/featurehub-javascript-client-sdk/app/middleware.ts b/featurehub-javascript-client-sdk/app/middleware.ts index 30e5d79..542d9c5 100644 --- a/featurehub-javascript-client-sdk/app/middleware.ts +++ b/featurehub-javascript-client-sdk/app/middleware.ts @@ -22,6 +22,11 @@ class BaggageHolder implements FeatureStateHolder { this.baggageValue = value; } + // feature properties are not included in baggage, they don't make logical sense. + get featureProperties(): Record | undefined { + return undefined; + } + isEnabled(): boolean { return this.getBoolean() === true; } diff --git a/featurehub-javascript-client-sdk/app/models/.openapi-generator/FILES b/featurehub-javascript-client-sdk/app/models/.openapi-generator/FILES index 50f646d..cacadd8 100644 --- a/featurehub-javascript-client-sdk/app/models/.openapi-generator/FILES +++ b/featurehub-javascript-client-sdk/app/models/.openapi-generator/FILES @@ -1,11 +1,12 @@ .gitignore .npmignore -.openapi-generator-ignore git_push.sh index.ts models/application-version-info.ts -models/base-rollout-strategy-attribute.ts +models/base-rollout-strategy-all-of.ts models/base-rollout-strategy.ts +models/base-strategy.ts +models/base-uistrategy.ts models/feature-environment-collection.ts models/feature-rollout-strategy-all-of.ts models/feature-rollout-strategy-attribute.ts @@ -16,6 +17,7 @@ models/feature-value-type.ts models/index.ts models/role-type.ts models/rollout-strategy-attribute-conditional.ts +models/rollout-strategy-attribute.ts models/rollout-strategy-field-type.ts models/sseresult-state.ts models/strategy-attribute-country-name.ts diff --git a/featurehub-javascript-client-sdk/app/models/.openapi-generator/sse.yaml-featurehub-api.sha256 b/featurehub-javascript-client-sdk/app/models/.openapi-generator/sse.yaml-featurehub-api.sha256 index 3ab99cf..25c5902 100644 --- a/featurehub-javascript-client-sdk/app/models/.openapi-generator/sse.yaml-featurehub-api.sha256 +++ b/featurehub-javascript-client-sdk/app/models/.openapi-generator/sse.yaml-featurehub-api.sha256 @@ -1 +1 @@ -c1d8832e6a2bf63c1201a9ce0e28f9024bb577984c9ab652a03f4bb27f443f83 \ No newline at end of file +357944b3dddaf0986739059920aae8fc5e554ee1d80beffa07e26b38ea4f88ff \ No newline at end of file diff --git a/featurehub-javascript-client-sdk/app/models/index.ts b/featurehub-javascript-client-sdk/app/models/index.ts index 8ba397e..72dbc58 100644 --- a/featurehub-javascript-client-sdk/app/models/index.ts +++ b/featurehub-javascript-client-sdk/app/models/index.ts @@ -4,7 +4,7 @@ * FeatureServiceApi * This describes the API clients use for accessing features * - * The version of the OpenAPI document: 1.1.3 + * The version of the OpenAPI document: 1.1.8 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/featurehub-javascript-client-sdk/app/models/models/application-version-info.ts b/featurehub-javascript-client-sdk/app/models/models/application-version-info.ts index 632bbcc..3308538 100644 --- a/featurehub-javascript-client-sdk/app/models/models/application-version-info.ts +++ b/featurehub-javascript-client-sdk/app/models/models/application-version-info.ts @@ -4,7 +4,7 @@ * FeatureServiceApi * This describes the API clients use for accessing features * - * The version of the OpenAPI document: 1.1.3 + * The version of the OpenAPI document: 1.1.8 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/featurehub-javascript-client-sdk/app/models/models/base-rollout-strategy-all-of.ts b/featurehub-javascript-client-sdk/app/models/models/base-rollout-strategy-all-of.ts new file mode 100644 index 0000000..399943b --- /dev/null +++ b/featurehub-javascript-client-sdk/app/models/models/base-rollout-strategy-all-of.ts @@ -0,0 +1,31 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * FeatureServiceApi + * This describes the API clients use for accessing features + * + * The version of the OpenAPI document: 1.1.8 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface BaseRolloutStrategyAllOf + */ +export interface BaseRolloutStrategyAllOf { + /** + * when we attach the RolloutStrategy for Dacha or SSE this lets us push the value out. Only visible in SDK and SSE Edge. + * @type {any} + * @memberof BaseRolloutStrategyAllOf + */ + value?: any | null; +} + + diff --git a/featurehub-javascript-client-sdk/app/models/models/base-rollout-strategy.ts b/featurehub-javascript-client-sdk/app/models/models/base-rollout-strategy.ts index 3be1d87..39777d9 100644 --- a/featurehub-javascript-client-sdk/app/models/models/base-rollout-strategy.ts +++ b/featurehub-javascript-client-sdk/app/models/models/base-rollout-strategy.ts @@ -4,7 +4,7 @@ * FeatureServiceApi * This describes the API clients use for accessing features * - * The version of the OpenAPI document: 1.1.3 + * The version of the OpenAPI document: 1.1.8 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -13,44 +13,15 @@ */ -import { BaseRolloutStrategyAttribute } from './base-rollout-strategy-attribute'; +import { BaseRolloutStrategyAllOf } from './base-rollout-strategy-all-of'; +import { BaseStrategy } from './base-strategy'; +import { RolloutStrategyAttribute } from './rollout-strategy-attribute'; /** - * if the feature in an environment is different from its default, this will be the reason for it. a rollout strategy is defined at the Application level and then applied to a specific feature value. When they are copied to the cache layer they are cloned and the feature value for that strategy is inserted into the clone and those are published. + * @type BaseRolloutStrategy * @export - * @interface BaseRolloutStrategy */ -export interface BaseRolloutStrategy { - /** - * - * @type {string} - * @memberof BaseRolloutStrategy - */ - id?: string; - /** - * value between 0 and 1000000 - for four decimal places - * @type {number} - * @memberof BaseRolloutStrategy - */ - percentage?: number; - /** - * if you don\'t wish to apply percentage based on user id, you can use one or more attributes defined here - * @type {Array} - * @memberof BaseRolloutStrategy - */ - percentageAttributes?: Array; - /** - * when we attach the RolloutStrategy for Dacha or SSE this lets us push the value out. Only visible in SDK and SSE Edge. - * @type {any} - * @memberof BaseRolloutStrategy - */ - value?: any | null; - /** - * - * @type {Array} - * @memberof BaseRolloutStrategy - */ - attributes?: Array; -} +export type BaseRolloutStrategy = BaseRolloutStrategyAllOf & BaseStrategy; + diff --git a/featurehub-javascript-client-sdk/app/models/models/base-strategy.ts b/featurehub-javascript-client-sdk/app/models/models/base-strategy.ts new file mode 100644 index 0000000..d3c6358 --- /dev/null +++ b/featurehub-javascript-client-sdk/app/models/models/base-strategy.ts @@ -0,0 +1,44 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * FeatureServiceApi + * This describes the API clients use for accessing features + * + * The version of the OpenAPI document: 1.1.8 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import { RolloutStrategyAttribute } from './rollout-strategy-attribute'; + +/** + * if the feature in an environment is different from its default, this will be the reason for it. a rollout strategy is defined at the Application level and then applied to a specific feature value. When they are copied to the cache layer they are cloned and the feature value for that strategy is inserted into the clone and those are published. + * @export + * @interface BaseStrategy + */ +export interface BaseStrategy { + /** + * value between 0 and 1000000 - for four decimal places + * @type {number} + * @memberof BaseStrategy + */ + percentage?: number | null; + /** + * if you don\'t wish to apply percentage based on user id, you can use one or more attributes defined here + * @type {Array} + * @memberof BaseStrategy + */ + percentageAttributes?: Array | null; + /** + * + * @type {Array} + * @memberof BaseStrategy + */ + attributes?: Array | null; +} + + diff --git a/featurehub-javascript-client-sdk/app/models/models/base-uistrategy.ts b/featurehub-javascript-client-sdk/app/models/models/base-uistrategy.ts new file mode 100644 index 0000000..dad5bca --- /dev/null +++ b/featurehub-javascript-client-sdk/app/models/models/base-uistrategy.ts @@ -0,0 +1,49 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * FeatureServiceApi + * This describes the API clients use for accessing features + * + * The version of the OpenAPI document: 1.1.8 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + + +/** + * + * @export + * @interface BaseUIStrategy + */ +export interface BaseUIStrategy { + /** + * names are unique in a case insensitive fashion + * @type {string} + * @memberof BaseUIStrategy + */ + name?: string; + /** + * the colour used to display the strategy in the UI. indexed table of background/foreground combos. + * @type {number} + * @memberof BaseUIStrategy + */ + colouring?: number | null; + /** + * Disable this strategy across all feature values that are using it + * @type {boolean} + * @memberof BaseUIStrategy + */ + disabled?: boolean; + /** + * url to avatar (if any). Not sent to SDK. Preferably a unicorn. + * @type {string} + * @memberof BaseUIStrategy + */ + avatar?: string | null; +} + + diff --git a/featurehub-javascript-client-sdk/app/models/models/feature-environment-collection.ts b/featurehub-javascript-client-sdk/app/models/models/feature-environment-collection.ts index f414aad..3d905ea 100644 --- a/featurehub-javascript-client-sdk/app/models/models/feature-environment-collection.ts +++ b/featurehub-javascript-client-sdk/app/models/models/feature-environment-collection.ts @@ -4,7 +4,7 @@ * FeatureServiceApi * This describes the API clients use for accessing features * - * The version of the OpenAPI document: 1.1.3 + * The version of the OpenAPI document: 1.1.8 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/featurehub-javascript-client-sdk/app/models/models/feature-rollout-strategy-all-of.ts b/featurehub-javascript-client-sdk/app/models/models/feature-rollout-strategy-all-of.ts index d9e0804..c6eaac4 100644 --- a/featurehub-javascript-client-sdk/app/models/models/feature-rollout-strategy-all-of.ts +++ b/featurehub-javascript-client-sdk/app/models/models/feature-rollout-strategy-all-of.ts @@ -4,7 +4,7 @@ * FeatureServiceApi * This describes the API clients use for accessing features * - * The version of the OpenAPI document: 1.1.3 + * The version of the OpenAPI document: 1.1.8 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -21,12 +21,18 @@ import { FeatureRolloutStrategyAttribute } from './feature-rollout-strategy-attr * @interface FeatureRolloutStrategyAllOf */ export interface FeatureRolloutStrategyAllOf { + /** + * strategy id + * @type {string} + * @memberof FeatureRolloutStrategyAllOf + */ + id: string; /** * * @type {Array} * @memberof FeatureRolloutStrategyAllOf */ - attributes: Array; + attributes?: Array; } diff --git a/featurehub-javascript-client-sdk/app/models/models/feature-rollout-strategy-attribute.ts b/featurehub-javascript-client-sdk/app/models/models/feature-rollout-strategy-attribute.ts index 2286bc3..99cb7b4 100644 --- a/featurehub-javascript-client-sdk/app/models/models/feature-rollout-strategy-attribute.ts +++ b/featurehub-javascript-client-sdk/app/models/models/feature-rollout-strategy-attribute.ts @@ -4,7 +4,7 @@ * FeatureServiceApi * This describes the API clients use for accessing features * - * The version of the OpenAPI document: 1.1.3 + * The version of the OpenAPI document: 1.1.8 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -13,7 +13,7 @@ */ -import { BaseRolloutStrategyAttribute } from './base-rollout-strategy-attribute'; +import { RolloutStrategyAttribute } from './rollout-strategy-attribute'; import { RolloutStrategyAttributeConditional } from './rollout-strategy-attribute-conditional'; import { RolloutStrategyFieldType } from './rollout-strategy-field-type'; @@ -21,7 +21,7 @@ import { RolloutStrategyFieldType } from './rollout-strategy-field-type'; * @type FeatureRolloutStrategyAttribute * @export */ -export type FeatureRolloutStrategyAttribute = BaseRolloutStrategyAttribute; +export type FeatureRolloutStrategyAttribute = RolloutStrategyAttribute; diff --git a/featurehub-javascript-client-sdk/app/models/models/feature-rollout-strategy.ts b/featurehub-javascript-client-sdk/app/models/models/feature-rollout-strategy.ts index bf0bd24..75e1870 100644 --- a/featurehub-javascript-client-sdk/app/models/models/feature-rollout-strategy.ts +++ b/featurehub-javascript-client-sdk/app/models/models/feature-rollout-strategy.ts @@ -4,7 +4,7 @@ * FeatureServiceApi * This describes the API clients use for accessing features * - * The version of the OpenAPI document: 1.1.3 + * The version of the OpenAPI document: 1.1.8 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/featurehub-javascript-client-sdk/app/models/models/feature-state-update.ts b/featurehub-javascript-client-sdk/app/models/models/feature-state-update.ts index 225046e..78f034d 100644 --- a/featurehub-javascript-client-sdk/app/models/models/feature-state-update.ts +++ b/featurehub-javascript-client-sdk/app/models/models/feature-state-update.ts @@ -4,7 +4,7 @@ * FeatureServiceApi * This describes the API clients use for accessing features * - * The version of the OpenAPI document: 1.1.3 + * The version of the OpenAPI document: 1.1.8 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -31,13 +31,13 @@ export interface FeatureStateUpdate { * @type {boolean} * @memberof FeatureStateUpdate */ - updateValue?: boolean; + updateValue?: boolean | null; /** * set only if you wish to lock or unlock, otherwise null * @type {boolean} * @memberof FeatureStateUpdate */ - lock?: boolean; + lock?: boolean | null; } diff --git a/featurehub-javascript-client-sdk/app/models/models/feature-state.ts b/featurehub-javascript-client-sdk/app/models/models/feature-state.ts index 542685d..7fde914 100644 --- a/featurehub-javascript-client-sdk/app/models/models/feature-state.ts +++ b/featurehub-javascript-client-sdk/app/models/models/feature-state.ts @@ -4,7 +4,7 @@ * FeatureServiceApi * This describes the API clients use for accessing features * - * The version of the OpenAPI document: 1.1.3 + * The version of the OpenAPI document: 1.1.8 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -58,18 +58,30 @@ export interface FeatureState { * @memberof FeatureState */ value?: any | null; + /** + * strategy id that was applied if this was a server evaluated feature + * @type {string} + * @memberof FeatureState + */ + v?: string | null; + /** + * Any additional properties configured by System Operator + * @type {Record} + * @memberof FeatureState + */ + fp?: Record | null; /** * This field is filled in from the client side in the GET api as the GET api is able to request multiple environments. It is never passed from the server, as an array of feature states is wrapped in an environment. * @type {string} * @memberof FeatureState */ - environmentId?: string; + environmentId?: string | null; /** * * @type {Array} * @memberof FeatureState */ - strategies?: Array; + strategies?: Array | null; } diff --git a/featurehub-javascript-client-sdk/app/models/models/feature-value-type.ts b/featurehub-javascript-client-sdk/app/models/models/feature-value-type.ts index 8a10f04..d202582 100644 --- a/featurehub-javascript-client-sdk/app/models/models/feature-value-type.ts +++ b/featurehub-javascript-client-sdk/app/models/models/feature-value-type.ts @@ -4,7 +4,7 @@ * FeatureServiceApi * This describes the API clients use for accessing features * - * The version of the OpenAPI document: 1.1.3 + * The version of the OpenAPI document: 1.1.8 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/featurehub-javascript-client-sdk/app/models/models/index.ts b/featurehub-javascript-client-sdk/app/models/models/index.ts index bb70f23..835a622 100644 --- a/featurehub-javascript-client-sdk/app/models/models/index.ts +++ b/featurehub-javascript-client-sdk/app/models/models/index.ts @@ -1,6 +1,8 @@ export * from './application-version-info'; export * from './base-rollout-strategy'; -export * from './base-rollout-strategy-attribute'; +export * from './base-rollout-strategy-all-of'; +export * from './base-strategy'; +export * from './base-uistrategy'; export * from './feature-environment-collection'; export * from './feature-rollout-strategy'; export * from './feature-rollout-strategy-all-of'; @@ -9,6 +11,7 @@ export * from './feature-state'; export * from './feature-state-update'; export * from './feature-value-type'; export * from './role-type'; +export * from './rollout-strategy-attribute'; export * from './rollout-strategy-attribute-conditional'; export * from './rollout-strategy-field-type'; export * from './sseresult-state'; diff --git a/featurehub-javascript-client-sdk/app/models/models/role-type.ts b/featurehub-javascript-client-sdk/app/models/models/role-type.ts index 5cf28f2..52e7d01 100644 --- a/featurehub-javascript-client-sdk/app/models/models/role-type.ts +++ b/featurehub-javascript-client-sdk/app/models/models/role-type.ts @@ -4,7 +4,7 @@ * FeatureServiceApi * This describes the API clients use for accessing features * - * The version of the OpenAPI document: 1.1.3 + * The version of the OpenAPI document: 1.1.8 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). @@ -23,7 +23,8 @@ export enum RoleType { Read = 'READ', Lock = 'LOCK', Unlock = 'UNLOCK', - ChangeValue = 'CHANGE_VALUE' + ChangeValue = 'CHANGE_VALUE', + ExtendedData = 'EXTENDED_DATA' } diff --git a/featurehub-javascript-client-sdk/app/models/models/rollout-strategy-attribute-conditional.ts b/featurehub-javascript-client-sdk/app/models/models/rollout-strategy-attribute-conditional.ts index 9463840..0a8e02c 100644 --- a/featurehub-javascript-client-sdk/app/models/models/rollout-strategy-attribute-conditional.ts +++ b/featurehub-javascript-client-sdk/app/models/models/rollout-strategy-attribute-conditional.ts @@ -4,7 +4,7 @@ * FeatureServiceApi * This describes the API clients use for accessing features * - * The version of the OpenAPI document: 1.1.3 + * The version of the OpenAPI document: 1.1.8 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/featurehub-javascript-client-sdk/app/models/models/rollout-strategy-attribute.ts b/featurehub-javascript-client-sdk/app/models/models/rollout-strategy-attribute.ts new file mode 100644 index 0000000..6febd7a --- /dev/null +++ b/featurehub-javascript-client-sdk/app/models/models/rollout-strategy-attribute.ts @@ -0,0 +1,57 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * FeatureServiceApi + * This describes the API clients use for accessing features + * + * The version of the OpenAPI document: 1.1.8 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +import { RolloutStrategyAttributeConditional } from './rollout-strategy-attribute-conditional'; +import { RolloutStrategyFieldType } from './rollout-strategy-field-type'; + +/** + * + * @export + * @interface RolloutStrategyAttribute + */ +export interface RolloutStrategyAttribute { + /** + * An ID that needs to exist for validation + * @type {string} + * @memberof RolloutStrategyAttribute + */ + id?: string | null; + /** + * + * @type {RolloutStrategyAttributeConditional} + * @memberof RolloutStrategyAttribute + */ + conditional: RolloutStrategyAttributeConditional; + /** + * + * @type {string} + * @memberof RolloutStrategyAttribute + */ + fieldName: string; + /** + * the value(s) associated with this rule + * @type {Array} + * @memberof RolloutStrategyAttribute + */ + values?: Array; + /** + * + * @type {RolloutStrategyFieldType} + * @memberof RolloutStrategyAttribute + */ + type: RolloutStrategyFieldType; +} + + diff --git a/featurehub-javascript-client-sdk/app/models/models/rollout-strategy-field-type.ts b/featurehub-javascript-client-sdk/app/models/models/rollout-strategy-field-type.ts index 1c4ca86..5263380 100644 --- a/featurehub-javascript-client-sdk/app/models/models/rollout-strategy-field-type.ts +++ b/featurehub-javascript-client-sdk/app/models/models/rollout-strategy-field-type.ts @@ -4,7 +4,7 @@ * FeatureServiceApi * This describes the API clients use for accessing features * - * The version of the OpenAPI document: 1.1.3 + * The version of the OpenAPI document: 1.1.8 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/featurehub-javascript-client-sdk/app/models/models/sseresult-state.ts b/featurehub-javascript-client-sdk/app/models/models/sseresult-state.ts index 36717dc..ba7a578 100644 --- a/featurehub-javascript-client-sdk/app/models/models/sseresult-state.ts +++ b/featurehub-javascript-client-sdk/app/models/models/sseresult-state.ts @@ -4,7 +4,7 @@ * FeatureServiceApi * This describes the API clients use for accessing features * - * The version of the OpenAPI document: 1.1.3 + * The version of the OpenAPI document: 1.1.8 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/featurehub-javascript-client-sdk/app/models/models/strategy-attribute-country-name.ts b/featurehub-javascript-client-sdk/app/models/models/strategy-attribute-country-name.ts index ddd670f..f13302f 100644 --- a/featurehub-javascript-client-sdk/app/models/models/strategy-attribute-country-name.ts +++ b/featurehub-javascript-client-sdk/app/models/models/strategy-attribute-country-name.ts @@ -4,7 +4,7 @@ * FeatureServiceApi * This describes the API clients use for accessing features * - * The version of the OpenAPI document: 1.1.3 + * The version of the OpenAPI document: 1.1.8 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/featurehub-javascript-client-sdk/app/models/models/strategy-attribute-device-name.ts b/featurehub-javascript-client-sdk/app/models/models/strategy-attribute-device-name.ts index 8e4e6c9..d5f08fa 100644 --- a/featurehub-javascript-client-sdk/app/models/models/strategy-attribute-device-name.ts +++ b/featurehub-javascript-client-sdk/app/models/models/strategy-attribute-device-name.ts @@ -4,7 +4,7 @@ * FeatureServiceApi * This describes the API clients use for accessing features * - * The version of the OpenAPI document: 1.1.3 + * The version of the OpenAPI document: 1.1.8 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/featurehub-javascript-client-sdk/app/models/models/strategy-attribute-platform-name.ts b/featurehub-javascript-client-sdk/app/models/models/strategy-attribute-platform-name.ts index a7ea1ec..cde05a8 100644 --- a/featurehub-javascript-client-sdk/app/models/models/strategy-attribute-platform-name.ts +++ b/featurehub-javascript-client-sdk/app/models/models/strategy-attribute-platform-name.ts @@ -4,7 +4,7 @@ * FeatureServiceApi * This describes the API clients use for accessing features * - * The version of the OpenAPI document: 1.1.3 + * The version of the OpenAPI document: 1.1.8 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/featurehub-javascript-client-sdk/app/models/models/strategy-attribute-well-known-names.ts b/featurehub-javascript-client-sdk/app/models/models/strategy-attribute-well-known-names.ts index 44d8a24..4a821c6 100644 --- a/featurehub-javascript-client-sdk/app/models/models/strategy-attribute-well-known-names.ts +++ b/featurehub-javascript-client-sdk/app/models/models/strategy-attribute-well-known-names.ts @@ -4,7 +4,7 @@ * FeatureServiceApi * This describes the API clients use for accessing features * - * The version of the OpenAPI document: 1.1.3 + * The version of the OpenAPI document: 1.1.8 * * * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). diff --git a/featurehub-javascript-client-sdk/app/strategy_matcher.ts b/featurehub-javascript-client-sdk/app/strategy_matcher.ts index e66a402..0399888 100644 --- a/featurehub-javascript-client-sdk/app/strategy_matcher.ts +++ b/featurehub-javascript-client-sdk/app/strategy_matcher.ts @@ -285,7 +285,7 @@ export class ApplyFeature { for (const rsi of strategies) { if (rsi.percentage !== 0 && (defaultPercentageKey != null || - (rsi.percentageAttributes !== undefined && rsi.percentageAttributes.length))) { + (rsi.percentageAttributes !== undefined && rsi.percentageAttributes?.length))) { const newPercentageKey = ApplyFeature.determinePercentageKey(context, rsi.percentageAttributes!); if (!basePercentage.has(newPercentageKey)) { @@ -341,7 +341,7 @@ export class ApplyFeature { } private matchAttribute(context: ClientContext, rsi: FeatureRolloutStrategy): boolean { - for (const attr of rsi.attributes) { + for (const attr of (rsi.attributes || [])) { let suppliedValues = context.getAttrs(attr.fieldName!); if (suppliedValues.length == 0 && attr.fieldName!.toLowerCase() === 'now') { // tslint:disable-next-line:switch-default diff --git a/featurehub-javascript-client-sdk/package.json b/featurehub-javascript-client-sdk/package.json index da08997..57f63f2 100644 --- a/featurehub-javascript-client-sdk/package.json +++ b/featurehub-javascript-client-sdk/package.json @@ -1,6 +1,6 @@ { "name": "featurehub-javascript-client-sdk", - "version": "1.3.3", + "version": "1.3.4", "description": "FeatureHub client/browser SDK", "author": "info@featurehub.io", "sideEffects": false, @@ -50,7 +50,7 @@ "@types/chai": "^4.2.18", "@types/mocha": "^9.0.0", "@types/node": "^16.18.25", - "@types/sinon": "^10.0.13", + "@types/sinon": "^10.0.13", "@typescript-eslint/eslint-plugin": "^5.59.2", "@typescript-eslint/parser": "^5.59.2", "@typescript-eslint/typescript-estree": "^5.59.2", @@ -65,7 +65,6 @@ }, "dependencies": { "@juanelas/base64": "^1.0.5", - "@types/murmurhash": "^2.0.0", "cross-sha256": "^1.2.0", "murmurhash": "^2.0.1", "netmask": "^2.0.2", diff --git a/featurehub-javascript-client-sdk/test/apply_feature_spec.ts b/featurehub-javascript-client-sdk/test/apply_feature_spec.ts index 6db8def..b789e76 100644 --- a/featurehub-javascript-client-sdk/test/apply_feature_spec.ts +++ b/featurehub-javascript-client-sdk/test/apply_feature_spec.ts @@ -106,6 +106,7 @@ describe('apply feature works as expected', () => { matcher.findMatcher(Arg.any()).returns(sMatcher); const found = app.apply([{ + id: 'x', value: 'sausage', attributes: [ { @@ -174,6 +175,7 @@ describe('apply feature works as expected', () => { const sApp = new ApplyFeature(pCalc, new MatcherRegistry()); const found = sApp.apply([{ + id: 'x', value: 'sausage', percentage: 20, attributes: [ @@ -199,6 +201,7 @@ describe('apply feature works as expected', () => { const sApp = new ApplyFeature(pCalc, new MatcherRegistry()); const found = sApp.apply([{ + id: 'x', value: 'sausage', percentage: 20, attributes: [ diff --git a/featurehub-javascript-client-sdk/test/client_context_spec.ts b/featurehub-javascript-client-sdk/test/client_context_spec.ts index 66c1358..08e55ff 100644 --- a/featurehub-javascript-client-sdk/test/client_context_spec.ts +++ b/featurehub-javascript-client-sdk/test/client_context_spec.ts @@ -11,8 +11,8 @@ import { import { Substitute, Arg, SubstituteOf } from '@fluffy-spoon/substitute'; import { ClientEvalFeatureContext, ServerEvalFeatureContext, InternalFeatureRepository } from '../app'; import { expect } from 'chai'; -import {FeatureStateBaseHolder} from "../app/feature_state_holders"; -import {server} from "sinon"; +import { FeatureStateBaseHolder } from '../app/feature_state_holders'; +import { server } from 'sinon'; describe('Client context should be able to encode as expected', () => { let repo: SubstituteOf; @@ -63,9 +63,9 @@ describe('Client context should be able to encode as expected', () => { const fhSubst = Substitute.for>(); repo.feature('joy').returns(fhSubst); fhSubst.isSet().returns(false); - expect(serverContext.getFlag("joy", true)).to.be.true; - expect(serverContext.getString("joy", "loopypro")).to.eq("loopypro"); - expect(serverContext.getJson('joy', {x:1})).to.deep.eq({x:1}); + expect(serverContext.getFlag('joy', true)).to.be.true; + expect(serverContext.getString('joy', 'loopypro')).to.eq('loopypro'); + expect(serverContext.getJson('joy', { x: 1 })).to.deep.eq({ x: 1 }); expect(serverContext.getRawJson('joy', 'raw-json')).to.eq('raw-json'); }); diff --git a/featurehub-javascript-client-sdk/test/percentage_spec.ts b/featurehub-javascript-client-sdk/test/percentage_spec.ts index 20d1732..29ceb26 100644 --- a/featurehub-javascript-client-sdk/test/percentage_spec.ts +++ b/featurehub-javascript-client-sdk/test/percentage_spec.ts @@ -1,10 +1,10 @@ -import {expect} from "chai"; -import {Murmur3PercentageCalculator} from "../app/strategy_matcher"; +import { expect } from 'chai'; +import { Murmur3PercentageCalculator } from '../app/strategy_matcher'; describe('percentage checks', () => { it('should match other sdks', () => { - expect(new Murmur3PercentageCalculator().determineClientPercentage("fred", "abcde")).to.eq(212628); - expect(new Murmur3PercentageCalculator().determineClientPercentage("zappo-food", "172765e02-2-1-1-2-2-1")).to.eq(931882); + expect(new Murmur3PercentageCalculator().determineClientPercentage('fred', 'abcde')).to.eq(212628); + expect(new Murmur3PercentageCalculator().determineClientPercentage('zappo-food', '172765e02-2-1-1-2-2-1')).to.eq(931882); }); }); \ No newline at end of file diff --git a/featurehub-javascript-client-sdk/test/repository_delete_feature_spec.ts b/featurehub-javascript-client-sdk/test/repository_delete_feature_spec.ts index 56a0023..a5d6400 100644 --- a/featurehub-javascript-client-sdk/test/repository_delete_feature_spec.ts +++ b/featurehub-javascript-client-sdk/test/repository_delete_feature_spec.ts @@ -3,7 +3,7 @@ import { expect } from 'chai'; describe('if a feature is deleted it becomes undefined', () => { let repo: ClientFeatureRepository; - let features: Array + let features: Array; beforeEach(() => { repo = new ClientFeatureRepository(); diff --git a/featurehub-javascript-client-sdk/test/repository_single_feature_spec.ts b/featurehub-javascript-client-sdk/test/repository_single_feature_spec.ts index 763d71c..cd8b513 100644 --- a/featurehub-javascript-client-sdk/test/repository_single_feature_spec.ts +++ b/featurehub-javascript-client-sdk/test/repository_single_feature_spec.ts @@ -18,7 +18,7 @@ describe('repository reacts to single feature changes as expected', () => { expect(repo.feature('pear').getVersion()).to.eq(0); - repo.notify(SSEResultState.Feature, { id: '1', key: 'pear', version: 1, type: FeatureValueType.String, value: 'now-set' }); + repo.notify(SSEResultState.Feature, { id: '1', key: 'pear', version: 1, type: FeatureValueType.String, value: 'now-set', fp: { 'category': 'shoes', 'appName': 'cajon' } }); const str = repo.feature('pear').value; expect(str).to.eq('now-set'); @@ -26,6 +26,7 @@ describe('repository reacts to single feature changes as expected', () => { expect(repo.feature('pear').version).to.eq(1); expect(repo.feature('pear').getString()).to.eq('now-set'); expect(repo.feature('pear').str).to.eq('now-set'); + expect(repo.feature('pear').featureProperties).to.deep.eq({ 'category': 'shoes', 'appName': 'cajon' }); }); it('should specify undefined for unknown feature values', () => { diff --git a/featurehub-javascript-client-sdk/test/ripple_feature_update_spec.ts b/featurehub-javascript-client-sdk/test/ripple_feature_update_spec.ts index 2f87eb7..eb5d0b0 100644 --- a/featurehub-javascript-client-sdk/test/ripple_feature_update_spec.ts +++ b/featurehub-javascript-client-sdk/test/ripple_feature_update_spec.ts @@ -16,23 +16,23 @@ import { RolloutStrategyAttributeConditional, RolloutStrategyFieldType, SSEResultState -} from "../app"; -import {Arg, Substitute, SubstituteOf} from "@fluffy-spoon/substitute"; -import {FeatureStateBaseHolder} from "../app/feature_state_holders"; -import {expect} from "chai"; -import {Applied, ApplyFeature} from "../app/strategy_matcher"; +} from '../app'; +import { Arg, Substitute, SubstituteOf } from '@fluffy-spoon/substitute'; +import { FeatureStateBaseHolder } from '../app/feature_state_holders'; +import { expect } from 'chai'; +import { Applied, ApplyFeature } from '../app/strategy_matcher'; class TestingContext extends BaseClientContext { build(): Promise { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } feature(name: string): FeatureStateHolder { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } close(): void { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } constructor(repository: InternalFeatureRepository) { @@ -44,11 +44,11 @@ class FakeInternalRepository implements InternalFeatureRepository { private applier = new ApplyFeature(); notReady(): void { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } notify(state: SSEResultState, data: any) { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } valueInterceptorMatched(key: string): InterceptorValueMatch { @@ -60,78 +60,78 @@ class FakeInternalRepository implements InternalFeatureRepository { } readyness: Readyness = Readyness.Ready; - catchAndReleaseMode: boolean = false; + catchAndReleaseMode = false; logAnalyticsEvent(action: string, other?: Map, ctx?: ClientContext) { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } hasFeature(key: string): FeatureStateHolder { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } feature(key: string): FeatureStateHolder { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } getFeatureState(key: string): FeatureStateHolder { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } release(disableCatchAndRelease?: boolean): Promise { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } simpleFeatures(): Map { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } getFlag(key: string): boolean { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } getString(key: string): string { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } getJson(key: string): string { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } getNumber(key: string): number { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } isSet(key: string): boolean { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } addValueInterceptor(interceptor: FeatureStateValueInterceptor): void { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } addReadynessListener(listener: ReadynessListener): number { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } addReadinessListener(listener: ReadynessListener, ignoreNotReadyOnRegister?: boolean): number { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } removeReadinessListener(listener: number | ReadynessListener) { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } addAnalyticCollector(collector: AnalyticsCollector): void { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } addPostLoadNewFeatureStateAvailableListener(listener: PostLoadNewFeatureStateAvailableListener): number { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } removePostLoadNewFeatureStateAvailableListener(listener: number | PostLoadNewFeatureStateAvailableListener) { - throw new Error("Method not implemented."); + throw new Error('Method not implemented.'); } } @@ -183,8 +183,8 @@ describe('When checking for listeners triggering on strategy changes', () => { expect(listener1Result).to.not.be.undefined; expect(listener2Result).to.not.be.undefined; - let l1Result = listener1Result as FeatureStateHolder; - let l2Result = listener2Result as FeatureStateHolder; + const l1Result = listener1Result as FeatureStateHolder; + const l2Result = listener2Result as FeatureStateHolder; expect(l1Result?.flag).to.be.false; expect(l2Result?.flag).to.be.true; expect(listener2TriggerCounter).to.eq(1); diff --git a/featurehub-javascript-node-sdk/package.json b/featurehub-javascript-node-sdk/package.json index 73209e9..e385ccd 100644 --- a/featurehub-javascript-node-sdk/package.json +++ b/featurehub-javascript-node-sdk/package.json @@ -37,17 +37,18 @@ "coverage": "nyc npm run test", "test:watch": "npm run mocha --opts mocha.opts --watch", "tsc": "node ./node_modules/typescript/bin/tsc -p", - "link": "npm link featurehub-javascript-client-sdk", + "setup": "npm link featurehub-javascript-client-sdk", + "link": "npm link", "compile": "npm run build && npm link", "release": "cp ../README.md . && npm run build && npm publish", "prepublishOnly": "npm run build", "lint": "./node_modules/.bin/eslint . --ext .js,.jsx,.ts,.tsx --fix" }, "devDependencies": { - "@fluffy-spoon/substitute": "^1.208.0", - "@types/chai": "^4.2.18", - "@types/mocha": "^9.0.0", - "@types/node": "^12.20.12", + "@fluffy-spoon/substitute": "^1.208.0", + "@types/chai": "^4.2.18", + "@types/mocha": "^9.0.0", + "@types/node": "^12.20.12", "chai": "^4.2.0", "mocha": "^9.2.0", "nyc": "^15.1.0", @@ -64,6 +65,6 @@ "featurehub-javascript-client-sdk": "^1.3.2" }, "engines": { - "node": ">=14.0.0" + "node": ">=14.0.0" } }