From 9cb3ccf124190ab3fe35cdd58251ec67b10a0850 Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Thu, 5 Mar 2026 00:21:36 -0300 Subject: [PATCH 1/2] Handle null conditions and excluded fields in IRBSegment parsing --- src/dtos/types.ts | 4 ++-- src/sync/polling/updaters/splitChangesUpdater.ts | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/dtos/types.ts b/src/dtos/types.ts index 6e21e831..bfd7269a 100644 --- a/src/dtos/types.ts +++ b/src/dtos/types.ts @@ -208,11 +208,11 @@ export interface IRBSegment { name: string, changeNumber: number, status: 'ACTIVE' | 'ARCHIVED', - conditions?: ISplitCondition[], + conditions?: ISplitCondition[] | null, excluded?: { keys?: string[] | null, segments?: IExcludedSegment[] | null - } + } | null } export interface ISplit { diff --git a/src/sync/polling/updaters/splitChangesUpdater.ts b/src/sync/polling/updaters/splitChangesUpdater.ts index 27a68313..b5786bbf 100644 --- a/src/sync/polling/updaters/splitChangesUpdater.ts +++ b/src/sync/polling/updaters/splitChangesUpdater.ts @@ -31,7 +31,7 @@ function checkAllSegmentsExist(segments: ISegmentsCacheBase): Promise { * Exported for testing purposes. */ export function parseSegments(ruleEntity: ISplit | IRBSegment, matcherType: typeof IN_SEGMENT | typeof IN_RULE_BASED_SEGMENT = IN_SEGMENT): Set { - const { conditions = [], excluded } = ruleEntity as IRBSegment; + const { conditions, excluded } = ruleEntity as IRBSegment; const segments = new Set(); if (excluded && excluded.segments) { @@ -42,12 +42,14 @@ export function parseSegments(ruleEntity: ISplit | IRBSegment, matcherType: type }); } - for (let i = 0; i < conditions.length; i++) { - const matchers = conditions[i].matcherGroup.matchers; + if (conditions) { + for (let i = 0; i < conditions.length; i++) { + const matchers = conditions[i].matcherGroup.matchers; - matchers.forEach(matcher => { - if (matcher.matcherType === matcherType) segments.add(matcher.userDefinedSegmentMatcherData.segmentName); - }); + matchers.forEach(matcher => { + if (matcher.matcherType === matcherType) segments.add(matcher.userDefinedSegmentMatcherData.segmentName); + }); + } } return segments; From dbf697817125edef7b2566e58016ff09b04401bc Mon Sep 17 00:00:00 2001 From: Emiliano Sanchez Date: Wed, 11 Mar 2026 17:56:46 -0300 Subject: [PATCH 2/2] Refactor /splitChanges DTO to make the SDK more robust in case nullable field are not defined --- src/dtos/types.ts | 10 +++++----- src/evaluator/types.ts | 4 ++-- src/evaluator/value/index.ts | 2 +- src/sync/polling/updaters/splitChangesUpdater.ts | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/dtos/types.ts b/src/dtos/types.ts index bfd7269a..6a252c8c 100644 --- a/src/dtos/types.ts +++ b/src/dtos/types.ts @@ -41,10 +41,10 @@ export interface IDependencyMatcherData { interface ISplitMatcherBase { matcherType: string - negate: boolean - keySelector: null | { + negate?: boolean + keySelector?: null | { trafficType: string, - attribute: string | null + attribute?: string | null } userDefinedSegmentMatcherData?: null | IInSegmentMatcherData userDefinedLargeSegmentMatcherData?: null | IInLargeSegmentMatcherData @@ -207,7 +207,7 @@ export interface IExcludedSegment { export interface IRBSegment { name: string, changeNumber: number, - status: 'ACTIVE' | 'ARCHIVED', + status?: 'ACTIVE' | 'ARCHIVED', conditions?: ISplitCondition[] | null, excluded?: { keys?: string[] | null, @@ -218,7 +218,7 @@ export interface IRBSegment { export interface ISplit { name: string, changeNumber: number, - status: 'ACTIVE' | 'ARCHIVED', + status?: 'ACTIVE' | 'ARCHIVED', conditions: ISplitCondition[], prerequisites?: null | { n: string, diff --git a/src/evaluator/types.ts b/src/evaluator/types.ts index 92806ddf..42900f06 100644 --- a/src/evaluator/types.ts +++ b/src/evaluator/types.ts @@ -13,8 +13,8 @@ export interface IMatcherDto { name: string value?: string | number | boolean | string[] | IDependencyMatcherData | IBetweenMatcherData | IBetweenStringMatcherData | null - attribute: string | null - negate: boolean + attribute?: string | null + negate?: boolean dataType: string } diff --git a/src/evaluator/value/index.ts b/src/evaluator/value/index.ts index 06184aa5..b6ac9fbb 100644 --- a/src/evaluator/value/index.ts +++ b/src/evaluator/value/index.ts @@ -4,7 +4,7 @@ import { ILogger } from '../../logger/types'; import { sanitize } from './sanitize'; import { ENGINE_VALUE, ENGINE_VALUE_NO_ATTRIBUTES, ENGINE_VALUE_INVALID } from '../../logger/constants'; -function parseValue(log: ILogger, key: SplitIO.SplitKey, attributeName: string | null, attributes?: SplitIO.Attributes) { +function parseValue(log: ILogger, key: SplitIO.SplitKey, attributeName?: string | null, attributes?: SplitIO.Attributes) { let value = undefined; if (attributeName) { if (attributes) { diff --git a/src/sync/polling/updaters/splitChangesUpdater.ts b/src/sync/polling/updaters/splitChangesUpdater.ts index b5786bbf..0510a485 100644 --- a/src/sync/polling/updaters/splitChangesUpdater.ts +++ b/src/sync/polling/updaters/splitChangesUpdater.ts @@ -90,7 +90,7 @@ function matchFilters(featureFlag: ISplit, filters: ISplitFiltersValidation) { export function computeMutation(rules: Array, segments: Set, filters?: ISplitFiltersValidation): ISplitMutations { return rules.reduce((accum, ruleEntity) => { - if (ruleEntity.status === 'ACTIVE' && (!filters || matchFilters(ruleEntity as ISplit, filters))) { + if (ruleEntity.status !== 'ARCHIVED' && (!filters || matchFilters(ruleEntity as ISplit, filters))) { accum.added.push(ruleEntity); parseSegments(ruleEntity).forEach((segmentName: string) => {