diff --git a/flagsmith-core.ts b/flagsmith-core.ts index 5558a62..5b93816 100644 --- a/flagsmith-core.ts +++ b/flagsmith-core.ts @@ -1,6 +1,7 @@ import { DynatraceObject, GetValueOptions, + HasFeatureOptions, IDatadogRum, IFlags, IFlagsmith, @@ -151,8 +152,8 @@ const Flagsmith = class { javaLongOrObject: {}, } Object.keys(this.flags).map((key) => { - setDynatraceValue(traits, FLAGSMITH_CONFIG_ANALYTICS_KEY + key, this.getValue(key, {}, true)) - setDynatraceValue(traits, FLAGSMITH_FLAG_ANALYTICS_KEY + key, this.hasFeature(key, true)) + setDynatraceValue(traits, FLAGSMITH_CONFIG_ANALYTICS_KEY + key, this.getValue(key, { skipAnalytics: true })) + setDynatraceValue(traits, FLAGSMITH_FLAG_ANALYTICS_KEY + key, this.hasFeature(key, { skipAnalytics: true })) }) Object.keys(this.traits).map((key) => { setDynatraceValue(traits, FLAGSMITH_TRAIT_ANALYTICS_KEY + key, this.getTrait(key)) @@ -588,7 +589,7 @@ const Flagsmith = class { res = flag.value; } - if (!skipAnalytics) { + if (!options?.skipAnalytics && !skipAnalytics) { this.evaluateFlag(key, "VALUE"); } @@ -656,13 +657,17 @@ const Flagsmith = class { } }; - hasFeature = (key: string, skipAnalytics?: boolean) => { + hasFeature = (key: string, options?: HasFeatureOptions) => { + // Support legacy skipAnalytics boolean parameter + const usingNewOptions = typeof options === 'object' const flag = this.flags && this.flags[key.toLowerCase().replace(/ /g, '_')]; let res = false; - if (flag && flag.enabled) { + if (!flag && usingNewOptions && typeof options.fallback !== 'undefined') { + res = options?.fallback + } else if (flag && flag.enabled) { res = true; } - if (!skipAnalytics) { + if ((usingNewOptions && !options.skipAnalytics) || !options) { this.evaluateFlag(key, "ENABLED"); } diff --git a/lib/flagsmith-es/package.json b/lib/flagsmith-es/package.json index 1a6d2a5..f4ae81a 100644 --- a/lib/flagsmith-es/package.json +++ b/lib/flagsmith-es/package.json @@ -1,6 +1,6 @@ { "name": "flagsmith-es", - "version": "4.0.3", + "version": "4.1.0", "description": "Feature flagging to support continuous development. This is an esm equivalent of the standard flagsmith npm module.", "main": "./index.js", "type": "module", diff --git a/lib/flagsmith/package.json b/lib/flagsmith/package.json index f8f502c..83e1a3a 100644 --- a/lib/flagsmith/package.json +++ b/lib/flagsmith/package.json @@ -1,6 +1,6 @@ { "name": "flagsmith", - "version": "4.0.3", + "version": "4.1.0", "description": "Feature flagging to support continuous development", "main": "./index.js", "repository": { diff --git a/lib/react-native-flagsmith/package.json b/lib/react-native-flagsmith/package.json index 9ba92dc..c88396a 100644 --- a/lib/react-native-flagsmith/package.json +++ b/lib/react-native-flagsmith/package.json @@ -1,6 +1,6 @@ { "name": "react-native-flagsmith", - "version": "4.0.3", + "version": "4.1.0", "description": "Feature flagging to support continuous development", "main": "./index.js", "repository": { diff --git a/test/functions.test.ts b/test/functions.test.ts new file mode 100644 index 0000000..2c5d41a --- /dev/null +++ b/test/functions.test.ts @@ -0,0 +1,22 @@ +// Sample test +import { getFlagsmith } from './test-constants'; + +describe('Flagsmith.functions', () => { + + beforeEach(() => { + // Avoid mocks, but if you need to add them here + }); + test('should use a fallback when the feature is undefined', async () => { + const onChange = jest.fn() + const {flagsmith,initConfig, AsyncStorage,mockFetch} = getFlagsmith({onChange}) + await flagsmith.init(initConfig); + + expect(flagsmith.getValue("deleted_feature",{fallback:"foo"})).toBe("foo"); + expect(flagsmith.hasFeature("deleted_feature",{fallback:true})).toBe(true); + expect(flagsmith.hasFeature("deleted_feature",{fallback:false})).toBe(false); + expect(flagsmith.hasFeature("font_size",{fallback:false})).toBe(true); + expect(flagsmith.getValue("font_size",{fallback:100})).toBe(16); + expect(flagsmith.getValue("font_size")).toBe(16); + expect(flagsmith.hasFeature("font_size")).toBe(true); + }) +}); diff --git a/types.d.ts b/types.d.ts index d3d7acf..4b21f32 100644 --- a/types.d.ts +++ b/types.d.ts @@ -17,10 +17,16 @@ export declare type IFlags = Record = Record; export declare type GetValueOptions | object> = { - json?: boolean; + skipAnalytics?: boolean + json?: boolean fallback?: T } +export declare type HasFeatureOptions = { + skipAnalytics?: boolean + fallback?: boolean +} | boolean + export interface IRetrieveInfo { isFromServer: boolean; @@ -149,14 +155,35 @@ export interface IFlagsmith void; /** - * Get the whether a flag is enabled e.g. flagsmith.hasFeature("powerUserFeature") - */ - hasFeature: (key: F) => boolean; - - /** - * Get the value of a particular remote config e.g. flagsmith.getValue("font_size") - */ - getValue(key: F, options?: GetValueOptions): IFlagsmithValue; + * Returns whether a feature is enabled, or a fallback value if it does not exist. + * @param {HasFeatureOptions} [optionsOrSkipAnalytics=false] If `true`, will not track analytics for this flag + * evaluation. Using a boolean for this parameter is deprecated - use `{ skipAnalytics: true }` instead. + * @param [optionsOrSkipAnalytics.fallback=false] Returns this value if the feature does not exist. + * @param [optionsOrSkipAnalytics.skipAnalytics=false] If `true`, do not track analytics for this feature evaluation. + * @example + * flagsmith.hasFeature("power_user_feature") + * @example + * flagsmith.hasFeature("enabled_by_default_feature", { fallback: true }) + */ + hasFeature: (key: F, optionsOrSkipAnalytics?: HasFeatureOptions) => boolean; + + /** + * Returns the value of a feature, or a fallback value. + * @param [options.json=false] Deserialise the feature value using `JSON.parse` and return the result or `options.fallback`. + * @param [options.fallback=null] Return this value in any of these cases: + * * The feature does not exist. + * * The feature has no value. + * * `options.json` is `true` and the feature's value is not valid JSON. + * @param [options.skipAnalytics=false] If `true`, do not track analytics for this feature evaluation. + * @param [skipAnalytics=false] Deprecated - use `options.skipAnalytics` instead. + * @example + * flagsmith.getValue("remote_config") // "{\"hello\":\"world\"}" + * flagsmith.getValue("remote_config", { json: true }) // { hello: "world" } + * @example + * flagsmith.getValue("font_size") // "12px" + * flagsmith.getValue("font_size", { json: true, fallback: "8px" }) // "8px" + */ + getValue(key: F, options?: GetValueOptions, skipAnalytics?: boolean): IFlagsmithValue; /** * Get the value of a particular trait for the identified user