EventFilter now supports more complex condition filters #271
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@trigger.dev/core": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
EventFilter now supports more complex condition filters #271
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { EventDispatcher, EventRecord } from "@trigger.dev/database";
|
import type { EventDispatcher, EventRecord } from "@trigger.dev/database";
|
||||||
import type { EventFilter } from "@trigger.dev/core";
|
import type { EventFilter } from "@trigger.dev/core";
|
||||||
import { EventFilterSchema } from "@trigger.dev/core";
|
import { EventFilterSchema, eventFilterMatches } from "@trigger.dev/core";
|
||||||
import { $transaction, PrismaClientOrTransaction, prisma } from "~/db.server";
|
import { $transaction, PrismaClientOrTransaction, prisma } from "~/db.server";
|
||||||
import { logger } from "~/services/logger.server";
|
import { logger } from "~/services/logger.server";
|
||||||
import { workerQueue } from "../worker.server";
|
import { workerQueue } from "../worker.server";
|
||||||
@@ -124,29 +124,6 @@ export class EventMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public matches(filter: EventFilter) {
|
public matches(filter: EventFilter) {
|
||||||
return patternMatches(this.event, filter);
|
return eventFilterMatches(this.event, filter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function patternMatches(payload: any, pattern: any): boolean {
|
|
||||||
for (const [patternKey, patternValue] of Object.entries(pattern)) {
|
|
||||||
const payloadValue = payload[patternKey];
|
|
||||||
|
|
||||||
if (Array.isArray(patternValue)) {
|
|
||||||
if (patternValue.length > 0 && !patternValue.includes(payloadValue)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (typeof patternValue === "object") {
|
|
||||||
if (Array.isArray(payloadValue)) {
|
|
||||||
if (!payloadValue.some((item) => patternMatches(item, patternValue))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!patternMatches(payloadValue, patternValue)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
module.exports = {
|
||||||
|
moduleFileExtensions: ["ts", "tsx", "js"],
|
||||||
|
transform: {
|
||||||
|
"^.+\\.(ts|tsx)$": "ts-jest",
|
||||||
|
},
|
||||||
|
testMatch: ["<rootDir>/test/**/*.ts?(x)", "<rootDir>/test/**/?(*.)+(spec|test).ts?(x)"],
|
||||||
|
testEnvironment: "node",
|
||||||
|
};
|
||||||
@@ -22,7 +22,8 @@
|
|||||||
"clean": "rimraf dist",
|
"clean": "rimraf dist",
|
||||||
"build": "npm run clean && npm run build:tsup",
|
"build": "npm run clean && npm run build:tsup",
|
||||||
"build:tsup": "tsup --dts-resolve",
|
"build:tsup": "tsup --dts-resolve",
|
||||||
"typecheck": "tsc --noEmit"
|
"typecheck": "tsc --noEmit",
|
||||||
|
"test": "jest"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ulid": "^2.3.0",
|
"ulid": "^2.3.0",
|
||||||
@@ -31,12 +32,15 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@trigger.dev/tsconfig": "workspace:*",
|
"@trigger.dev/tsconfig": "workspace:*",
|
||||||
|
"@types/jest": "^29.5.3",
|
||||||
"@types/node": "16",
|
"@types/node": "16",
|
||||||
|
"jest": "^29.6.2",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
|
"ts-jest": "^29.1.1",
|
||||||
"tsup": "^7.1.0",
|
"tsup": "^7.1.0",
|
||||||
"typescript": "^4.9.4"
|
"typescript": "^4.9.4"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.8.0"
|
"node": ">=16.8.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,186 @@
|
|||||||
|
import { EventFilter } from "./schemas/eventFilter";
|
||||||
|
|
||||||
|
// EventFilter is a recursive type, where the keys are strings and the values are an array of strings, numbers, booleans, or objects.
|
||||||
|
// If the values of the array are strings, numbers, or booleans, than we are matching against the value of the payload.
|
||||||
|
// If the values of the array are objects, then we are doing content filtering
|
||||||
|
// An example would be [{ $endsWith: ".png" }, { $startsWith: "images/" } ]
|
||||||
|
export function eventFilterMatches(payload: any, filter: EventFilter): boolean {
|
||||||
|
for (const [patternKey, patternValue] of Object.entries(filter)) {
|
||||||
|
const payloadValue = payload[patternKey];
|
||||||
|
|
||||||
|
if (Array.isArray(patternValue)) {
|
||||||
|
if (patternValue.length === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see if all the items in the array are a string
|
||||||
|
if ((patternValue as unknown[]).every((item) => typeof item === "string")) {
|
||||||
|
if ((patternValue as string[]).includes(payloadValue)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see if all the items in the array are a number
|
||||||
|
if ((patternValue as unknown[]).every((item) => typeof item === "number")) {
|
||||||
|
if ((patternValue as number[]).includes(payloadValue)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see if all the items in the array are a boolean
|
||||||
|
if ((patternValue as unknown[]).every((item) => typeof item === "boolean")) {
|
||||||
|
if ((patternValue as boolean[]).includes(payloadValue)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we know that all the items in the array are objects
|
||||||
|
const objectArray = patternValue as Exclude<
|
||||||
|
typeof patternValue,
|
||||||
|
number[] | string[] | boolean[]
|
||||||
|
>;
|
||||||
|
|
||||||
|
if (!contentFiltersMatches(payloadValue, objectArray)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
} else if (typeof patternValue === "object") {
|
||||||
|
if (Array.isArray(payloadValue)) {
|
||||||
|
if (!payloadValue.some((item) => eventFilterMatches(item, patternValue))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!eventFilterMatches(payloadValue, patternValue)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContentFilters = Exclude<EventFilter[string], EventFilter | string[] | number[] | boolean[]>;
|
||||||
|
|
||||||
|
function contentFiltersMatches(actualValue: any, contentFilters: ContentFilters): boolean {
|
||||||
|
for (const contentFilter of contentFilters) {
|
||||||
|
if (typeof contentFilter === "object") {
|
||||||
|
const [key, value] = Object.entries(contentFilter)[0];
|
||||||
|
|
||||||
|
if (!contentFilterMatches(actualValue, contentFilter)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function contentFilterMatches(actualValue: any, contentFilter: ContentFilters[number]): boolean {
|
||||||
|
if ("$endsWith" in contentFilter) {
|
||||||
|
if (typeof actualValue !== "string") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return actualValue.endsWith(contentFilter.$endsWith);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("$startsWith" in contentFilter) {
|
||||||
|
if (typeof actualValue !== "string") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return actualValue.startsWith(contentFilter.$startsWith);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("$anythingBut" in contentFilter) {
|
||||||
|
if (Array.isArray(contentFilter.$anythingBut)) {
|
||||||
|
if ((contentFilter.$anythingBut as any[]).includes(actualValue)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (contentFilter.$anythingBut === actualValue) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("$exists" in contentFilter) {
|
||||||
|
if (contentFilter.$exists) {
|
||||||
|
return actualValue !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return actualValue === undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("$gt" in contentFilter) {
|
||||||
|
if (typeof actualValue !== "number") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return actualValue > contentFilter.$gt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("$lt" in contentFilter) {
|
||||||
|
if (typeof actualValue !== "number") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return actualValue < contentFilter.$lt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("$gte" in contentFilter) {
|
||||||
|
if (typeof actualValue !== "number") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return actualValue >= contentFilter.$gte;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("$lte" in contentFilter) {
|
||||||
|
if (typeof actualValue !== "number") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return actualValue <= contentFilter.$lte;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("$between" in contentFilter) {
|
||||||
|
if (typeof actualValue !== "number") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return actualValue >= contentFilter.$between[0] && actualValue <= contentFilter.$between[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("$includes" in contentFilter) {
|
||||||
|
if (Array.isArray(actualValue)) {
|
||||||
|
return actualValue.includes(contentFilter.$includes);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use localCompare
|
||||||
|
if ("$ignoreCaseEquals" in contentFilter) {
|
||||||
|
if (typeof actualValue !== "string") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
actualValue.localeCompare(contentFilter.$ignoreCaseEquals, undefined, {
|
||||||
|
sensitivity: "accent",
|
||||||
|
}) === 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -5,3 +5,4 @@ export * from "./utils";
|
|||||||
export * from "./retry";
|
export * from "./retry";
|
||||||
export * from "./replacements";
|
export * from "./replacements";
|
||||||
export * from "./searchParams";
|
export * from "./searchParams";
|
||||||
|
export * from "./eventFilterMatches";
|
||||||
|
|||||||
@@ -7,6 +7,46 @@ const EventMatcherSchema = z.union([
|
|||||||
z.array(z.number()),
|
z.array(z.number()),
|
||||||
/** Match against a boolean */
|
/** Match against a boolean */
|
||||||
z.array(z.boolean()),
|
z.array(z.boolean()),
|
||||||
|
z.array(
|
||||||
|
z.union([
|
||||||
|
z.object({
|
||||||
|
$endsWith: z.string(),
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
$startsWith: z.string(),
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
$exists: z.boolean(),
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
$anythingBut: z.union([z.string(), z.number(), z.boolean()]),
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
$anythingBut: z.union([z.array(z.string()), z.array(z.number()), z.array(z.boolean())]),
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
$gt: z.number(),
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
$lt: z.number(),
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
$gte: z.number(),
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
$lte: z.number(),
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
$between: z.tuple([z.number(), z.number()]),
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
$includes: z.union([z.string(), z.number(), z.boolean()]),
|
||||||
|
}),
|
||||||
|
z.object({
|
||||||
|
$ignoreCaseEquals: z.string(),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
type EventMatcher = z.infer<typeof EventMatcherSchema>;
|
type EventMatcher = z.infer<typeof EventMatcherSchema>;
|
||||||
|
|||||||
@@ -0,0 +1,522 @@
|
|||||||
|
import { EventFilter } from "../src";
|
||||||
|
import { eventFilterMatches } from "../src/eventFilterMatches";
|
||||||
|
|
||||||
|
describe("eventFilterMatches", () => {
|
||||||
|
it("should return true when payload matches string filter", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "John",
|
||||||
|
age: 30,
|
||||||
|
isAdmin: false,
|
||||||
|
hobbies: ["reading", "swimming"],
|
||||||
|
address: {
|
||||||
|
street: "123 Main St",
|
||||||
|
city: "Anytown",
|
||||||
|
state: "CA",
|
||||||
|
zip: "12345",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
name: ["John"],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when payload matches boolean filter", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "John",
|
||||||
|
age: 30,
|
||||||
|
isAdmin: false,
|
||||||
|
hobbies: ["reading", "swimming"],
|
||||||
|
address: {
|
||||||
|
street: "123 Main St",
|
||||||
|
city: "Anytown",
|
||||||
|
state: "CA",
|
||||||
|
zip: "12345",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
isAdmin: [false],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when payload matches number filter", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "John",
|
||||||
|
age: 30,
|
||||||
|
isAdmin: false,
|
||||||
|
hobbies: ["reading", "swimming"],
|
||||||
|
address: {
|
||||||
|
street: "123 Main St",
|
||||||
|
city: "Anytown",
|
||||||
|
state: "CA",
|
||||||
|
zip: "12345",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
age: [30],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when payload matches $startsWith content filter", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "John",
|
||||||
|
age: 30,
|
||||||
|
isAdmin: false,
|
||||||
|
hobbies: ["reading", "swimming"],
|
||||||
|
address: {
|
||||||
|
street: "123 Main St",
|
||||||
|
city: "Anytown",
|
||||||
|
state: "CA",
|
||||||
|
zip: "12345",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
name: [{ $startsWith: "Jo" }],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when payload matches $endsWith content filter", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "John",
|
||||||
|
age: 30,
|
||||||
|
isAdmin: false,
|
||||||
|
hobbies: ["reading", "swimming"],
|
||||||
|
address: {
|
||||||
|
street: "123 Main St",
|
||||||
|
city: "Anytown",
|
||||||
|
state: "CA",
|
||||||
|
zip: "12345",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
name: [{ $endsWith: "hn" }],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when payload matches $startsWith and $endsWith content filters", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "John",
|
||||||
|
age: 30,
|
||||||
|
isAdmin: false,
|
||||||
|
hobbies: ["reading", "swimming"],
|
||||||
|
address: {
|
||||||
|
street: "123 Main St",
|
||||||
|
city: "Anytown",
|
||||||
|
state: "CA",
|
||||||
|
zip: "12345",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
name: [{ $startsWith: "Jo" }, { $endsWith: "hn" }],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when payload does not match $anythingBut filter", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "John",
|
||||||
|
age: 30,
|
||||||
|
isAdmin: false,
|
||||||
|
hobbies: ["reading", "swimming"],
|
||||||
|
address: {
|
||||||
|
street: "123 Main St",
|
||||||
|
city: "Anytown",
|
||||||
|
state: "CA",
|
||||||
|
zip: "12345",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
name: [{ $anythingBut: "Jane" }],
|
||||||
|
address: {
|
||||||
|
street: [{ $anythingBut: "456 Elm St" }],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when payload does not match $anythingBut filter with an array", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "John",
|
||||||
|
age: 30,
|
||||||
|
isAdmin: false,
|
||||||
|
hobbies: ["reading", "swimming"],
|
||||||
|
address: {
|
||||||
|
street: "123 Main St",
|
||||||
|
city: "Anytown",
|
||||||
|
state: "CA",
|
||||||
|
zip: "12345",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
name: [{ $anythingBut: ["Jane", "Joe"] }],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when payload does have a key that $exists = true", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "John",
|
||||||
|
age: 30,
|
||||||
|
isAdmin: false,
|
||||||
|
hobbies: ["reading", "swimming"],
|
||||||
|
address: {
|
||||||
|
street: "123 Main St",
|
||||||
|
city: "Anytown",
|
||||||
|
state: "CA",
|
||||||
|
zip: "12345",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
name: [{ $exists: true }],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when payload does NOT have a key that $exists = false", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "John",
|
||||||
|
age: 30,
|
||||||
|
isAdmin: false,
|
||||||
|
hobbies: ["reading", "swimming"],
|
||||||
|
address: {
|
||||||
|
street: "123 Main St",
|
||||||
|
city: "Anytown",
|
||||||
|
state: "CA",
|
||||||
|
zip: "12345",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
foo: [{ $exists: false }],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when payload does match numeric 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 = {
|
||||||
|
age: [{ $gt: 20 }, { $lt: 40 }],
|
||||||
|
score: [{ $between: [90, 110] }],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when payload matches an includes 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: [{ $includes: "reading" }],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true when payload matches an ignoreCaseEquals 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 = {
|
||||||
|
name: [{ $ignoreCaseEquals: "john" }],
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when payload does not match string filter", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "Jane",
|
||||||
|
age: 25,
|
||||||
|
hobbies: ["running", "yoga"],
|
||||||
|
address: {
|
||||||
|
street: "456 Elm St",
|
||||||
|
city: "Othertown",
|
||||||
|
state: "NY",
|
||||||
|
zip: "67890",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
name: ["John"],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when payload does not match string filter because it's the wrong type", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "Jane",
|
||||||
|
age: 25,
|
||||||
|
hobbies: ["running", "yoga"],
|
||||||
|
address: {
|
||||||
|
street: "456 Elm St",
|
||||||
|
city: "Othertown",
|
||||||
|
state: "NY",
|
||||||
|
zip: "67890",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
age: ["John"],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when payload does not match boolean filter", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "Jane",
|
||||||
|
age: 25,
|
||||||
|
isAdmin: true,
|
||||||
|
hobbies: ["running", "yoga"],
|
||||||
|
address: {
|
||||||
|
street: "456 Elm St",
|
||||||
|
city: "Othertown",
|
||||||
|
state: "NY",
|
||||||
|
zip: "67890",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
isAdmin: [false],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when payload does not match number filter", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "Jane",
|
||||||
|
age: 25,
|
||||||
|
isAdmin: true,
|
||||||
|
hobbies: ["running", "yoga"],
|
||||||
|
address: {
|
||||||
|
street: "456 Elm St",
|
||||||
|
city: "Othertown",
|
||||||
|
state: "NY",
|
||||||
|
zip: "67890",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
age: [30],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when payload does not match $startsWith content filter", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "Jane",
|
||||||
|
age: 25,
|
||||||
|
isAdmin: true,
|
||||||
|
hobbies: ["running", "yoga"],
|
||||||
|
address: {
|
||||||
|
street: "456 Elm St",
|
||||||
|
city: "Othertown",
|
||||||
|
state: "NY",
|
||||||
|
zip: "67890",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
name: [{ $startsWith: "Jo" }],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when payload does not match $endsWith content filter", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "Jane",
|
||||||
|
age: 25,
|
||||||
|
isAdmin: true,
|
||||||
|
hobbies: ["running", "yoga"],
|
||||||
|
address: {
|
||||||
|
street: "456 Elm St",
|
||||||
|
city: "Othertown",
|
||||||
|
state: "NY",
|
||||||
|
zip: "67890",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
name: [{ $startsWith: "Ja" }, { $endsWith: "hn" }],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when payload does match $anythingBut content filters", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "Jane",
|
||||||
|
age: 25,
|
||||||
|
isAdmin: true,
|
||||||
|
hobbies: ["running", "yoga"],
|
||||||
|
address: {
|
||||||
|
street: "456 Elm St",
|
||||||
|
city: "Othertown",
|
||||||
|
state: "NY",
|
||||||
|
zip: "67890",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
name: [{ $anythingBut: "Jane" }],
|
||||||
|
address: {
|
||||||
|
street: [{ $anythingBut: "456 Elm St" }],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when payload does match $anythingBut content filters with an array", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "Jane",
|
||||||
|
age: 25,
|
||||||
|
isAdmin: true,
|
||||||
|
hobbies: ["running", "yoga"],
|
||||||
|
address: {
|
||||||
|
street: "456 Elm St",
|
||||||
|
city: "Othertown",
|
||||||
|
state: "NY",
|
||||||
|
zip: "67890",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
name: [{ $anythingBut: ["Jane", "John"] }],
|
||||||
|
address: {
|
||||||
|
street: [{ $anythingBut: "456 Elm St" }],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when payload does not have a key that $exists = true", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "Jane",
|
||||||
|
age: 25,
|
||||||
|
isAdmin: true,
|
||||||
|
hobbies: ["running", "yoga"],
|
||||||
|
address: {
|
||||||
|
street: "456 Elm St",
|
||||||
|
city: "Othertown",
|
||||||
|
state: "NY",
|
||||||
|
zip: "67890",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
foo: [{ $exists: true }],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when payload does have a key that $exists = false", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "Jane",
|
||||||
|
age: 25,
|
||||||
|
score: 100,
|
||||||
|
isAdmin: true,
|
||||||
|
hobbies: ["running", "yoga"],
|
||||||
|
address: {
|
||||||
|
street: "456 Elm St",
|
||||||
|
city: "Othertown",
|
||||||
|
state: "NY",
|
||||||
|
zip: "67890",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
name: [{ $exists: false }],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false when the payload does not match the numeric filters", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "Jane",
|
||||||
|
age: 25,
|
||||||
|
score: 100,
|
||||||
|
isAdmin: true,
|
||||||
|
hobbies: ["running", "yoga"],
|
||||||
|
address: {
|
||||||
|
street: "456 Elm St",
|
||||||
|
city: "Othertown",
|
||||||
|
state: "NY",
|
||||||
|
zip: "67890",
|
||||||
|
latitude: 37.7749,
|
||||||
|
longitude: 122.4194,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
age: [{ $gt: 30 }],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should return false when the payload does not match an includes filter", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "Jane",
|
||||||
|
age: 25,
|
||||||
|
score: 100,
|
||||||
|
isAdmin: true,
|
||||||
|
hobbies: ["running", "yoga"],
|
||||||
|
address: {
|
||||||
|
street: "456 Elm St",
|
||||||
|
city: "San Francisco",
|
||||||
|
state: "CA",
|
||||||
|
zip: "67890",
|
||||||
|
latitude: 37.7749,
|
||||||
|
longitude: 122.4194,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
hobbies: [{ $includes: "swimming" }],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should return false when the payload does not match any ignoreCaseEquals condition", () => {
|
||||||
|
const payload = {
|
||||||
|
name: "Jane",
|
||||||
|
age: 25,
|
||||||
|
score: 100,
|
||||||
|
isAdmin: true,
|
||||||
|
hobbies: ["running", "yoga"],
|
||||||
|
address: {
|
||||||
|
street: "456 Elm St",
|
||||||
|
city: "San Francisco",
|
||||||
|
state: "CA",
|
||||||
|
zip: "67890",
|
||||||
|
latitude: 37.7749,
|
||||||
|
longitude: 122.4194,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const filter: EventFilter = {
|
||||||
|
name: [{ $ignoreCaseEquals: "john" }],
|
||||||
|
};
|
||||||
|
expect(eventFilterMatches(payload, filter)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"extends": "@trigger.dev/tsconfig/node18.json",
|
||||||
|
"include": ["src/globals.d.ts", "./src/**/*.ts", "tsup.config.ts"],
|
||||||
|
"compilerOptions": {
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"declaration": false,
|
||||||
|
"declarationMap": false
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"extends": "@trigger.dev/tsconfig/node18.json",
|
"extends": "@trigger.dev/tsconfig/node18.json",
|
||||||
"include": ["src/globals.d.ts", "./src/**/*.ts", "tsup.config.ts"],
|
"include": ["src/globals.d.ts", "./src/**/*.ts", "tsup.config.ts", "./test/**/*.ts"],
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"emitDecoratorMetadata": true,
|
"emitDecoratorMetadata": true,
|
||||||
"declaration": false,
|
"declaration": false,
|
||||||
"declarationMap": false
|
"declarationMap": false,
|
||||||
|
"types": ["jest"]
|
||||||
},
|
},
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { defineConfig } from "tsup";
|
|||||||
export default defineConfig([
|
export default defineConfig([
|
||||||
{
|
{
|
||||||
name: "main",
|
name: "main",
|
||||||
|
config: "tsconfig.build.json",
|
||||||
entry: ["./src/index.ts"],
|
entry: ["./src/index.ts"],
|
||||||
outDir: "./dist",
|
outDir: "./dist",
|
||||||
platform: "neutral",
|
platform: "neutral",
|
||||||
|
|||||||
Generated
+946
-4
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user