From 6c7cf01b03df9da9893d82e09385f6c03b5bc834 Mon Sep 17 00:00:00 2001 From: Ryan Lamb <4955475+kinyoklion@users.noreply.github.com> Date: Mon, 14 Aug 2023 15:21:05 -0700 Subject: [PATCH] fix: Allow for negation of segment match clauses. --- .../evaluation/Evaluator.segments.test.ts | 14 ++++++++++++++ .../shared/sdk-server/src/evaluation/Evaluator.ts | 4 ++-- .../sdk-server/src/evaluation/matchClause.ts | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/shared/sdk-server/__tests__/evaluation/Evaluator.segments.test.ts b/packages/shared/sdk-server/__tests__/evaluation/Evaluator.segments.test.ts index 5f94defa9a..5843bfe05c 100644 --- a/packages/shared/sdk-server/__tests__/evaluation/Evaluator.segments.test.ts +++ b/packages/shared/sdk-server/__tests__/evaluation/Evaluator.segments.test.ts @@ -110,6 +110,20 @@ describe('when evaluating user equivalent contexts for segments', () => { expect(res.detail.value).toBe(false); }); + it('can negate a segment match op', async () => { + const segment = { + key: 'test', + included: ['foo'], + version: 1, + }; + const evaluator = new Evaluator(basicPlatform, new TestQueries({ segments: [segment] })); + const flag = makeFlagWithSegmentMatch(segment); + flag.rules[0].clauses![0].negate = true; + const user = { key: 'bar' }; + const res = await evaluator.evaluate(flag, Context.fromLDContext(user)); + expect(res.detail.value).toBe(true); + }); + it.each([basicUser, basicSingleKindUser, basicMultiKindUser])( 'matches segment with user who is both included and excluded', async (context) => { diff --git a/packages/shared/sdk-server/src/evaluation/Evaluator.ts b/packages/shared/sdk-server/src/evaluation/Evaluator.ts index 55b563b27c..fc1d3b3bf6 100644 --- a/packages/shared/sdk-server/src/evaluation/Evaluator.ts +++ b/packages/shared/sdk-server/src/evaluation/Evaluator.ts @@ -17,7 +17,7 @@ import ErrorKinds from './ErrorKinds'; import EvalResult from './EvalResult'; import evalTargets from './evalTargets'; import makeBigSegmentRef from './makeBigSegmentRef'; -import matchClauseWithoutSegmentOperations from './matchClause'; +import matchClauseWithoutSegmentOperations, { maybeNegate } from './matchClause'; import matchSegmentTargets from './matchSegmentTargets'; import { Queries } from './Queries'; import Reasons from './Reasons'; @@ -296,7 +296,7 @@ export default class Evaluator { return new MatchError(errorResult); } - return new Match(match); + return new Match(maybeNegate(clause, match)); } // This is after segment matching, which does not use the reference. if (!clause.attributeReference.isValid) { diff --git a/packages/shared/sdk-server/src/evaluation/matchClause.ts b/packages/shared/sdk-server/src/evaluation/matchClause.ts index c6d28eed99..282db748dd 100644 --- a/packages/shared/sdk-server/src/evaluation/matchClause.ts +++ b/packages/shared/sdk-server/src/evaluation/matchClause.ts @@ -3,7 +3,7 @@ import { Context } from '@launchdarkly/js-sdk-common'; import { Clause } from './data/Clause'; import Operators from './Operations'; -function maybeNegate(clause: Clause, value: boolean): boolean { +export function maybeNegate(clause: Clause, value: boolean): boolean { if (clause.negate) { return !value; }