feat: Add types to the workflow conditional (#7979)

**What**
Add typing to the when conditional workflow step

FIXES CORE-2494
This commit is contained in:
Adrien de Peretti
2024-07-05 16:43:16 +02:00
committed by GitHub
parent b368251ca3
commit b24c8503d5
5 changed files with 88 additions and 9 deletions

View File

@@ -39,6 +39,6 @@
"build": "rimraf dist && tsc --build",
"watch": "tsc --build --watch",
"test": "jest --runInBand --bail --forceExit",
"test:run": "../../node_modules/.bin/ts-node ./src/utils/_playground.ts"
"test:run": "../../../node_modules/.bin/ts-node ./src/utils/_playground.ts"
}
}

View File

@@ -2,6 +2,8 @@ import {
createStep,
createWorkflow,
StepResponse,
transform,
when,
WorkflowData,
} from "./composer"
@@ -29,7 +31,16 @@ const workflow = createWorkflow(
const workflow2 = createWorkflow("workflow", function () {
const step1Res = step1()
step3()
const step3Res = when({ value: true }, ({ value }) => {
return value
}).then(() => {
return step3()
})
transform({ step3Res }, ({ step3Res }) => {
console.log(step3Res)
})
const workflowRes = workflow.asStep({ outsideWorkflowData: step1Res.step1 })

View File

@@ -41,16 +41,19 @@ describe("Workflow composer", () => {
expect(result).toEqual({ result: "hi from outside" })
})
it("should skip step if condition is true", async function () {
it("should skip step if condition is false", async function () {
const step1 = createStep("step1", async (_, context) => {
return new StepResponse({ result: "step1" })
})
const step2 = createStep("step2", async (input: string, context) => {
return new StepResponse({ result: input })
})
const step3 = createStep("step3", async (input: string, context) => {
return new StepResponse({ result: input ?? "default response" })
})
const step3 = createStep(
"step3",
async (input: string | undefined, context) => {
return new StepResponse({ result: input ?? "default response" })
}
)
const subWorkflow = createWorkflow(
getNewWorkflowId(),
@@ -80,6 +83,48 @@ describe("Workflow composer", () => {
expect(result).toEqual({ result: "default response" })
})
it("should not skip step if condition is true", async function () {
const step1 = createStep("step1", async (_, context) => {
return new StepResponse({ result: "step1" })
})
const step2 = createStep("step2", async (input: string, context) => {
return new StepResponse({ result: input })
})
const step3 = createStep(
"step3",
async (input: string | undefined, context) => {
return new StepResponse({ result: input ?? "default response" })
}
)
const subWorkflow = createWorkflow(
getNewWorkflowId(),
function (input: WorkflowData<string>) {
step1()
return step2(input)
}
)
const workflow = createWorkflow(
getNewWorkflowId(),
function (input: { callSubFlow: boolean }) {
const subWorkflowRes = when({ input }, ({ input }) => {
return input.callSubFlow
}).then(() => {
return subWorkflow.runAsStep({
input: "hi from outside",
})
})
return step3(subWorkflowRes.result)
}
)
const { result } = await workflow.run({ input: { callSubFlow: true } })
expect(result).toEqual({ result: "hi from outside" })
})
it("should revert the workflow and sub workflow on failure", async function () {
const step1Mock = jest.fn()
const step1 = createStep(

View File

@@ -4,9 +4,9 @@ import {
WorkflowStepHandler,
WorkflowStepHandlerArguments,
} from "@medusajs/orchestration"
import { OrchestrationUtils, deepCopy, isString } from "@medusajs/utils"
import { deepCopy, isString, OrchestrationUtils } from "@medusajs/utils"
import { ulid } from "ulid"
import { StepResponse, resolveValue } from "./helpers"
import { resolveValue, StepResponse } from "./helpers"
import { proxify } from "./helpers/proxy"
import {
CreateWorkflowComposerContext,
@@ -346,7 +346,7 @@ function wrapConditionalStep(
const originalInvoke = handle.invoke
handle.invoke = async (stepArguments: WorkflowStepHandlerArguments) => {
const args = await resolveValue(input, stepArguments)
const canContinue = await condition(args)
const canContinue = await condition(args, stepArguments)
if (stepArguments.step.definition?.async) {
stepArguments.step.definition.backgroundExecution = true

View File

@@ -1,4 +1,27 @@
import { OrchestrationUtils } from "@medusajs/utils"
import { StepExecutionContext, WorkflowData } from "./type"
type ConditionFunction<T extends object | WorkflowData> = (
input: T extends WorkflowData<infer U>
? U
: T extends object
? { [K in keyof T]: T[K] extends WorkflowData<infer U> ? U : T[K] }
: {},
context: StepExecutionContext
) => boolean
type ThenFunc = <ThenResolver extends () => any>(
resolver: ThenResolver
) => ReturnType<ThenResolver> extends WorkflowData<infer ReturnedWorkflowData>
? Partial<WorkflowData<ReturnedWorkflowData>>
: ReturnType<ThenResolver>
export function when<T extends object | WorkflowData, Then extends Function>(
values: T,
condition: ConditionFunction<T>
): {
then: ThenFunc
}
export function when(input, condition) {
global[OrchestrationUtils.SymbolMedusaWorkflowComposerCondition] = {