Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/services/__tests__/splitApi.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ describe('splitApi', () => {

function expectedConfigsUrl(since: number, till: number, usesFilter: boolean, settings: ISettings, rbSince?: number) {
const filterQueryString = settings.sync.__splitFiltersValidation && settings.sync.__splitFiltersValidation.queryString;
return `sdk/configs?${settings.sync.flagSpecVersion ? `s=${settings.sync.flagSpecVersion}&` : ''}since=${since}${rbSince ? '&rbSince=' + rbSince : ''}${usesFilter ? filterQueryString : ''}${till ? '&till=' + till : ''}`;
return `sdk/v1/configs?${settings.sync.flagSpecVersion ? `s=${settings.sync.flagSpecVersion}&` : ''}since=${since}${rbSince ? '&rbSince=' + rbSince : ''}${usesFilter ? filterQueryString : ''}${till ? '&till=' + till : ''}`;
}
});

Expand Down
2 changes: 1 addition & 1 deletion src/services/splitApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function splitApiFactory(
},

fetchConfigs(since: number, noCache?: boolean, till?: number, rbSince?: number) {
const url = `${urls.sdk}/configs?${settings.sync.flagSpecVersion ? `s=${settings.sync.flagSpecVersion}&` : ''}since=${since}${rbSince ? '&rbSince=' + rbSince : ''}${filterQueryString || ''}${till ? '&till=' + till : ''}`;
const url = `${urls.sdk}/v1/configs?${settings.sync.flagSpecVersion ? `s=${settings.sync.flagSpecVersion}&` : ''}since=${since}${rbSince ? '&rbSince=' + rbSince : ''}${filterQueryString || ''}${till ? '&till=' + till : ''}`;
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(CONFIGS));
},

Expand Down
4 changes: 2 additions & 2 deletions src/sync/polling/fetchers/__tests__/configsFetcher.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ const INPUT: IConfigsResponse = {
since: 100,
till: 200,
updated: [{
name: 'SomeConfig1',
identifier: 'SomeConfig1',
variants: [{ name: 'v1', definition: { prop1: true, prop2: 123 } }, { name: 'v2', definition: { prop1: false, prop2: 456 } }],
changeNumber: 0,
targeting: { default: 'v2', conditions: [{ partitions: [{ variant: 'v1', size: 100 }], label: 'main condition', matchers: [{ type: 'IS_EQUAL_TO', data: { type: 'NUMBER', number: 42 }, attribute: 'age' }, { type: 'WHITELIST', data: { strings: ['a', 'b', 'c'] }, attribute: 'favoriteCharacter' }] }] }
targeting: { default: 'v2', conditions: [{ partitions: [{ variant: 'v1', size: 100 }], label: 'main condition', matchers: [{ type: 'EQUAL_TO', data: { type: 'NUMBER', number: 42 }, attribute: 'age' }, { type: 'WHITELIST', data: { strings: ['a', 'b', 'c'] }, attribute: 'favoriteCharacter' }] }] }
}],
};

Expand Down
200 changes: 171 additions & 29 deletions src/sync/polling/fetchers/configsFetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,154 @@ import { IDefinitionChangesFetcher } from './types';
import SplitIO from '../../../../types/splitio';
import { ISdkFactoryContextSync } from '../../../sdkFactory/types';

type IConfigMatcher = {
type: 'IS_EQUAL_TO';
data: { type: 'NUMBER'; number: number };
attribute?: string;
} | {
type: 'WHITELIST';
type IConfigMatcherDataType = 'DATETIME' | 'NUMBER'

interface IConfigMatcherBase {
type: string;
attribute?: string | null;
data?:
{ type?: IConfigMatcherDataType; number: number } |
{ type?: IConfigMatcherDataType; start: number; end: number } |
{ strings: string[] } |
{ name: string } |
{ config: string; variants: string[] } |
{ value: boolean } |
{ string: string } |
{ start: string; end: string }
}

interface IAllKeysConfigMatcher extends IConfigMatcherBase {
type: 'ALL_KEYS'
}

interface IWhitelistConfigMatcher extends IConfigMatcherBase {
type: 'WHITELIST',
data: { strings: string[] }
}

interface IEqualToConfigMatcher extends IConfigMatcherBase {
type: 'EQUAL_TO';
data: { type?: IConfigMatcherDataType; number: number };
}

interface IGreaterThanOrEqualToConfigMatcher extends IConfigMatcherBase {
type: 'GREATER_THAN_OR_EQUAL_TO';
data: { type?: IConfigMatcherDataType; number: number };
}

interface ILessThanOrEqualToConfigMatcher extends IConfigMatcherBase {
type: 'LESS_THAN_OR_EQUAL_TO';
data: { type?: IConfigMatcherDataType; number: number };
}

interface IBetweenConfigMatcher extends IConfigMatcherBase {
type: 'BETWEEN';
data: { type?: IConfigMatcherDataType; start: number; end: number };
}

interface IInSegmentConfigMatcher extends IConfigMatcherBase {
type: 'IN_SEGMENT';
data: { name: string };
}

interface IInRBSegmentConfigMatcher extends IConfigMatcherBase {
type: 'IN_RULE_BASED_SEGMENT';
data: { name: string };
}

interface IInLargeSegmentConfigMatcher extends IConfigMatcherBase {
type: 'IN_LARGE_SEGMENT';
data: { name: string };
}

interface IEqualToSetConfigMatcher extends IConfigMatcherBase {
type: 'EQUAL_TO_SET';
data: { strings: string[] };
}

interface IContainsAnyOfSetConfigMatcher extends IConfigMatcherBase {
type: 'CONTAINS_ANY_OF_SET';
data: { strings: string[] };
}

interface IContainsAllOfSetConfigMatcher extends IConfigMatcherBase {
type: 'CONTAINS_ALL_OF_SET';
data: { strings: string[] };
}

interface IPartOfSetConfigMatcher extends IConfigMatcherBase {
type: 'PART_OF_SET';
data: { strings: string[] };
}

interface IStartsWithConfigMatcher extends IConfigMatcherBase {
type: 'STARTS_WITH';
data: { strings: string[] };
}

interface IEndsWithConfigMatcher extends IConfigMatcherBase {
type: 'ENDS_WITH';
data: { strings: string[] };
}

interface IContainsStringConfigMatcher extends IConfigMatcherBase {
type: 'CONTAINS_STRING';
data: { strings: string[] };
}

interface IInListSemverConfigMatcher extends IConfigMatcherBase {
type: 'IN_LIST_SEMVER';
data: { strings: string[] };
attribute?: string;
}

interface IInConfigVariantConfigMatcher extends IConfigMatcherBase {
type: 'IN_CONFIG_VARIANT';
data: { config: string; variants: string[] };
}

interface IEqualToBooleanConfigMatcher extends IConfigMatcherBase {
type: 'EQUAL_TO_BOOLEAN';
data: { value: boolean };
}

interface IMatchesStringConfigMatcher extends IConfigMatcherBase {
type: 'MATCHES_STRING';
data: { string: string };
}

interface IEqualToSemverConfigMatcher extends IConfigMatcherBase {
type: 'EQUAL_TO_SEMVER';
data: { string: string };
}

interface IGreaterThanOrEqualToSemverConfigMatcher extends IConfigMatcherBase {
type: 'GREATER_THAN_OR_EQUAL_TO_SEMVER';
data: { string: string };
}

interface ILessThanOrEqualToSemverConfigMatcher extends IConfigMatcherBase {
type: 'LESS_THAN_OR_EQUAL_TO_SEMVER';
data: { string: string };
}

interface IBetweenSemverConfigMatcher extends IConfigMatcherBase {
type: 'BETWEEN_SEMVER';
data: { start: string; end: string };
}

type IConfigMatcher = IAllKeysConfigMatcher | IInSegmentConfigMatcher | IWhitelistConfigMatcher | IEqualToConfigMatcher | IGreaterThanOrEqualToConfigMatcher |
ILessThanOrEqualToConfigMatcher | IBetweenConfigMatcher | IEqualToSetConfigMatcher | IContainsAnyOfSetConfigMatcher | IContainsAllOfSetConfigMatcher | IPartOfSetConfigMatcher |
IStartsWithConfigMatcher | IEndsWithConfigMatcher | IContainsStringConfigMatcher | IInConfigVariantConfigMatcher | IEqualToBooleanConfigMatcher | IMatchesStringConfigMatcher |
IEqualToSemverConfigMatcher | IGreaterThanOrEqualToSemverConfigMatcher | ILessThanOrEqualToSemverConfigMatcher | IBetweenSemverConfigMatcher | IInListSemverConfigMatcher |
IInLargeSegmentConfigMatcher | IInRBSegmentConfigMatcher

interface IConfigPartition {
variant: string
size: number
}

interface IConfig {
name: string;
identifier: string;
variants: Array<{
name: string;
definition: SplitIO.JsonObject;
Expand All @@ -37,6 +168,7 @@ interface IConfig {
trafficAllocation?: number,
trafficAllocationSeed?: number,
conditions?: Array<{
type?: 'ROLLOUT' | 'WHITELIST';
label: string;
partitions: Array<IConfigPartition>;
matchers: Array<IConfigMatcher>;
Expand Down Expand Up @@ -77,7 +209,6 @@ export function configsFetcherFactory(params: ISdkFactoryContextSync): IDefiniti

configsFetcher.type = 'configs' as const;
return configsFetcher;

}

function defaultCondition(treatment: string): IDefinitionCondition {
Expand All @@ -96,25 +227,33 @@ function defaultCondition(treatment: string): IDefinitionCondition {
};
}

const wl = (d: { strings: string[] }) => ({ whitelistMatcherData: { whitelist: d.strings } });
const num = (d: { type?: IConfigMatcherDataType; number: number }) => ({ unaryNumericMatcherData: { dataType: d.type || 'NUMBER', value: d.number } });
const seg = (d: { name: string }) => ({ userDefinedSegmentMatcherData: { segmentName: d.name } });
const str = (d: { string: string }) => ({ stringMatcherData: d.string });

const MATCHER_CONVERTERS: Record<IConfigMatcher['type'], (data: any) => Record<string, any>> = {
ALL_KEYS: () => ({}),
IN_SEGMENT: seg, IN_RULE_BASED_SEGMENT: seg,
IN_LARGE_SEGMENT: (d) => ({ userDefinedLargeSegmentMatcherData: { largeSegmentName: d.name } }),
WHITELIST: wl, EQUAL_TO_SET: wl, CONTAINS_ANY_OF_SET: wl, CONTAINS_ALL_OF_SET: wl,
PART_OF_SET: wl, STARTS_WITH: wl, ENDS_WITH: wl, CONTAINS_STRING: wl, IN_LIST_SEMVER: wl,
EQUAL_TO: num, GREATER_THAN_OR_EQUAL_TO: num, LESS_THAN_OR_EQUAL_TO: num,
BETWEEN: (d) => ({ betweenMatcherData: { dataType: d.type || 'NUMBER', start: d.start, end: d.end } }),
IN_CONFIG_VARIANT: (d) => ({ dependencyMatcherData: { split: d.config, treatments: d.variants } }),
EQUAL_TO_BOOLEAN: (d) => ({ booleanMatcherData: d.value }),
MATCHES_STRING: str, EQUAL_TO_SEMVER: str, GREATER_THAN_OR_EQUAL_TO_SEMVER: str, LESS_THAN_OR_EQUAL_TO_SEMVER: str,
BETWEEN_SEMVER: (d) => ({ betweenStringMatcherData: { start: d.start, end: d.end } }),
};

function convertMatcher(matcher: IConfigMatcher): IDefinitionMatcher {
const keySelector = matcher.attribute ? { trafficType: 'user', attribute: matcher.attribute } : null;

switch (matcher.type) {
case 'IS_EQUAL_TO':
return {
matcherType: 'EQUAL_TO',
negate: false,
keySelector,
unaryNumericMatcherData: { dataType: matcher.data.type, value: matcher.data.number },
};
case 'WHITELIST':
return {
matcherType: 'WHITELIST',
negate: false,
keySelector,
whitelistMatcherData: { whitelist: matcher.data.strings },
};
}
return {
matcherType: matcher.type,
negate: false,
keySelector,
...MATCHER_CONVERTERS[matcher.type](matcher.data),
} as IDefinitionMatcher;
}

function convertConfigToDefinition(config: IConfig): IDefinition {
Expand All @@ -124,7 +263,7 @@ function convertConfigToDefinition(config: IConfig): IDefinition {
config.variants.forEach(variant => configurations[variant.name] = variant.definition);

const conditions: IDefinitionCondition[] = config.targeting?.conditions?.map(condition => ({
conditionType: condition.matchers.some((m: IConfigMatcher) => m.type === 'WHITELIST') ? 'WHITELIST' : 'ROLLOUT',
conditionType: condition.type || (condition.matchers.some((m: IConfigMatcher) => m.type === 'WHITELIST') ? 'WHITELIST' : 'ROLLOUT'),
label: condition.label,
matcherGroup: {
combiner: 'AND',
Expand All @@ -133,10 +272,13 @@ function convertConfigToDefinition(config: IConfig): IDefinition {
partitions: condition.partitions.map(partition => ({ treatment: partition.variant, size: partition.size })),
})) || [];

conditions.push(defaultCondition(defaultTreatment));
// only add default condition if there is no a last condition with matcher type ALL_KEYS
if (!conditions.some(condition => condition.matcherGroup.matchers.some(matcher => matcher.matcherType === 'ALL_KEYS'))) {
conditions.push(defaultCondition(defaultTreatment));
}

return {
name: config.name,
name: config.identifier,
changeNumber: config.changeNumber || 0,
status: 'ACTIVE',
conditions,
Expand Down
4 changes: 2 additions & 2 deletions types/splitio.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2360,9 +2360,9 @@ declare namespace SplitIO {
type JsonObject = { [key: string]: JsonValue; };

/**
* Config definition.
* Config object returned by getConfig.
*/
interface Config {
type Config = {
/**
* The name of the variant.
*/
Expand Down