diff --git a/.changeset/chilled-rivers-grab.md b/.changeset/chilled-rivers-grab.md new file mode 100644 index 000000000..5b3b27ebb --- /dev/null +++ b/.changeset/chilled-rivers-grab.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/core": patch +--- + +feat: Add $not to eventFilters diff --git a/packages/core/src/eventFilterMatches.ts b/packages/core/src/eventFilterMatches.ts index 27c7a573b..c175797a5 100644 --- a/packages/core/src/eventFilterMatches.ts +++ b/packages/core/src/eventFilterMatches.ts @@ -198,5 +198,15 @@ function contentFilterMatches(actualValue: any, contentFilter: ContentFilters[nu return actualValue !== null; } + if ("$not" in contentFilter) { + if (Array.isArray(actualValue)) { + return !actualValue.includes(contentFilter.$not); + } else if (typeof actualValue === 'number' || typeof actualValue === 'boolean' || typeof actualValue === 'string') { + return actualValue !== contentFilter.$not; + } + + return false; + } + return true; } diff --git a/packages/core/src/schemas/eventFilter.ts b/packages/core/src/schemas/eventFilter.ts index d07dc9009..81be2e7c1 100644 --- a/packages/core/src/schemas/eventFilter.ts +++ b/packages/core/src/schemas/eventFilter.ts @@ -52,6 +52,9 @@ const EventMatcherSchema = z.union([ z.object({ $includes: z.union([z.string(), z.number(), z.boolean()]), }), + z.object({ + $not: z.union([z.string(), z.number(), z.boolean()]) + }) ]) ), ]); diff --git a/packages/core/test/eventFilterMatches.test.ts b/packages/core/test/eventFilterMatches.test.ts index 4a9249b45..9209ed3c8 100644 --- a/packages/core/test/eventFilterMatches.test.ts +++ b/packages/core/test/eventFilterMatches.test.ts @@ -252,6 +252,54 @@ describe("eventFilterMatches", () => { expect(eventFilterMatches(payload, filter)).toBe(true); }); + it("should return true when payload matches an not condition", () => { + const payload = { + name: "John", + age: 30, + score: 100, + isAdmin: false, + hobbies: ["reading", "swimming"], + address: { + street: "123 Main St", + city: "Anytown", + state: "CA", + zip: "12345", + }, + }; + const filter: EventFilter = { + hobbies: [{ $not: "gaming" }], + age: [{ $not: 39 }], + isAdmin: [{ $not: true }], + name: [{ $not: 'Test' }] + }; + + expect(eventFilterMatches(payload, filter)).toBe(true); + }); + + it("should return false when payload not matches an not condition", () => { + const payload = { + name: "John", + age: 30, + score: 100, + isAdmin: false, + hobbies: ["reading", "swimming"], + address: { + street: "123 Main St", + city: "Anytown", + state: "CA", + zip: "12345", + }, + }; + const filter: EventFilter = { + hobbies: [{ $not: "reading" }], + age: [{ $not: 30 }], + isAdmin: [{ $not: false }], + name: [{ $not: 'John' }] + }; + + expect(eventFilterMatches(payload, filter)).toBe(false); + }); + it("should return true when payload matches an ignoreCaseEquals condition", () => { const payload = { name: "John",