feat: Add support for scheduled workflows (#7651)

We still need to:
But wanted to open the PR for early feedback on the approach
This commit is contained in:
Stevche Radevski
2024-06-10 16:49:52 +02:00
committed by GitHub
parent 7f53fe06b6
commit 69410162f6
20 changed files with 465 additions and 44 deletions

View File

@@ -0,0 +1,23 @@
import { SchedulerOptions } from "@medusajs/orchestration"
import {
createStep,
createWorkflow,
StepResponse,
} from "@medusajs/workflows-sdk"
export const createScheduled = (name: string, schedule?: SchedulerOptions) => {
const workflowScheduledStepInvoke = jest.fn((input, context) => {
return new StepResponse({})
})
const step = createStep("step_1", workflowScheduledStepInvoke)
createWorkflow(
{ name, schedule: schedule ?? "* * * * * *" },
function (input) {
return step(input)
}
)
return workflowScheduledStepInvoke
}

View File

@@ -3,9 +3,10 @@ import { RemoteJoinerQuery } from "@medusajs/types"
import { TransactionHandlerType } from "@medusajs/utils"
import { IWorkflowEngineService } from "@medusajs/workflows-sdk"
import { knex } from "knex"
import { setTimeout } from "timers/promises"
import { setTimeout as setTimeoutPromise } from "timers/promises"
import "../__fixtures__"
import { workflow2Step2Invoke, workflow2Step3Invoke } from "../__fixtures__"
import { createScheduled } from "../__fixtures__/workflow_scheduled"
import { DB_URL, TestDatabase } from "../utils"
const sharedPgConnection = knex<any, any>({
@@ -24,42 +25,41 @@ const afterEach_ = async () => {
jest.setTimeout(50000)
describe("Workflow Orchestrator module", function () {
describe("Testing basic workflow", function () {
let workflowOrcModule: IWorkflowEngineService
let query: (
query: string | RemoteJoinerQuery | object,
variables?: Record<string, unknown>
) => Promise<any>
let workflowOrcModule: IWorkflowEngineService
let query: (
query: string | RemoteJoinerQuery | object,
variables?: Record<string, unknown>
) => Promise<any>
afterEach(afterEach_)
afterEach(afterEach_)
beforeAll(async () => {
const {
runMigrations,
query: remoteQuery,
modules,
} = await MedusaApp({
sharedResourcesConfig: {
database: {
connection: sharedPgConnection,
},
beforeAll(async () => {
const {
runMigrations,
query: remoteQuery,
modules,
} = await MedusaApp({
sharedResourcesConfig: {
database: {
connection: sharedPgConnection,
},
modulesConfig: {
workflows: {
resolve: __dirname + "/../..",
},
},
modulesConfig: {
workflows: {
resolve: __dirname + "/../..",
},
})
query = remoteQuery
await runMigrations()
workflowOrcModule = modules.workflows as unknown as IWorkflowEngineService
},
})
afterEach(afterEach_)
query = remoteQuery
await runMigrations()
workflowOrcModule = modules.workflows as unknown as IWorkflowEngineService
})
afterEach(afterEach_)
describe("Testing basic workflow", function () {
it("should return a list of workflow executions and remove after completed when there is no retentionTime set", async () => {
await workflowOrcModule.run("workflow_1", {
input: {
@@ -168,7 +168,7 @@ describe("Workflow Orchestrator module", function () {
}
)
await setTimeout(200)
await setTimeoutPromise(200)
expect(transaction.flow.state).toEqual("reverted")
})
@@ -201,4 +201,43 @@ describe("Workflow Orchestrator module", function () {
expect(onFinish).toHaveBeenCalledTimes(0)
})
})
describe("Scheduled workflows", () => {
beforeAll(() => {
jest.useFakeTimers()
jest.spyOn(global, "setTimeout")
})
afterAll(() => {
jest.useRealTimers()
})
it("should execute a scheduled workflow", async () => {
const spy = createScheduled("standard")
await jest.runOnlyPendingTimersAsync()
expect(setTimeout).toHaveBeenCalledTimes(2)
expect(spy).toHaveBeenCalledTimes(1)
await jest.runOnlyPendingTimersAsync()
expect(setTimeout).toHaveBeenCalledTimes(3)
expect(spy).toHaveBeenCalledTimes(2)
})
it("should stop executions after the set number of executions", async () => {
const spy = await createScheduled("num-executions", {
cron: "* * * * * *",
numberOfExecutions: 2,
})
await jest.runOnlyPendingTimersAsync()
expect(spy).toHaveBeenCalledTimes(1)
await jest.runOnlyPendingTimersAsync()
expect(spy).toHaveBeenCalledTimes(2)
await jest.runOnlyPendingTimersAsync()
expect(spy).toHaveBeenCalledTimes(2)
})
})
})