Skip to content

Commit

Permalink
feat: Add types to the workflow conditional (#7979)
Browse files Browse the repository at this point in the history
**What**
Add typing to the when conditional workflow step

FIXES CORE-2494
  • Loading branch information
adrien2p committed Jul 5, 2024
1 parent b368251 commit b24c850
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 9 deletions.
2 changes: 1 addition & 1 deletion packages/core/workflows-sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
13 changes: 12 additions & 1 deletion packages/core/workflows-sdk/src/utils/_playground.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {
createStep,
createWorkflow,
StepResponse,
transform,
when,
WorkflowData,
} from "./composer"

Expand Down Expand Up @@ -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 })

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand Down Expand Up @@ -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(
Expand Down
6 changes: 3 additions & 3 deletions packages/core/workflows-sdk/src/utils/composer/create-step.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
23 changes: 23 additions & 0 deletions packages/core/workflows-sdk/src/utils/composer/when.ts
Original file line number Diff line number Diff line change
@@ -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] = {
Expand Down

0 comments on commit b24c850

Please sign in to comment.