diff --git a/packages/destination-subscriptions/src/__tests__/generate-fql.test.ts b/packages/destination-subscriptions/src/__tests__/generate-fql.test.ts index 090375faab..ebd00f055b 100644 --- a/packages/destination-subscriptions/src/__tests__/generate-fql.test.ts +++ b/packages/destination-subscriptions/src/__tests__/generate-fql.test.ts @@ -211,3 +211,43 @@ test('should handle ast with nested (dot-delimited) and irregular properties', ( expect(generateFql(ast)).toEqual('properties.foo.bar.baz\\ alp.zaz = "hello"') }) + +test('should handle number_not_equals ast', () => { + const ast: Subscription = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-trait', + name: 'value', + operator: 'number_not_equals', + value: '456.0' + } + ] + } + + expect(generateFql(ast)).toEqual('traits.value != 456') +}) + +test('should handle both not equal (!=) and number_not_equals ast', () => { + const ast: Subscription = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: 'number_not_equals', + value: '123' + }, + { + type: 'event-trait', + name: 'label', + operator: '!=', + value: '456' + } + ] + } + + expect(generateFql(ast)).toEqual('properties.value != 123 and traits.label != "456"') +}) diff --git a/packages/destination-subscriptions/src/__tests__/validate.test.ts b/packages/destination-subscriptions/src/__tests__/validate.test.ts index f3ec2f24ca..e750b51430 100644 --- a/packages/destination-subscriptions/src/__tests__/validate.test.ts +++ b/packages/destination-subscriptions/src/__tests__/validate.test.ts @@ -740,3 +740,25 @@ test('event type = "track" and event = "Page Viewed" or event = "Order Completed }) ).toEqual(true) }) + +test('operators - number_not_equals (numbers)', () => { + for (const value of ['456', 456]) { + const ast = { + type: 'group', + operator: 'and', + children: [ + { + type: 'event-property', + name: 'value', + operator: 'number_not_equals', + value + } + ] + } + + expect(validate(ast, { properties: { value: 123 } })).toEqual(true) + + expect(validate(ast, { properties: { value: '456' } })).toEqual(false) + expect(validate(ast, { properties: { value: 0 } })).toEqual(true) + } +}) diff --git a/packages/destination-subscriptions/src/generate-fql.ts b/packages/destination-subscriptions/src/generate-fql.ts index a84a38af3e..73f0b0b5ca 100644 --- a/packages/destination-subscriptions/src/generate-fql.ts +++ b/packages/destination-subscriptions/src/generate-fql.ts @@ -49,6 +49,8 @@ const fqlExpression = (name: string, operator: Operator, value: string | boolean return `${name} ${operator} ${Number(value)}` case 'number_equals': return `${name} = ${Number(value)}` + case 'number_not_equals': + return `${name} != ${Number(value)}` default: return `${name} ${operator} ${stringifyValue(value)}` } diff --git a/packages/destination-subscriptions/src/parse-fql.ts b/packages/destination-subscriptions/src/parse-fql.ts index eb0bf79ff2..a09d03087e 100644 --- a/packages/destination-subscriptions/src/parse-fql.ts +++ b/packages/destination-subscriptions/src/parse-fql.ts @@ -193,6 +193,7 @@ const parse = (tokens: Token[]): Condition => { const isExists = operatorToken.value === '!=' && valueToken.value === 'null' const isNotExists = operatorToken.value === '=' && valueToken.value === 'null' const isNumberEquals = operatorToken.value === '=' && valueToken.type === 'number' + const isNumberNotEquals = operatorToken.value === '!=' && valueToken.type === 'number' if (conditionType === 'event') { nodes.push({ @@ -284,6 +285,13 @@ const parse = (tokens: Token[]): Condition => { operator: 'number_equals', value: getTokenValue(valueToken) }) + } else if (isNumberNotEquals) { + nodes.push({ + type: 'event-property', + name: token.value.replace(/^(properties)\./, ''), + operator: 'number_not_equals', + value: getTokenValue(valueToken) + }) } else { nodes.push({ type: 'event-property', @@ -324,6 +332,13 @@ const parse = (tokens: Token[]): Condition => { operator: 'number_equals', value: getTokenValue(valueToken) }) + } else if (isNumberNotEquals) { + nodes.push({ + type: 'event-trait', + name: token.value.replace(/^(traits)\./, ''), + operator: 'number_not_equals', + value: getTokenValue(valueToken) + }) } else { nodes.push({ type: 'event-trait', @@ -364,6 +379,13 @@ const parse = (tokens: Token[]): Condition => { operator: 'number_equals', value: getTokenValue(valueToken) }) + } else if (isNumberNotEquals) { + nodes.push({ + type: 'event-context', + name: token.value.replace(/^(context)\./, ''), + operator: 'number_not_equals', + value: getTokenValue(valueToken) + }) } else { nodes.push({ type: 'event-context', diff --git a/packages/destination-subscriptions/src/types.ts b/packages/destination-subscriptions/src/types.ts index b4ef3a65e7..692d5e4ce8 100644 --- a/packages/destination-subscriptions/src/types.ts +++ b/packages/destination-subscriptions/src/types.ts @@ -84,6 +84,7 @@ export type Operator = | 'not_exists' | 'is_true' | 'is_false' + | 'number_not_equals' export type ConditionType = | 'group' diff --git a/packages/destination-subscriptions/src/validate.ts b/packages/destination-subscriptions/src/validate.ts index fcfb53dd47..d792e5d700 100644 --- a/packages/destination-subscriptions/src/validate.ts +++ b/packages/destination-subscriptions/src/validate.ts @@ -77,6 +77,8 @@ const validateValue = (actual: unknown, operator: Operator, expected?: string | return typeof actual === 'number' && Number(actual) === Number(expected) case '!=': return actual !== String(expected) + case 'number_not_equals': + return typeof actual === 'number' && Number(actual) !== Number(expected) case '<': return typeof actual === 'number' && Number(actual) < Number(expected) case '<=':