diff --git a/src/dtos/types.ts b/src/dtos/types.ts index 4fdf562c..03363928 100644 --- a/src/dtos/types.ts +++ b/src/dtos/types.ts @@ -164,7 +164,7 @@ export interface ISplit { configurations?: { [treatmentName: string]: string }, - sets: string[] + sets?: string[] } // Split definition used in offline mode diff --git a/src/evaluator/__tests__/evaluate-features.spec.ts b/src/evaluator/__tests__/evaluate-features.spec.ts index c400c983..3b81a37b 100644 --- a/src/evaluator/__tests__/evaluate-features.spec.ts +++ b/src/evaluator/__tests__/evaluate-features.spec.ts @@ -1,7 +1,9 @@ // @ts-nocheck -import { evaluateFeatures } from '../index'; +import { evaluateFeatures, evaluateFeaturesByFlagSets } from '../index'; import * as LabelsConstants from '../../utils/labels'; import { loggerMock } from '../../logger/__tests__/sdkLogger.mock'; +import { _Set } from '../../utils/lang/sets'; +import { returnSetsUnion } from '../../utils/lang/sets'; const splitsMock = { regular: { 'changeNumber': 1487277320548, 'trafficAllocationSeed': 1667452163, 'trafficAllocation': 100, 'trafficTypeName': 'user', 'name': 'always-on', 'seed': 1684183541, 'configurations': {}, 'status': 'ACTIVE', 'killed': false, 'defaultTreatment': 'off', 'conditions': [{ 'conditionType': 'ROLLOUT', 'matcherGroup': { 'combiner': 'AND', 'matchers': [{ 'keySelector': { 'trafficType': 'user', 'attribute': '' }, 'matcherType': 'ALL_KEYS', 'negate': false, 'userDefinedSegmentMatcherData': { 'segmentName': '' }, 'unaryNumericMatcherData': { 'dataType': '', 'value': 0 }, 'whitelistMatcherData': { 'whitelist': null }, 'betweenMatcherData': { 'dataType': '', 'start': 0, 'end': 0 } }] }, 'partitions': [{ 'treatment': 'on', 'size': 100 }, { 'treatment': 'off', 'size': 0 }], 'label': 'in segment all' }] }, @@ -14,6 +16,11 @@ const splitsMock = { trafficAlocation1WithConfig: { 'changeNumber': 1487277320548, 'trafficAllocationSeed': -1667452163, 'trafficAllocation': 1, 'trafficTypeName': 'user', 'name': 'always-on6', 'seed': 1684183541, 'configurations': { 'off': "{color:'black'}" }, 'status': 'ACTIVE', 'killed': false, 'defaultTreatment': 'off', 'conditions': [{ 'conditionType': 'ROLLOUT', 'matcherGroup': { 'combiner': 'AND', 'matchers': [{ 'keySelector': { 'trafficType': 'user', 'attribute': '' }, 'matcherType': 'ALL_KEYS', 'negate': false, 'userDefinedSegmentMatcherData': { 'segmentName': '' }, 'unaryNumericMatcherData': { 'dataType': '', 'value': 0 }, 'whitelistMatcherData': { 'whitelist': null }, 'betweenMatcherData': { 'dataType': '', 'start': 0, 'end': 0 } }] }, 'partitions': [{ 'treatment': 'on', 'size': 100 }, { 'treatment': 'off', 'size': 0 }], 'label': 'in segment all' }] } }; +const flagSetsMock = { + reg_and_config: new _Set(['regular', 'config']), + arch_and_killed: new _Set(['killed', 'archived']), +}; + const mockStorage = { splits: { getSplit(name) { @@ -29,6 +36,16 @@ const mockStorage = { }); return splits; + }, + getNamesByFlagSets(flagSets) { + let toReturn = new _Set([]); + flagSets.forEach(flagset => { + const featureFlagNames = flagSetsMock[flagset]; + if (featureFlagNames) { + toReturn = returnSetsUnion(toReturn, featureFlagNames); + } + }); + return toReturn; } } }; @@ -105,3 +122,57 @@ test('EVALUATOR - Multiple evaluations at once / should return right labels, tre // If the split is retrieved but is not in split (out of Traffic Allocation), we should get the right evaluation result, label and config. }); + +test('EVALUATOR - Multiple evaluations at once by flag sets / should return right labels, treatments and configs if storage returns without errors.', async function () { + + const expectedOutput = { + config: { + treatment: 'on', label: 'in segment all', + config: '{color:\'black\'}', changeNumber: 1487277320548 + }, + not_existent_split: { + treatment: 'control', label: LabelsConstants.SPLIT_NOT_FOUND, config: null + }, + }; + + const getResultsByFlagsets = (flagSets: string[]) => { + return evaluateFeaturesByFlagSets( + loggerMock, + 'fake-key', + flagSets, + null, + mockStorage, + ); + }; + + + + let multipleEvaluationAtOnceByFlagSets = await getResultsByFlagsets(['reg_and_config', 'arch_and_killed']); + + // assert evaluationWithConfig + expect(multipleEvaluationAtOnceByFlagSets['config']).toEqual(expectedOutput['config']); // If the split is retrieved successfully we should get the right evaluation result, label and config. + // @todo assert flag set not found - for input validations + + // assert regular + expect(multipleEvaluationAtOnceByFlagSets['regular']).toEqual({ ...expectedOutput['config'], config: null }); // If the split is retrieved successfully we should get the right evaluation result, label and config. If Split has no config it should have config equal null. + // assert killed + expect(multipleEvaluationAtOnceByFlagSets['killed']).toEqual({ ...expectedOutput['config'], treatment: 'off', config: null, label: LabelsConstants.SPLIT_KILLED }); + // 'If the split is retrieved but is killed, we should get the right evaluation result, label and config. + + // assert archived + expect(multipleEvaluationAtOnceByFlagSets['archived']).toEqual({ ...expectedOutput['config'], treatment: 'control', label: LabelsConstants.SPLIT_ARCHIVED, config: null }); + // If the split is retrieved but is archived, we should get the right evaluation result, label and config. + + // assert not_existent_split not in evaluation if it is not related to defined flag sets + expect(multipleEvaluationAtOnceByFlagSets['not_existent_split']).toEqual(undefined); + + multipleEvaluationAtOnceByFlagSets = await getResultsByFlagsets([]); + expect(multipleEvaluationAtOnceByFlagSets).toEqual({}); + + multipleEvaluationAtOnceByFlagSets = await getResultsByFlagsets(['reg_and_config']); + expect(multipleEvaluationAtOnceByFlagSets['config']).toEqual(expectedOutput['config']); + expect(multipleEvaluationAtOnceByFlagSets['regular']).toEqual({ ...expectedOutput['config'], config: null }); + expect(multipleEvaluationAtOnceByFlagSets['killed']).toEqual(undefined); + expect(multipleEvaluationAtOnceByFlagSets['archived']).toEqual(undefined); + +}); diff --git a/src/evaluator/index.ts b/src/evaluator/index.ts index dad4c9e4..70dad68a 100644 --- a/src/evaluator/index.ts +++ b/src/evaluator/index.ts @@ -7,6 +7,7 @@ import { IStorageAsync, IStorageSync } from '../storages/types'; import { IEvaluationResult } from './types'; import { SplitIO } from '../types'; import { ILogger } from '../logger/types'; +import { ISet, setToArray } from '../utils/lang/sets'; const treatmentException = { treatment: CONTROL, @@ -87,6 +88,29 @@ export function evaluateFeatures( getEvaluations(log, splitNames, parsedSplits, key, attributes, storage); } +export function evaluateFeaturesByFlagSets( + log: ILogger, + key: SplitIO.SplitKey, + flagSets: string[], + attributes: SplitIO.Attributes | undefined, + storage: IStorageSync | IStorageAsync, +): MaybeThenable> { + let storedFlagNames: MaybeThenable>; + + // get features by flag sets + try { + storedFlagNames = storage.splits.getNamesByFlagSets(flagSets); + } catch (e) { + // return empty evaluations + return {}; + } + + // evaluate related features + return thenable(storedFlagNames) ? + storedFlagNames.then((splitNames) => evaluateFeatures(log, key, setToArray(splitNames), attributes, storage)) : + evaluateFeatures(log, key, setToArray(storedFlagNames), attributes, storage); +} + function getEvaluation( log: ILogger, splitJSON: ISplit | null, diff --git a/src/sdkClient/__tests__/clientAttributesDecoration.spec.ts b/src/sdkClient/__tests__/clientAttributesDecoration.spec.ts index 45fe70ff..e007dc54 100644 --- a/src/sdkClient/__tests__/clientAttributesDecoration.spec.ts +++ b/src/sdkClient/__tests__/clientAttributesDecoration.spec.ts @@ -14,6 +14,22 @@ const clientMock = { }, getTreatmentsWithConfig(maybeKey: any, maybeFeatureFlagNames: string[], maybeAttributes?: any) { return maybeAttributes; + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getTreatmentsByFlagSets(maybeKey: any, maybeFeatureFlagNames: string[], maybeAttributes?: any, maybeFlagSetNames?: string[] | undefined) { + return maybeAttributes; + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getTreatmentsWithConfigByFlagSets(maybeKey: any, maybeFeatureFlagNames: string[], maybeAttributes?: any, maybeFlagSetNames?: string[] | undefined) { + return maybeAttributes; + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getTreatmentsByFlagSet(maybeKey: any, maybeFeatureFlagNames: string[], maybeAttributes?: any, maybeFlagSetName?: string | undefined) { + return maybeAttributes; + }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + getTreatmentsWithConfigByFlagSet(maybeKey: any, maybeFeatureFlagNames: string[], maybeAttributes?: any, maybeFlagSetName?: string | undefined) { + return maybeAttributes; } }; // @ts-expect-error @@ -225,4 +241,92 @@ describe('ATTRIBUTES DECORATION / evaluation', () => { }); + test('Evaluation attributes logic and precedence / getTreatmentsByFlagSets', () => { + + // If the same attribute is “cached” and provided on the function, the value received on the function call takes precedence. + expect(client.getTreatmentsByFlagSets('key', ['set'])).toEqual(undefined); // Nothing changes if no attributes were provided using the new api + expect(client.getTreatmentsByFlagSets('key', ['set'], { func_attr_bool: true, func_attr_str: 'true' })).toEqual({ func_attr_bool: true, func_attr_str: 'true' }); // Nothing changes if no attributes were provided using the new api + expect(client.getAttributes()).toEqual({}); // Attributes in memory storage must be empty + client.setAttribute('func_attr_bool', false); + expect(client.getAttributes()).toEqual({ 'func_attr_bool': false }); // In memory attribute storage must have the unique stored attribute + expect(client.getTreatmentsByFlagSets('key', ['set'], { func_attr_bool: true, func_attr_str: 'true' })).toEqual({ func_attr_bool: true, func_attr_str: 'true' }); // Function attributes has precedence against api ones + // @ts-ignore + expect(client.getTreatmentsByFlagSets('key', ['set'], null)).toEqual({ func_attr_bool: false }); // API attributes should be kept in memory and use for evaluations + expect(client.getTreatmentsByFlagSets('key', ['set'], { func_attr_str: 'true' })).toEqual({ func_attr_bool: false, func_attr_str: 'true' }); // API attributes should be kept in memory and use for evaluations + client.setAttributes({ func_attr_str: 'false' }); + expect(client.getAttributes()).toEqual({ 'func_attr_bool': false, 'func_attr_str': 'false' }); // In memory attribute storage must have two stored attributes + expect(client.getTreatmentsByFlagSets('key', ['set'], { func_attr_bool: true, func_attr_str: 'true', func_attr_number: 1 })).toEqual({ func_attr_bool: true, func_attr_str: 'true', func_attr_number: 1 }); // Function attributes has precedence against api ones + // @ts-ignore + expect(client.getTreatmentsByFlagSets('key', ['set'], null)).toEqual({ func_attr_bool: false, func_attr_str: 'false' }); // If the getTreatment function is called without attributes, stored attributes will be used to evaluate. + expect(client.getTreatmentsByFlagSets('key', ['set'])).toEqual({ func_attr_bool: false, func_attr_str: 'false' }); // If the getTreatment function is called without attributes, stored attributes will be used to evaluate. + client.clearAttributes(); + + }); + + test('Evaluation attributes logic and precedence / getTreatmentsWithConfigByFlagSets', () => { + + // If the same attribute is “cached” and provided on the function, the value received on the function call takes precedence. + expect(client.getTreatmentsWithConfigByFlagSets('key', ['set'])).toEqual(undefined); // Nothing changes if no attributes were provided using the new api + expect(client.getTreatmentsWithConfigByFlagSets('key', ['set'], { func_attr_bool: true, func_attr_str: 'true' })).toEqual({ func_attr_bool: true, func_attr_str: 'true' }); // Nothing changes if no attributes were provided using the new api + expect(client.getAttributes()).toEqual({}); // Attributes in memory storage must be empty + client.setAttribute('func_attr_bool', false); + expect(client.getAttributes()).toEqual({ 'func_attr_bool': false }); // In memory attribute storage must have the unique stored attribute + expect(client.getTreatmentsWithConfigByFlagSets('key', ['set'], { func_attr_bool: true, func_attr_str: 'true' })).toEqual({ func_attr_bool: true, func_attr_str: 'true' }); // Function attributes has precedence against api ones + // @ts-ignore + expect(client.getTreatmentsWithConfigByFlagSets('key', ['set'], null)).toEqual({ func_attr_bool: false }); // API attributes should be kept in memory and use for evaluations + expect(client.getTreatmentsWithConfigByFlagSets('key', ['set'], { func_attr_str: 'true' })).toEqual({ func_attr_bool: false, func_attr_str: 'true' }); // API attributes should be kept in memory and use for evaluations + client.setAttributes({ func_attr_str: 'false' }); + expect(client.getAttributes()).toEqual({ 'func_attr_bool': false, 'func_attr_str': 'false' }); // In memory attribute storage must have two stored attributes + expect(client.getTreatmentsWithConfigByFlagSets('key', ['set'], { func_attr_bool: true, func_attr_str: 'true', func_attr_number: 1 })).toEqual({ func_attr_bool: true, func_attr_str: 'true', func_attr_number: 1 }); // Function attributes has precedence against api ones + // @ts-ignore + expect(client.getTreatmentsWithConfigByFlagSets('key', ['set'], null)).toEqual({ func_attr_bool: false, func_attr_str: 'false' }); // If the getTreatment function is called without attributes, stored attributes will be used to evaluate. + expect(client.getTreatmentsWithConfigByFlagSets('key', ['set'])).toEqual({ func_attr_bool: false, func_attr_str: 'false' }); // If the getTreatment function is called without attributes, stored attributes will be used to evaluate. + client.clearAttributes(); + + }); + + test('Evaluation attributes logic and precedence / getTreatmentsByFlagSet', () => { + + // If the same attribute is “cached” and provided on the function, the value received on the function call takes precedence. + expect(client.getTreatmentsByFlagSet('key', 'set')).toEqual(undefined); // Nothing changes if no attributes were provided using the new api + expect(client.getTreatmentsByFlagSet('key', 'set', { func_attr_bool: true, func_attr_str: 'true' })).toEqual({ func_attr_bool: true, func_attr_str: 'true' }); // Nothing changes if no attributes were provided using the new api + expect(client.getAttributes()).toEqual({}); // Attributes in memory storage must be empty + client.setAttribute('func_attr_bool', false); + expect(client.getAttributes()).toEqual({ 'func_attr_bool': false }); // In memory attribute storage must have the unique stored attribute + expect(client.getTreatmentsByFlagSet('key', 'set', { func_attr_bool: true, func_attr_str: 'true' })).toEqual({ func_attr_bool: true, func_attr_str: 'true' }); // Function attributes has precedence against api ones + // @ts-ignore + expect(client.getTreatmentsByFlagSet('key', 'set', null)).toEqual({ func_attr_bool: false }); // API attributes should be kept in memory and use for evaluations + expect(client.getTreatmentsByFlagSet('key', 'set', { func_attr_str: 'true' })).toEqual({ func_attr_bool: false, func_attr_str: 'true' }); // API attributes should be kept in memory and use for evaluations + client.setAttributes({ func_attr_str: 'false' }); + expect(client.getAttributes()).toEqual({ 'func_attr_bool': false, 'func_attr_str': 'false' }); // In memory attribute storage must have two stored attributes + expect(client.getTreatmentsByFlagSet('key', 'set', { func_attr_bool: true, func_attr_str: 'true', func_attr_number: 1 })).toEqual({ func_attr_bool: true, func_attr_str: 'true', func_attr_number: 1 }); // Function attributes has precedence against api ones + // @ts-ignore + expect(client.getTreatmentsByFlagSet('key', 'set', null)).toEqual({ func_attr_bool: false, func_attr_str: 'false' }); // If the getTreatment function is called without attributes, stored attributes will be used to evaluate. + expect(client.getTreatmentsByFlagSet('key', 'set')).toEqual({ func_attr_bool: false, func_attr_str: 'false' }); // If the getTreatment function is called without attributes, stored attributes will be used to evaluate. + client.clearAttributes(); + + }); + + test('Evaluation attributes logic and precedence / getTreatmentsWithConfigByFlagSet', () => { + + // If the same attribute is “cached” and provided on the function, the value received on the function call takes precedence. + expect(client.getTreatmentsWithConfigByFlagSet('key', 'set')).toEqual(undefined); // Nothing changes if no attributes were provided using the new api + expect(client.getTreatmentsWithConfigByFlagSet('key', 'set', { func_attr_bool: true, func_attr_str: 'true' })).toEqual({ func_attr_bool: true, func_attr_str: 'true' }); // Nothing changes if no attributes were provided using the new api + expect(client.getAttributes()).toEqual({}); // Attributes in memory storage must be empty + client.setAttribute('func_attr_bool', false); + expect(client.getAttributes()).toEqual({ 'func_attr_bool': false }); // In memory attribute storage must have the unique stored attribute + expect(client.getTreatmentsWithConfigByFlagSet('key', 'set', { func_attr_bool: true, func_attr_str: 'true' })).toEqual({ func_attr_bool: true, func_attr_str: 'true' }); // Function attributes has precedence against api ones + // @ts-ignore + expect(client.getTreatmentsWithConfigByFlagSet('key', 'set', null)).toEqual({ func_attr_bool: false }); // API attributes should be kept in memory and use for evaluations + expect(client.getTreatmentsWithConfigByFlagSet('key', 'set', { func_attr_str: 'true' })).toEqual({ func_attr_bool: false, func_attr_str: 'true' }); // API attributes should be kept in memory and use for evaluations + client.setAttributes({ func_attr_str: 'false' }); + expect(client.getAttributes()).toEqual({ 'func_attr_bool': false, 'func_attr_str': 'false' }); // In memory attribute storage must have two stored attributes + expect(client.getTreatmentsWithConfigByFlagSet('key', 'set', { func_attr_bool: true, func_attr_str: 'true', func_attr_number: 1 })).toEqual({ func_attr_bool: true, func_attr_str: 'true', func_attr_number: 1 }); // Function attributes has precedence against api ones + // @ts-ignore + expect(client.getTreatmentsWithConfigByFlagSet('key', 'set', null)).toEqual({ func_attr_bool: false, func_attr_str: 'false' }); // If the getTreatment function is called without attributes, stored attributes will be used to evaluate. + expect(client.getTreatmentsWithConfigByFlagSet('key', 'set')).toEqual({ func_attr_bool: false, func_attr_str: 'false' }); // If the getTreatment function is called without attributes, stored attributes will be used to evaluate. + client.clearAttributes(); + + }); + }); diff --git a/src/sdkClient/__tests__/testUtils.ts b/src/sdkClient/__tests__/testUtils.ts index 30ee5966..901897e3 100644 --- a/src/sdkClient/__tests__/testUtils.ts +++ b/src/sdkClient/__tests__/testUtils.ts @@ -1,4 +1,4 @@ -const clientApiMethods = ['getTreatment', 'getTreatments', 'getTreatmentWithConfig', 'getTreatmentsWithConfig', 'track', 'destroy']; +const clientApiMethods = ['getTreatment', 'getTreatments', 'getTreatmentWithConfig', 'getTreatmentsWithConfig', 'getTreatmentsByFlagSets', 'getTreatmentsWithConfigByFlagSets', 'getTreatmentsByFlagSet', 'getTreatmentsWithConfigByFlagSet', 'track', 'destroy']; export function assertClientApi(client: any, sdkStatus?: object) { diff --git a/src/sdkClient/client.ts b/src/sdkClient/client.ts index 11036519..31aa5f74 100644 --- a/src/sdkClient/client.ts +++ b/src/sdkClient/client.ts @@ -1,15 +1,16 @@ -import { evaluateFeature, evaluateFeatures } from '../evaluator'; +import { evaluateFeature, evaluateFeatures, evaluateFeaturesByFlagSets } from '../evaluator'; import { thenable } from '../utils/promise/thenable'; import { getMatching, getBucketing } from '../utils/key'; import { validateSplitExistance } from '../utils/inputValidation/splitExistance'; import { validateTrafficTypeExistance } from '../utils/inputValidation/trafficTypeExistance'; import { SDK_NOT_READY } from '../utils/labels'; -import { CONTROL, TREATMENT, TREATMENTS, TREATMENT_WITH_CONFIG, TREATMENTS_WITH_CONFIG, TRACK } from '../utils/constants'; +import { CONTROL, TREATMENT, TREATMENTS, TREATMENT_WITH_CONFIG, TREATMENTS_WITH_CONFIG, TRACK, TREATMENTS_WITH_CONFIG_BY_FLAGSETS, TREATMENTS_BY_FLAGSETS, TREATMENTS_BY_FLAGSET, TREATMENTS_WITH_CONFIG_BY_FLAGSET } from '../utils/constants'; import { IEvaluationResult } from '../evaluator/types'; import { SplitIO, ImpressionDTO } from '../types'; import { IMPRESSION, IMPRESSION_QUEUEING } from '../logger/constants'; import { ISdkFactoryContext } from '../sdkFactory/types'; import { isStorageSync } from '../trackers/impressionObserver/utils'; +import { Method } from '../sync/submitters/types'; const treatmentNotReady = { treatment: CONTROL, label: SDK_NOT_READY }; @@ -81,6 +82,41 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl return getTreatments(key, featureFlagNames, attributes, true); } + function getTreatmentsByFlagSets(key: SplitIO.SplitKey, flagSetNames: string[], attributes: SplitIO.Attributes | undefined, withConfig = false, method: Method = TREATMENTS_BY_FLAGSETS) { + const stopTelemetryTracker = telemetryTracker.trackEval(method); + + const wrapUp = (evaluationResults: Record) => { + const queue: ImpressionDTO[] = []; + const treatments: Record = {}; + const evaluations = evaluationResults; + Object.keys(evaluations).forEach(featureFlagName => { + treatments[featureFlagName] = processEvaluation(evaluations[featureFlagName], featureFlagName, key, attributes, withConfig, `getTreatmentsByFlagSets${withConfig ? 'WithConfig' : ''}`, queue); + }); + impressionsTracker.track(queue, attributes); + + stopTelemetryTracker(queue[0] && queue[0].label); + return treatments; + }; + + const evaluations = readinessManager.isReady() || readinessManager.isReadyFromCache() ? + evaluateFeaturesByFlagSets(log, key, flagSetNames, attributes, storage) : + isStorageSync(settings) ? {} : Promise.resolve({}); // Promisify if async + + return thenable(evaluations) ? evaluations.then((res) => wrapUp(res)) : wrapUp(evaluations); + } + + function getTreatmentsWithConfigByFlagSets(key: SplitIO.SplitKey, flagSetNames: string[], attributes: SplitIO.Attributes | undefined) { + return getTreatmentsByFlagSets(key, flagSetNames, attributes, true, TREATMENTS_WITH_CONFIG_BY_FLAGSETS); + } + + function getTreatmentsByFlagSet(key: SplitIO.SplitKey, flagSetName: string, attributes: SplitIO.Attributes | undefined) { + return getTreatmentsByFlagSets(key, [flagSetName], attributes, false, TREATMENTS_BY_FLAGSET); + } + + function getTreatmentsWithConfigByFlagSet(key: SplitIO.SplitKey, flagSetName: string, attributes: SplitIO.Attributes | undefined) { + return getTreatmentsByFlagSets(key, [flagSetName], attributes, true, TREATMENTS_WITH_CONFIG_BY_FLAGSET); + } + // Internal function function processEvaluation( evaluation: IEvaluationResult, @@ -155,6 +191,10 @@ export function clientFactory(params: ISdkFactoryContext): SplitIO.IClient | Spl getTreatmentWithConfig, getTreatments, getTreatmentsWithConfig, + getTreatmentsByFlagSets, + getTreatmentsWithConfigByFlagSets, + getTreatmentsByFlagSet, + getTreatmentsWithConfigByFlagSet, track, isClientSide: false } as SplitIO.IClient | SplitIO.IAsyncClient; diff --git a/src/sdkClient/clientAttributesDecoration.ts b/src/sdkClient/clientAttributesDecoration.ts index 6db04e3e..57413ad9 100644 --- a/src/sdkClient/clientAttributesDecoration.ts +++ b/src/sdkClient/clientAttributesDecoration.ts @@ -16,6 +16,10 @@ export function clientAttributesDecoration = { ts: 'treatments', tc: 'treatmentWithConfig', tcs: 'treatmentsWithConfig', + tf: 'treatmentsByFlagSet', + tfs: 'treatmentsByFlagSets', + tcf: 'treatmentsWithConfigByFlagSet', + tcfs: 'treatmentsWithConfigByFlagSets', tr: 'track' }; diff --git a/src/sync/submitters/types.ts b/src/sync/submitters/types.ts index 5b7ff0c4..440f466a 100644 --- a/src/sync/submitters/types.ts +++ b/src/sync/submitters/types.ts @@ -124,7 +124,11 @@ export type TREATMENTS = 'ts'; export type TREATMENT_WITH_CONFIG = 'tc'; export type TREATMENTS_WITH_CONFIG = 'tcs'; export type TRACK = 'tr'; -export type Method = TREATMENT | TREATMENTS | TREATMENT_WITH_CONFIG | TREATMENTS_WITH_CONFIG | TRACK; +export type TREATMENTS_BY_FLAGSET = 'tf' +export type TREATMENTS_BY_FLAGSETS = 'tfs' +export type TREATMENTS_WITH_CONFIG_BY_FLAGSET = 'tcf' +export type TREATMENTS_WITH_CONFIG_BY_FLAGSETS = 'tcfs' +export type Method = TREATMENT | TREATMENTS | TREATMENT_WITH_CONFIG | TREATMENTS_WITH_CONFIG | TRACK | TREATMENTS_BY_FLAGSET | TREATMENTS_BY_FLAGSETS | TREATMENTS_WITH_CONFIG_BY_FLAGSET | TREATMENTS_WITH_CONFIG_BY_FLAGSETS; export type MethodLatencies = Partial>>; diff --git a/src/types.ts b/src/types.ts index 1ed3100d..2fb862c8 100644 --- a/src/types.ts +++ b/src/types.ts @@ -614,7 +614,7 @@ export namespace SplitIO { * list of sets per feature flag * @property {string[]} sets */ - sets: string[] + sets?: string[] }; /** * A promise that resolves to a feature flag view. @@ -1064,6 +1064,42 @@ export namespace SplitIO { * @returns {TreatmentsWithConfig} The map with all the TreatmentWithConfig objects */ getTreatmentsWithConfig(key: SplitKey, featureFlagNames: string[], attributes?: Attributes): TreatmentsWithConfig, + /** + * Returns a Treatments value, which is an object map with the treatments for the feature flags related to the given flagSet. + * @function getTreatmentsByFlagSet + * @param {string} key - The string key representing the consumer. + * @param {string} flagSet - The flagSet name we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsByFlagSet(key: SplitKey, flagSet: string, attributes?: Attributes): Treatments, + /** + * Returns a TreatmentsWithConfig value, which is an object map with the TreatmentWithConfig (an object with both treatment and config string) for the feature flags related to the given flagSets. + * @function getTreatmentsWithConfigByFlagSet + * @param {string} key - The string key representing the consumer. + * @param {string} flagSet - The flagSet name we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsWithConfigByFlagSet(key: SplitKey, flagSet: string, attributes?: Attributes): TreatmentsWithConfig, + /** + * Returns a Returns a Treatments value, which is an object with both treatment and config string for to the feature flags related to the given flagSets. + * @function getTreatmentsByFlagSets + * @param {string} key - The string key representing the consumer. + * @param {Array} flagSets - An array of the flagSet names we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsByFlagSets(key: SplitKey, flagSets: string[], attributes?: Attributes): Treatments, + /** + * Returns a TreatmentsWithConfig value, which is an object map with the TreatmentWithConfig (an object with both treatment and config string) for the feature flags related to the given flagSets. + * @function getTreatmentsWithConfigByFlagSets + * @param {string} key - The string key representing the consumer. + * @param {Array} flagSets - An array of the flagSet names we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsWithConfigByFlagSets(key: SplitKey, flagSets: string[], attributes?: Attributes): TreatmentsWithConfig, /** * Tracks an event to be fed to the results product on Split user interface. * @function track @@ -1124,6 +1160,46 @@ export namespace SplitIO { * @returns {AsyncTreatmentsWithConfig} TreatmentsWithConfig promise that resolves to the map of TreatmentsWithConfig objects. */ getTreatmentsWithConfig(key: SplitKey, featureFlagNames: string[], attributes?: Attributes): AsyncTreatmentsWithConfig, + /** + * Returns a Treatments value, which will be (or eventually be) an object map with the treatments for the features related to the given flag set. + * For usage on NodeJS as we don't have only one key. + * @function getTreatmentsByFlagSet + * @param {string} key - The string key representing the consumer. + * @param {string} flagSet - The flag set name we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsByFlagSet(key: SplitKey, flagSet: string, attributes?: Attributes): AsyncTreatments, + /** + * Returns a TreatmentWithConfig value, which will be (or eventually be) an object with both treatment and config string for features related to the given flag set. + * For usage on NodeJS as we don't have only one key. + * @function getTreatmentsWithConfigByFlagSet + * @param {string} key - The string key representing the consumer. + * @param {string} flagSet - The flag set name we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsWithConfigByFlagSet(key: SplitKey, flagSet: string, attributes?: Attributes): AsyncTreatmentsWithConfig, + /** + * Returns a Treatments value, which will be (or eventually be) an object map with the treatments for the feature flags related to the given flag sets. + * For usage on NodeJS as we don't have only one key. + * @function getTreatmentsByFlagSets + * @param {string} key - The string key representing the consumer. + * @param {Array} flagSets - An array of the flag set names we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsByFlagSets(key: SplitKey, flagSets: string[], attributes?: Attributes): AsyncTreatments, + /** + * Returns a TreatmentWithConfig value, which will be (or eventually be) an object with both treatment and config string for the feature flags related to the given flag sets. + * For usage on NodeJS as we don't have only one key. + * @function getTreatmentsWithConfigByFlagSets + * @param {string} key - The string key representing the consumer. + * @param {Array} flagSets - An array of the flag set names we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsWithConfigByFlagSets(key: SplitKey, flagSets: string[], attributes?: Attributes): AsyncTreatmentsWithConfig, /** * Tracks an event to be fed to the results product on Split user interface, and returns a promise to signal when the event was successfully queued (or not). * @function track @@ -1174,6 +1250,42 @@ export namespace SplitIO { * @returns {TreatmentsWithConfig} The map with all the TreatmentWithConfig objects */ getTreatmentsWithConfig(featureFlagNames: string[], attributes?: Attributes): TreatmentsWithConfig, + /** + * Returns a Treatments value, which is an object map with the treatments for the feature flags related to the given flag set. + * @function getTreatmentsByFlagSet + * @param {string} key - The string key representing the consumer. + * @param {string} flagSet - The flag set name we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsByFlagSet(key: SplitKey, flagSet: string, attributes?: Attributes): Treatments, + /** + * Returns a TreatmentsWithConfig value, which is an object map with the TreatmentWithConfig (an object with both treatment and config string) for the feature flags related to the given flag set. + * @function getTreatmentsWithConfigByFlagSet + * @param {string} key - The string key representing the consumer. + * @param {string} flagSet - The flag set name we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsWithConfigByFlagSet(key: SplitKey, flagSet: string, attributes?: Attributes): TreatmentsWithConfig, + /** + * Returns a Returns a Treatments value, which is an object with both treatment and config string for to the feature flags related to the given flag sets. + * @function getTreatmentsByFlagSets + * @param {string} key - The string key representing the consumer. + * @param {Array} flagSets - An array of the flag set names we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsByFlagSets(key: SplitKey, flagSets: string[], attributes?: Attributes): Treatments, + /** + * Returns a TreatmentsWithConfig value, which is an object map with the TreatmentWithConfig (an object with both treatment and config string) for the feature flags related to the given flag sets. + * @function getTreatmentsWithConfigByFlagSets + * @param {string} key - The string key representing the consumer. + * @param {Array} flagSets - An array of the flag set names we want to get the treatments. + * @param {Attributes=} attributes - An object of type Attributes defining the attributes for the given key. + * @returns {Treatments} The map with all the TreatmentWithConfig objects + */ + getTreatmentsWithConfigByFlagSets(key: SplitKey, flagSets: string[], attributes?: Attributes): TreatmentsWithConfig, /** * Tracks an event to be fed to the results product on Split user interface. * @function track diff --git a/src/utils/constants/index.ts b/src/utils/constants/index.ts index c55984a3..18dc1ccb 100644 --- a/src/utils/constants/index.ts +++ b/src/utils/constants/index.ts @@ -65,6 +65,10 @@ export const TREATMENT = 't'; export const TREATMENTS = 'ts'; export const TREATMENT_WITH_CONFIG = 'tc'; export const TREATMENTS_WITH_CONFIG = 'tcs'; +export const TREATMENTS_BY_FLAGSET = 'tf'; +export const TREATMENTS_BY_FLAGSETS = 'tfs'; +export const TREATMENTS_WITH_CONFIG_BY_FLAGSET = 'tcf'; +export const TREATMENTS_WITH_CONFIG_BY_FLAGSETS = 'tcfs'; export const TRACK = 'tr'; export const CONNECTION_ESTABLISHED = 0;