Files
medusa-store/packages/orchestration/src/workflow/local-workflow.ts
Carlos R. L. Rodrigues f12299deb1 Feat: @medusajs/workflows (#4553)
feat: medusa workflows
2023-07-25 10:13:14 -03:00

210 lines
5.4 KiB
TypeScript

import { Context, LoadedModule, MedusaContainer } from "@medusajs/types"
import {
DistributedTransaction,
TransactionOrchestrator,
TransactionStepsDefinition,
} from "../transaction"
import {
WorkflowDefinition,
WorkflowManager,
WorkflowStepHandler,
} from "./workflow-manager"
import { OrchestratorBuilder } from "../transaction/orchestrator-builder"
import { asValue } from "awilix"
import { createMedusaContainer } from "@medusajs/utils"
type StepHandler = {
invoke: WorkflowStepHandler
compensate?: WorkflowStepHandler
}
export class LocalWorkflow {
protected container: MedusaContainer
protected workflowId: string
protected flow: OrchestratorBuilder
protected workflow: WorkflowDefinition
protected handlers: Map<string, StepHandler>
constructor(
workflowId: string,
modulesLoaded?: LoadedModule[] | MedusaContainer
) {
const globalWorkflow = WorkflowManager.getWorkflow(workflowId)
if (!globalWorkflow) {
throw new Error(`Workflow with id "${workflowId}" not found.`)
}
this.flow = new OrchestratorBuilder(globalWorkflow.flow_)
this.workflowId = workflowId
this.workflow = globalWorkflow
this.handlers = new Map(globalWorkflow.handlers_)
const container = createMedusaContainer()
// Medusa container
if (!Array.isArray(modulesLoaded) && modulesLoaded) {
const cradle = modulesLoaded.cradle
for (const key in cradle) {
container.register(key, asValue(cradle[key]))
}
}
// Array of modules
else if (modulesLoaded?.length) {
for (const mod of modulesLoaded) {
const registrationName = mod.__definition.registrationName
container.register(registrationName, asValue(mod))
}
}
this.container = container
}
protected commit() {
const finalFlow = this.flow.build()
this.workflow = {
id: this.workflowId,
flow_: finalFlow,
orchestrator: new TransactionOrchestrator(this.workflowId, finalFlow),
handler: WorkflowManager.buildHandlers(this.handlers),
handlers_: this.handlers,
}
}
async run(uniqueTransactionId: string, input?: unknown, context?: Context) {
if (this.flow.hasChanges) {
this.commit()
}
const { handler, orchestrator } = this.workflow
const transaction = await orchestrator.beginTransaction(
uniqueTransactionId,
handler(this.container, context),
input
)
await orchestrator.resume(transaction)
return transaction
}
async registerStepSuccess(
idempotencyKey: string,
response?: unknown,
context?: Context
): Promise<DistributedTransaction> {
const { handler, orchestrator } = this.workflow
return await orchestrator.registerStepSuccess(
idempotencyKey,
handler(this.container, context),
undefined,
response
)
}
async registerStepFailure(
idempotencyKey: string,
error?: Error | any,
context?: Context
): Promise<DistributedTransaction> {
const { handler, orchestrator } = this.workflow
return await orchestrator.registerStepFailure(
idempotencyKey,
error,
handler(this.container, context)
)
}
addAction(
action: string,
handler: StepHandler,
options: Partial<TransactionStepsDefinition> = {}
) {
this.assertHandler(handler, action)
this.handlers.set(action, handler)
return this.flow.addAction(action, options)
}
replaceAction(
existingAction: string,
action: string,
handler: StepHandler,
options: Partial<TransactionStepsDefinition> = {}
) {
this.assertHandler(handler, action)
this.handlers.set(action, handler)
return this.flow.replaceAction(existingAction, action, options)
}
insertActionBefore(
existingAction: string,
action: string,
handler: StepHandler,
options: Partial<TransactionStepsDefinition> = {}
) {
this.assertHandler(handler, action)
this.handlers.set(action, handler)
return this.flow.insertActionBefore(existingAction, action, options)
}
insertActionAfter(
existingAction: string,
action: string,
handler: StepHandler,
options: Partial<TransactionStepsDefinition> = {}
) {
this.assertHandler(handler, action)
this.handlers.set(action, handler)
return this.flow.insertActionAfter(existingAction, action, options)
}
appendAction(
action: string,
to: string,
handler: StepHandler,
options: Partial<TransactionStepsDefinition> = {}
) {
this.assertHandler(handler, action)
this.handlers.set(action, handler)
return this.flow.appendAction(action, to, options)
}
moveAction(actionToMove: string, targetAction: string): OrchestratorBuilder {
return this.flow.moveAction(actionToMove, targetAction)
}
moveAndMergeNextAction(
actionToMove: string,
targetAction: string
): OrchestratorBuilder {
return this.flow.moveAndMergeNextAction(actionToMove, targetAction)
}
mergeActions(where: string, ...actions: string[]) {
return this.flow.mergeActions(where, ...actions)
}
deleteAction(action: string, parentSteps?) {
return this.flow.deleteAction(action, parentSteps)
}
pruneAction(action: string) {
return this.flow.pruneAction(action)
}
protected assertHandler(handler: StepHandler, action: string): void | never {
if (!handler?.invoke) {
throw new Error(
`Handler for action "${action}" is missing invoke function.`
)
}
}
}