feat(orchestration,workflows-sdk): Skip step (#8334)

This commit is contained in:
Carlos R. L. Rodrigues authored and GitHub committed 2024-07-29 14:26:47 -03:00
1 parent e98012a858
commit 24c105f288
17 files changed
+267 -28

No files matched your search

@@ -2063,6 +2063,8 @@
"runningState": "Running...", "runningState": "Running...",
"awaitingState": "Awaiting", "awaitingState": "Awaiting",
"failedState": "Failed", "failedState": "Failed",
"skippedState": "Skipped",
"skippedFailureState": "Skipped (Failure)",
"definitionLabel": "Definition", "definitionLabel": "Definition",
"outputLabel": "Output", "outputLabel": "Output",
"compensateInputLabel": "Compensate input", "compensateInputLabel": "Compensate input",
@@ -2085,6 +2087,7 @@
"step": { "step": {
"state": { "state": {
"skipped": "Skipped", "skipped": "Skipped",
"skippedFailure": "Skipped (Failure)",
"dormant": "Dormant", "dormant": "Dormant",
"timeout": "Timeout" "timeout": "Timeout"
} }
@@ -4,13 +4,18 @@ export const STEP_IN_PROGRESS_STATES = [
TransactionStepState.COMPENSATING, TransactionStepState.COMPENSATING,
TransactionStepState.INVOKING, TransactionStepState.INVOKING,
] ]
export const STEP_SKIPPED_STATES = [
TransactionStepState.SKIPPED,
TransactionStepState.SKIPPED_FAILURE,
]
export const STEP_OK_STATES = [TransactionStepState.DONE] export const STEP_OK_STATES = [TransactionStepState.DONE]
export const STEP_ERROR_STATES = [ export const STEP_ERROR_STATES = [
TransactionStepState.FAILED, TransactionStepState.FAILED,
TransactionStepState.REVERTED, TransactionStepState.REVERTED,
TransactionStepState.TIMEOUT, TransactionStepState.TIMEOUT,
TransactionStepState.DORMANT, TransactionStepState.DORMANT,
TransactionStepState.SKIPPED,
] ]
export const STEP_INACTIVE_STATES = [TransactionStepState.NOT_STARTED] export const STEP_INACTIVE_STATES = [TransactionStepState.NOT_STARTED]
@@ -91,5 +91,6 @@ export enum TransactionStepState {
FAILED = "failed", FAILED = "failed",
DORMANT = "dormant", DORMANT = "dormant",
SKIPPED = "skipped", SKIPPED = "skipped",
SKIPPED_FAILURE = "skipped_failure",
TIMEOUT = "timeout", TIMEOUT = "timeout",
} }
@@ -93,6 +93,8 @@ export const getStepState = (
return t("workflowExecutions.state.notStarted") return t("workflowExecutions.state.notStarted")
case TransactionStepState.SKIPPED: case TransactionStepState.SKIPPED:
return t("workflowExecutions.step.state.skipped") return t("workflowExecutions.step.state.skipped")
case TransactionStepState.SKIPPED_FAILURE:
return t("workflowExecutions.step.state.skippedFailure")
case TransactionStepState.DORMANT: case TransactionStepState.DORMANT:
return t("workflowExecutions.step.state.dormant") return t("workflowExecutions.step.state.dormant")
case TransactionStepState.TIMEOUT: case TransactionStepState.TIMEOUT:
@@ -17,6 +17,7 @@ import {
STEP_INACTIVE_STATES, STEP_INACTIVE_STATES,
STEP_IN_PROGRESS_STATES, STEP_IN_PROGRESS_STATES,
STEP_OK_STATES, STEP_OK_STATES,
STEP_SKIPPED_STATES,
} from "../../../constants" } from "../../../constants"
import { import {
StepError, StepError,
@@ -132,6 +133,9 @@ const Event = ({
<div className="bg-ui-bg-base shadow-borders-base flex size-2.5 items-center justify-center rounded-full"> <div className="bg-ui-bg-base shadow-borders-base flex size-2.5 items-center justify-center rounded-full">
<div <div
className={clx("size-1.5 rounded-full", { className={clx("size-1.5 rounded-full", {
"bg-ui-tag-neutral-bg": STEP_SKIPPED_STATES.includes(
step.invoke.state
),
"bg-ui-tag-green-icon": STEP_OK_STATES.includes( "bg-ui-tag-green-icon": STEP_OK_STATES.includes(
step.invoke.state step.invoke.state
), ),
@@ -204,7 +208,8 @@ const Event = ({
snippets={[ snippets={[
{ {
code: JSON.stringify( code: JSON.stringify(
stepInvokeContext.output.output, // TODO: Apply resolve value: packages/core/workflows-sdk/src/utils/composer/helpers/resolve-value.ts
stepInvokeContext?.output?.output ?? {},
null, null,
2 2
), ),
@@ -227,8 +232,9 @@ const Event = ({
<CodeBlock <CodeBlock
snippets={[ snippets={[
{ {
// TODO: Apply resolve value: packages/core/workflows-sdk/src/utils/composer/helpers/resolve-value.ts
code: JSON.stringify( code: JSON.stringify(
stepInvokeContext.output.compensateInput, stepInvokeContext?.output?.compensateInput ?? {},
null, null,
2 2
), ),
@@ -290,6 +296,8 @@ const StepState = ({
const isFailed = state === TransactionStepState.FAILED const isFailed = state === TransactionStepState.FAILED
const isRunning = state === TransactionStepState.INVOKING const isRunning = state === TransactionStepState.INVOKING
const isSkipped = state === TransactionStepState.SKIPPED
const isSkippedFailure = state === TransactionStepState.SKIPPED_FAILURE
if (isUnreachable) { if (isUnreachable) {
return null return null
@@ -306,10 +314,20 @@ const StepState = ({
) )
} }
if (isFailed) { let stateText: string | undefined
if (isSkipped) {
stateText = t("workflowExecutions.history.skippedState")
} else if (isSkippedFailure) {
stateText = t("workflowExecutions.history.skippedFailureState")
} else if (isFailed) {
stateText = t("workflowExecutions.history.failedState")
}
if (stateText !== null) {
return ( return (
<Text size="small" leading="compact" className="text-ui-fg-subtle"> <Text size="small" leading="compact" className="text-ui-fg-subtle">
{t("workflowExecutions.history.failedState")} {stateText}
</Text> </Text>
) )
} }
@@ -15,6 +15,7 @@ import {
STEP_INACTIVE_STATES, STEP_INACTIVE_STATES,
STEP_IN_PROGRESS_STATES, STEP_IN_PROGRESS_STATES,
STEP_OK_STATES, STEP_OK_STATES,
STEP_SKIPPED_STATES,
} from "../../../constants" } from "../../../constants"
import { WorkflowExecutionDTO, WorkflowExecutionStep } from "../../../types" import { WorkflowExecutionDTO, WorkflowExecutionStep } from "../../../types"
@@ -405,6 +406,9 @@ const Node = ({ step }: { step: WorkflowExecutionStep }) => {
className={clx( className={clx(
"size-2 rounded-sm shadow-[inset_0_0_0_1px_rgba(0,0,0,0.12)]", "size-2 rounded-sm shadow-[inset_0_0_0_1px_rgba(0,0,0,0.12)]",
{ {
"bg-ui-tag-neutral-bg": STEP_SKIPPED_STATES.includes(
step.invoke.state
),
"bg-ui-tag-green-icon": STEP_OK_STATES.includes( "bg-ui-tag-green-icon": STEP_OK_STATES.includes(
step.invoke.state step.invoke.state
), ),
@@ -1207,7 +1207,7 @@ describe("Transaction Orchestrator", () => {
expect( expect(
transaction.getFlow().steps["_root.action1.action2.action4"].invoke transaction.getFlow().steps["_root.action1.action2.action4"].invoke
.state .state
).toBe(TransactionStepState.SKIPPED) ).toBe(TransactionStepState.SKIPPED_FAILURE)
expect( expect(
transaction.getFlow().steps["_root.action1.action2.action4"].invoke transaction.getFlow().steps["_root.action1.action2.action4"].invoke
.status .status
@@ -14,6 +14,19 @@ export class PermanentStepFailureError extends Error {
} }
} }
export class SkipStepResponse extends Error {
static isSkipStepResponse(error: Error): error is SkipStepResponse {
return (
error instanceof SkipStepResponse || error?.name === "SkipStepResponse"
)
}
constructor(message?: string) {
super(message)
this.name = "SkipStepResponse"
}
}
export class TransactionStepTimeoutError extends Error { export class TransactionStepTimeoutError extends Error {
static isTransactionStepTimeoutError( static isTransactionStepTimeoutError(
error: Error error: Error
@@ -21,6 +21,7 @@ import {
isErrorLike, isErrorLike,
PermanentStepFailureError, PermanentStepFailureError,
serializeError, serializeError,
SkipStepResponse,
TransactionStepTimeoutError, TransactionStepTimeoutError,
TransactionTimeoutError, TransactionTimeoutError,
} from "./errors" } from "./errors"
@@ -36,6 +37,7 @@ export type TransactionFlow = {
} }
hasAsyncSteps: boolean hasAsyncSteps: boolean
hasFailedSteps: boolean hasFailedSteps: boolean
hasSkippedOnFailureSteps: boolean
hasWaitingSteps: boolean hasWaitingSteps: boolean
hasSkippedSteps: boolean hasSkippedSteps: boolean
hasRevertedSteps: boolean hasRevertedSteps: boolean
@@ -125,6 +127,7 @@ export class TransactionOrchestrator extends EventEmitter {
TransactionStepState.FAILED, TransactionStepState.FAILED,
TransactionStepState.TIMEOUT, TransactionStepState.TIMEOUT,
TransactionStepState.SKIPPED, TransactionStepState.SKIPPED,
TransactionStepState.SKIPPED_FAILURE,
] ]
const siblings = this.getPreviousStep(flow, previousStep).next.map( const siblings = this.getPreviousStep(flow, previousStep).next.map(
@@ -143,6 +146,7 @@ export class TransactionOrchestrator extends EventEmitter {
TransactionStepState.REVERTED, TransactionStepState.REVERTED,
TransactionStepState.FAILED, TransactionStepState.FAILED,
TransactionStepState.DORMANT, TransactionStepState.DORMANT,
TransactionStepState.SKIPPED,
] ]
const siblings = step.next.map((sib) => flow.steps[sib]) const siblings = step.next.map((sib) => flow.steps[sib])
return ( return (
@@ -253,6 +257,7 @@ export class TransactionOrchestrator extends EventEmitter {
completed: number completed: number
}> { }> {
let hasSkipped = false let hasSkipped = false
let hasSkippedOnFailure = false
let hasIgnoredFailure = false let hasIgnoredFailure = false
let hasFailed = false let hasFailed = false
let hasWaiting = false let hasWaiting = false
@@ -328,7 +333,9 @@ export class TransactionOrchestrator extends EventEmitter {
} else { } else {
completedSteps++ completedSteps++
if (curState.state === TransactionStepState.SKIPPED) { if (curState.state === TransactionStepState.SKIPPED_FAILURE) {
hasSkippedOnFailure = true
} else if (curState.state === TransactionStepState.SKIPPED) {
hasSkipped = true hasSkipped = true
} else if (curState.state === TransactionStepState.REVERTED) { } else if (curState.state === TransactionStepState.REVERTED) {
hasReverted = true hasReverted = true
@@ -358,6 +365,9 @@ export class TransactionOrchestrator extends EventEmitter {
return await this.checkAllSteps(transaction) return await this.checkAllSteps(transaction)
} else if (completedSteps === totalSteps) { } else if (completedSteps === totalSteps) {
if (hasSkippedOnFailure) {
flow.hasSkippedOnFailureSteps = true
}
if (hasSkipped) { if (hasSkipped) {
flow.hasSkippedSteps = true flow.hasSkippedSteps = true
} }
@@ -453,6 +463,39 @@ export class TransactionOrchestrator extends EventEmitter {
transaction.emit(eventName, { step, transaction }) transaction.emit(eventName, { step, transaction })
} }
private static async skipStep(
transaction: DistributedTransaction,
step: TransactionStep
): Promise<void> {
const hasStepTimedOut =
step.getStates().state === TransactionStepState.TIMEOUT
const flow = transaction.getFlow()
const options = TransactionOrchestrator.getWorkflowOptions(flow.modelId)
if (!hasStepTimedOut) {
step.changeStatus(TransactionStepStatus.OK)
step.changeState(TransactionStepState.SKIPPED)
}
if (step.definition.async || options?.storeExecution) {
await transaction.saveCheckpoint()
}
const cleaningUp: Promise<unknown>[] = []
if (step.hasRetryScheduled()) {
cleaningUp.push(transaction.clearRetry(step))
}
if (step.hasTimeout()) {
cleaningUp.push(transaction.clearStepTimeout(step))
}
await promiseAll(cleaningUp)
const eventName = DistributedTransactionEvent.STEP_SKIPPED
transaction.emit(eventName, { step, transaction })
}
private static async setStepTimeout( private static async setStepTimeout(
transaction: DistributedTransaction, transaction: DistributedTransaction,
step: TransactionStep, step: TransactionStep,
@@ -539,7 +582,7 @@ export class TransactionOrchestrator extends EventEmitter {
) { ) {
for (const childStep of step.next) { for (const childStep of step.next) {
const child = flow.steps[childStep] const child = flow.steps[childStep]
child.changeState(TransactionStepState.SKIPPED) child.changeState(TransactionStepState.SKIPPED_FAILURE)
} }
} else { } else {
flow.state = TransactionState.WAITING_TO_COMPENSATE flow.state = TransactionState.WAITING_TO_COMPENSATE
@@ -701,6 +744,12 @@ export class TransactionOrchestrator extends EventEmitter {
) )
} }
const output = response?.__type ? response.output : response
if (SkipStepResponse.isSkipStepResponse(output)) {
await TransactionOrchestrator.skipStep(transaction, step)
return
}
await TransactionOrchestrator.setStepSuccess( await TransactionOrchestrator.setStepSuccess(
transaction, transaction,
step, step,
@@ -754,11 +803,19 @@ export class TransactionOrchestrator extends EventEmitter {
) )
} }
let setResponse = true
if (SkipStepResponse.isSkipStepResponse(response)) {
await TransactionOrchestrator.skipStep(transaction, step)
setResponse = false
}
if (setResponse) {
await TransactionOrchestrator.setStepSuccess( await TransactionOrchestrator.setStepSuccess(
transaction, transaction,
step, step,
response response
) )
}
await transaction.scheduleRetry( await transaction.scheduleRetry(
step, step,
@@ -912,6 +969,7 @@ export class TransactionOrchestrator extends EventEmitter {
metadata: flowMetadata, metadata: flowMetadata,
hasAsyncSteps: features.hasAsyncSteps, hasAsyncSteps: features.hasAsyncSteps,
hasFailedSteps: false, hasFailedSteps: false,
hasSkippedOnFailureSteps: false,
hasSkippedSteps: false, hasSkippedSteps: false,
hasWaitingSteps: false, hasWaitingSteps: false,
hasRevertedSteps: false, hasRevertedSteps: false,
@@ -1176,6 +1234,41 @@ export class TransactionOrchestrator extends EventEmitter {
return [transaction, step] return [transaction, step]
} }
/** Skip the execution of a specific transaction and step
* @param responseIdempotencyKey - The idempotency key for the step
* @param handler - The handler function to execute the step
* @param transaction - The current transaction. If not provided it will be loaded based on the responseIdempotencyKey
*/
public async skipStep(
responseIdempotencyKey: string,
handler?: TransactionStepHandler,
transaction?: DistributedTransaction
): Promise<DistributedTransaction> {
const [curTransaction, step] =
await TransactionOrchestrator.getTransactionAndStepFromIdempotencyKey(
responseIdempotencyKey,
handler,
transaction
)
if (step.getStates().status === TransactionStepStatus.WAITING) {
this.emit(DistributedTransactionEvent.RESUME, {
transaction: curTransaction,
})
await TransactionOrchestrator.skipStep(curTransaction, step)
await this.executeNext(curTransaction)
} else {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
`Cannot skip a step when status is ${step.getStates().status}`
)
}
return curTransaction
}
/** Register a step success for a specific transaction and step /** Register a step success for a specific transaction and step
* @param responseIdempotencyKey - The idempotency key for the step * @param responseIdempotencyKey - The idempotency key for the step
* @param handler - The handler function to execute the step * @param handler - The handler function to execute the step
@@ -93,11 +93,13 @@ export class TransactionStep {
TransactionStepState.COMPENSATING, TransactionStepState.COMPENSATING,
TransactionStepState.FAILED, TransactionStepState.FAILED,
TransactionStepState.SKIPPED, TransactionStepState.SKIPPED,
TransactionStepState.SKIPPED_FAILURE,
], ],
[TransactionStepState.INVOKING]: [ [TransactionStepState.INVOKING]: [
TransactionStepState.FAILED, TransactionStepState.FAILED,
TransactionStepState.DONE, TransactionStepState.DONE,
TransactionStepState.TIMEOUT, TransactionStepState.TIMEOUT,
TransactionStepState.SKIPPED,
], ],
[TransactionStepState.COMPENSATING]: [ [TransactionStepState.COMPENSATING]: [
TransactionStepState.REVERTED, TransactionStepState.REVERTED,
@@ -24,7 +24,7 @@ export type TransactionStepsDefinition = {
/** /**
* Indicates whether the workflow should continue even if there is a permanent failure in this step. * Indicates whether the workflow should continue even if there is a permanent failure in this step.
* In case it is set to true, the children steps of this step will not be executed and their status will be marked as TransactionStepState.SKIPPED. * In case it is set to true, the children steps of this step will not be executed and their status will be marked as TransactionStepState.SKIPPED_FAILURE.
*/ */
continueOnPermanentFailure?: boolean continueOnPermanentFailure?: boolean
@@ -164,6 +164,7 @@ export enum DistributedTransactionEvent {
TIMEOUT = "timeout", TIMEOUT = "timeout",
STEP_BEGIN = "stepBegin", STEP_BEGIN = "stepBegin",
STEP_SUCCESS = "stepSuccess", STEP_SUCCESS = "stepSuccess",
STEP_SKIPPED = "stepSkipped",
STEP_FAILURE = "stepFailure", STEP_FAILURE = "stepFailure",
STEP_AWAITING = "stepAwaiting", STEP_AWAITING = "stepAwaiting",
COMPENSATE_STEP_SUCCESS = "compensateStepSuccess", COMPENSATE_STEP_SUCCESS = "compensateStepSuccess",
@@ -211,6 +212,11 @@ export type DistributedTransactionEvents = {
step: TransactionStep step: TransactionStep
transaction: DistributedTransaction transaction: DistributedTransaction
}) => void }) => void
onStepSkipped?: (args: {
step: TransactionStep
transaction: DistributedTransaction
}) => void
} }
export type StepFeatures = { export type StepFeatures = {
@@ -1,12 +1,12 @@
import { Context, LoadedModule, MedusaContainer } from "@medusajs/types" import { Context, LoadedModule, MedusaContainer } from "@medusajs/types"
import { import {
createMedusaContainer,
isDefined,
isString,
MedusaContext, MedusaContext,
MedusaContextType, MedusaContextType,
MedusaError, MedusaError,
MedusaModuleType, MedusaModuleType,
createMedusaContainer,
isDefined,
isString,
} from "@medusajs/utils" } from "@medusajs/utils"
import { asValue } from "awilix" import { asValue } from "awilix"
import { import {
@@ -281,6 +281,13 @@ export class LocalWorkflow {
eventWrapperMap.get("onCompensateStepFailure") eventWrapperMap.get("onCompensateStepFailure")
) )
} }
if (subscribe?.onStepSkipped) {
transaction.on(
DistributedTransactionEvent.STEP_SKIPPED,
eventWrapperMap.get("onStepSkipped")
)
}
} }
if (transaction) { if (transaction) {
@@ -3,14 +3,6 @@ export enum TransactionHandlerType {
COMPENSATE = "compensate", COMPENSATE = "compensate",
} }
export enum TransactionStepStatus {
IDLE = "idle",
OK = "ok",
WAITING = "waiting_response",
TEMPORARY_FAILURE = "temp_failure",
PERMANENT_FAILURE = "permanent_failure",
}
export enum TransactionState { export enum TransactionState {
NOT_STARTED = "not_started", NOT_STARTED = "not_started",
INVOKING = "invoking", INVOKING = "invoking",
@@ -30,5 +22,14 @@ export enum TransactionStepState {
FAILED = "failed", FAILED = "failed",
DORMANT = "dormant", DORMANT = "dormant",
SKIPPED = "skipped", SKIPPED = "skipped",
SKIPPED_FAILURE = "skipped_failure",
TIMEOUT = "timeout", TIMEOUT = "timeout",
} }
export enum TransactionStepStatus {
IDLE = "idle",
OK = "ok",
WAITING = "waiting_response",
TEMPORARY_FAILURE = "temp_failure",
PERMANENT_FAILURE = "permanent_failure",
}
@@ -1,20 +1,20 @@
import { WorkflowManager, WorkflowScheduler } from "@medusajs/orchestration" import { WorkflowManager, WorkflowScheduler } from "@medusajs/orchestration"
import { import {
ModuleRegistrationName,
composeMessage, composeMessage,
createMedusaContainer, createMedusaContainer,
ModuleRegistrationName,
promiseAll, promiseAll,
} from "@medusajs/utils" } from "@medusajs/utils"
import { asValue } from "awilix"
import { import {
StepResponse,
createStep, createStep,
createWorkflow, createWorkflow,
hook, hook,
parallelize, parallelize,
StepResponse,
transform, transform,
} from ".." } from ".."
import { MedusaWorkflow } from "../../../medusa-workflow" import { MedusaWorkflow } from "../../../medusa-workflow"
import { asValue } from "awilix"
import { IDistributedSchedulerStorage, SchedulerOptions } from "../../dist" import { IDistributedSchedulerStorage, SchedulerOptions } from "../../dist"
jest.setTimeout(30000) jest.setTimeout(30000)
@@ -1113,6 +1113,82 @@ describe("Workflow composer", function () {
}) })
}) })
it("should compose a new workflow and skip steps depending on the input", async () => {
const mockStep1Fn = jest.fn().mockImplementation((input) => {
if (input === 1) {
return StepResponse.skip()
} else {
return new StepResponse({ obj: "return from 1" })
}
})
const mockStep2Fn = jest.fn().mockImplementation((input) => {
if (!input) {
return StepResponse.skip()
}
})
const mockStep3Fn = jest.fn().mockImplementation((inputs) => {
return new StepResponse({
inputs,
obj: "return from 3",
})
})
const step1 = createStep("step1", mockStep1Fn)
const step2 = createStep("step2", mockStep2Fn)
const step3 = createStep("step3", mockStep3Fn)
const workflow = createWorkflow("workflow1", function (input) {
const returnStep1 = step1(input)
const ret2 = step2(returnStep1)
return step3({ one: returnStep1, two: ret2, input })
})
const { result: workflowResult, transaction } = await workflow().run({
input: 1,
})
expect(transaction.getFlow().hasSkippedSteps).toBe(true)
expect(mockStep3Fn.mock.calls[0][0]).toEqual({
input: 1,
})
expect(workflowResult).toEqual({
inputs: {
input: 1,
},
obj: "return from 3",
})
const { result: workflowResultTwo, transaction: transactionTwo } =
await workflow().run({
input: "none",
})
expect(transactionTwo.getFlow().hasSkippedSteps).toBe(false)
expect(mockStep3Fn.mock.calls[1][0]).toEqual({
one: {
obj: "return from 1",
},
two: {
__type: "Symbol(WorkflowWorkflowData)",
},
input: "none",
})
expect(workflowResultTwo).toEqual({
inputs: {
one: {
obj: "return from 1",
},
two: {
__type: "Symbol(WorkflowWorkflowData)",
},
input: "none",
},
obj: "return from 3",
})
})
it("should compose two new workflows sequentially and execute them sequentially", async () => { it("should compose two new workflows sequentially and execute them sequentially", async () => {
const mockStep1Fn = jest.fn().mockImplementation((input, context) => { const mockStep1Fn = jest.fn().mockImplementation((input, context) => {
return new StepResponse({ inputs: [input], obj: "return from 1" }) return new StepResponse({ inputs: [input], obj: "return from 1" })
@@ -4,9 +4,9 @@ import {
WorkflowStepHandler, WorkflowStepHandler,
WorkflowStepHandlerArguments, WorkflowStepHandlerArguments,
} from "@medusajs/orchestration" } from "@medusajs/orchestration"
import { deepCopy, isString, OrchestrationUtils } from "@medusajs/utils" import { OrchestrationUtils, deepCopy, isString } from "@medusajs/utils"
import { ulid } from "ulid" import { ulid } from "ulid"
import { resolveValue, StepResponse } from "./helpers" import { StepResponse, resolveValue } from "./helpers"
import { proxify } from "./helpers/proxy" import { proxify } from "./helpers/proxy"
import { import {
CreateWorkflowComposerContext, CreateWorkflowComposerContext,
@@ -353,7 +353,7 @@ function wrapConditionalStep(
} }
if (!canContinue) { if (!canContinue) {
return return StepResponse.skip()
} }
return await originalInvoke(stepArguments) return await originalInvoke(stepArguments)
@@ -1,5 +1,8 @@
import { PermanentStepFailureError } from "@medusajs/orchestration" import {
import { isDefined, OrchestrationUtils } from "@medusajs/utils" PermanentStepFailureError,
SkipStepResponse,
} from "@medusajs/orchestration"
import { OrchestrationUtils, isDefined } from "@medusajs/utils"
/** /**
* This class is used to create the response returned by a step. A step return its data by returning an instance of `StepResponse`. * This class is used to create the response returned by a step. A step return its data by returning an instance of `StepResponse`.
@@ -116,6 +119,10 @@ export class StepResponse<TOutput, TCompensateInput = TOutput> {
throw new PermanentStepFailureError(message) throw new PermanentStepFailureError(message)
} }
static skip(): SkipStepResponse {
return new SkipStepResponse()
}
/** /**
* @internal * @internal
*/ */
@@ -50,6 +50,7 @@ export function when(input, condition) {
} }
delete global[OrchestrationUtils.SymbolMedusaWorkflowComposerCondition] delete global[OrchestrationUtils.SymbolMedusaWorkflowComposerCondition]
return ret return ret
}, },
} }