Files
medusa-store/packages/orchestration/src/transaction/transaction-step.ts
Carlos R. L. Rodrigues c0ca002901 feat(orchestration,workflows): pipe oncomplete and workflow preparation (#4697)
* chore: pipe onComplete and workflow preparation step

* changeset

* fix: tests

---------

Co-authored-by: Adrien de Peretti <adrien.deperetti@gmail.com>
Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
2023-08-08 13:06:47 +02:00

162 lines
4.5 KiB
TypeScript

import {
DistributedTransaction,
TransactionPayload,
} from "./distributed-transaction"
import {
TransactionHandlerType,
TransactionState,
TransactionStepStatus,
TransactionStepsDefinition,
} from "./types"
export type TransactionStepHandler = (
actionId: string,
handlerType: TransactionHandlerType,
payload: TransactionPayload,
transaction?: DistributedTransaction
) => Promise<unknown>
/**
* @class TransactionStep
* @classdesc A class representing a single step in a transaction flow
*/
export class TransactionStep {
/**
* @member id - The id of the step
* @member depth - The depth of the step in the flow
* @member definition - The definition of the step
* @member invoke - The current state and status of the invoke action of the step
* @member compensate - The current state and status of the compensate action of the step
* @member attempts - The number of attempts made to execute the step
* @member failures - The number of failures encountered while executing the step
* @member lastAttempt - The timestamp of the last attempt made to execute the step
* @member next - The ids of the next steps in the flow
* @member saveResponse - A flag indicating if the response of a step should be shared in the transaction context and available to subsequent steps - default is true
*/
private stepFailed = false
id: string
depth: number
definition: TransactionStepsDefinition
invoke: {
state: TransactionState
status: TransactionStepStatus
}
compensate: {
state: TransactionState
status: TransactionStepStatus
}
attempts: number
failures: number
lastAttempt: number | null
next: string[]
saveResponse: boolean
public getStates() {
return this.isCompensating() ? this.compensate : this.invoke
}
public beginCompensation() {
if (this.isCompensating()) {
return
}
this.stepFailed = true
this.attempts = 0
this.failures = 0
this.lastAttempt = null
}
public isCompensating() {
return this.stepFailed
}
public changeState(toState: TransactionState) {
const allowed = {
[TransactionState.DORMANT]: [TransactionState.NOT_STARTED],
[TransactionState.NOT_STARTED]: [
TransactionState.INVOKING,
TransactionState.COMPENSATING,
TransactionState.FAILED,
TransactionState.SKIPPED,
],
[TransactionState.INVOKING]: [
TransactionState.FAILED,
TransactionState.DONE,
],
[TransactionState.COMPENSATING]: [
TransactionState.REVERTED,
TransactionState.FAILED,
],
[TransactionState.DONE]: [TransactionState.COMPENSATING],
}
const curState = this.getStates()
if (
curState.state === toState ||
allowed?.[curState.state]?.includes(toState)
) {
curState.state = toState
return
}
throw new Error(
`Updating State from "${curState.state}" to "${toState}" is not allowed.`
)
}
public changeStatus(toStatus: TransactionStepStatus) {
const allowed = {
[TransactionStepStatus.WAITING]: [
TransactionStepStatus.OK,
TransactionStepStatus.TEMPORARY_FAILURE,
TransactionStepStatus.PERMANENT_FAILURE,
],
[TransactionStepStatus.TEMPORARY_FAILURE]: [
TransactionStepStatus.IDLE,
TransactionStepStatus.PERMANENT_FAILURE,
],
[TransactionStepStatus.PERMANENT_FAILURE]: [TransactionStepStatus.IDLE],
}
const curState = this.getStates()
if (
curState.status === toStatus ||
toStatus === TransactionStepStatus.WAITING ||
allowed?.[curState.status]?.includes(toStatus)
) {
curState.status = toStatus
return
}
throw new Error(
`Updating Status from "${curState.status}" to "${toStatus}" is not allowed.`
)
}
canRetry(): boolean {
return !!(
this.lastAttempt &&
this.definition.retryInterval &&
Date.now() - this.lastAttempt > this.definition.retryInterval * 1e3
)
}
canInvoke(flowState: TransactionState): boolean {
const { status, state } = this.getStates()
return (
(!this.isCompensating() &&
state === TransactionState.NOT_STARTED &&
flowState === TransactionState.INVOKING) ||
status === TransactionStepStatus.TEMPORARY_FAILURE
)
}
canCompensate(flowState: TransactionState): boolean {
return (
this.isCompensating() &&
this.getStates().state === TransactionState.NOT_STARTED &&
flowState === TransactionState.COMPENSATING
)
}
}