From cbf0cf76d33d90b73fd3f3ec7a55631010155c52 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Tue, 5 Nov 2024 17:09:32 +0100 Subject: [PATCH 1/2] Use timestamp from runtime --- packages/server/src/automations/utils.ts | 10 +++++++--- packages/types/src/sdk/automations/index.ts | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/server/src/automations/utils.ts b/packages/server/src/automations/utils.ts index 62125ea589..365dc36b68 100644 --- a/packages/server/src/automations/utils.ts +++ b/packages/server/src/automations/utils.ts @@ -70,6 +70,10 @@ export async function processEvent(job: AutomationJob) { const task = async () => { try { + if (isCronTrigger(job.data.automation)) { + // Requires the timestamp at run time + job.data.event.timestamp = Date.now() + } // need to actually await these so that an error can be captured properly console.log("automation running", ...loggingArgs(job)) @@ -210,15 +214,15 @@ export async function enableCronTrigger(appId: any, automation: Automation) { } // make a job id rather than letting Bull decide, makes it easier to handle on way out const jobId = `${appId}_cron_${utils.newid()}` - const job: any = await automationQueue.add( + const job = await automationQueue.add( { automation, - event: { appId, timestamp: Date.now() }, + event: { appId }, }, { repeat: { cron: cronExp }, jobId } ) // Assign cron job ID from bull so we can remove it later if the cron trigger is removed - trigger.cronJobId = job.id + trigger.cronJobId = job.id.toString() // can't use getAppDB here as this is likely to be called from dev app, // but this call could be for dev app or prod app, need to just use what // was passed in diff --git a/packages/types/src/sdk/automations/index.ts b/packages/types/src/sdk/automations/index.ts index 9ceded03ee..ba79d47021 100644 --- a/packages/types/src/sdk/automations/index.ts +++ b/packages/types/src/sdk/automations/index.ts @@ -14,6 +14,7 @@ export interface AutomationDataEvent { row?: Row oldRow?: Row user?: UserBindings + timestamp?: number } export interface AutomationData { From 0667dd9d30cd3038b61fd006d18c907f5a2cf349 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Wed, 6 Nov 2024 12:38:58 +0100 Subject: [PATCH 2/2] Add test --- .../backend-core/src/queue/inMemoryQueue.ts | 2 +- .../tests/cron-automations.spec.ts | 49 +++++++++++++++++++ .../server/src/tests/utilities/structures.ts | 32 ++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 packages/server/src/automations/tests/cron-automations.spec.ts diff --git a/packages/backend-core/src/queue/inMemoryQueue.ts b/packages/backend-core/src/queue/inMemoryQueue.ts index 62b971f9f5..1d8544828d 100644 --- a/packages/backend-core/src/queue/inMemoryQueue.ts +++ b/packages/backend-core/src/queue/inMemoryQueue.ts @@ -141,7 +141,7 @@ class InMemoryQueue implements Partial { } else { pushMessage() } - return {} as any + return { id: jobId } as any } /** diff --git a/packages/server/src/automations/tests/cron-automations.spec.ts b/packages/server/src/automations/tests/cron-automations.spec.ts new file mode 100644 index 0000000000..62c8ccd612 --- /dev/null +++ b/packages/server/src/automations/tests/cron-automations.spec.ts @@ -0,0 +1,49 @@ +import tk from "timekeeper" +import "../../environment" +import * as automations from "../index" +import * as setup from "./utilities" +import { basicCronAutomation } from "../../tests/utilities/structures" + +const initialTime = Date.now() +tk.freeze(initialTime) + +const oneMinuteInMs = 60 * 1000 + +describe("cron automations", () => { + let config = setup.getConfig() + + beforeAll(async () => { + await automations.init() + await config.init() + }) + + afterAll(async () => { + await automations.shutdown() + setup.afterAll() + }) + + beforeEach(() => { + tk.freeze(initialTime) + }) + + async function travel(ms: number) { + tk.travel(Date.now() + ms) + } + + it("should initialise the automation timestamp", async () => { + const automation = basicCronAutomation(config.appId!, "* * * * *") + await config.api.automation.post(automation) + await travel(oneMinuteInMs) + await config.publish() + + const automationLogs = await config.getAutomationLogs() + expect(automationLogs.data).toHaveLength(1) + expect(automationLogs.data).toEqual([ + expect.objectContaining({ + trigger: expect.objectContaining({ + outputs: { timestamp: initialTime + oneMinuteInMs }, + }), + }), + ]) + }) +}) diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts index 49046d9eda..beb03ecf08 100644 --- a/packages/server/src/tests/utilities/structures.ts +++ b/packages/server/src/tests/utilities/structures.ts @@ -245,6 +245,38 @@ export function basicAutomation(appId?: string): Automation { } } +export function basicCronAutomation(appId: string, cron: string): Automation { + const automation: Automation = { + name: `Automation ${generator.guid()}`, + definition: { + trigger: { + stepId: AutomationTriggerStepId.CRON, + name: "test", + tagline: "test", + icon: "test", + description: "test", + type: AutomationStepType.TRIGGER, + id: "test", + inputs: { + cron, + }, + schema: { + inputs: { + properties: {}, + }, + outputs: { + properties: {}, + }, + }, + }, + steps: [], + }, + type: "automation", + appId, + } + return automation +} + export function serverLogAutomation(appId?: string): Automation { return { name: "My Automation",