docs: fixes to long-running workflows + relationship docs (#9101)
This commit is contained in:
@@ -108,7 +108,7 @@ For example:
|
||||
|
||||
export const manyToManyHighlights = [
|
||||
["5", "manyToMany", "An order is associated with many products."],
|
||||
["10", "manyToMany", "A product is associated with many orders."]
|
||||
["12", "manyToMany", "A product is associated with many orders."]
|
||||
]
|
||||
|
||||
```ts highlights={manyToManyHighlights}
|
||||
@@ -116,15 +116,21 @@ import { model } from "@medusajs/utils"
|
||||
|
||||
const Order = model.define("order", {
|
||||
id: model.id().primaryKey(),
|
||||
products: model.manyToMany(() => Product),
|
||||
products: model.manyToMany(() => Product, {
|
||||
mappedBy: "orders"
|
||||
}),
|
||||
})
|
||||
|
||||
const Product = model.define("product", {
|
||||
id: model.id().primaryKey(),
|
||||
orders: model.manyToMany(() => Order),
|
||||
orders: model.manyToMany(() => Order, {
|
||||
mappedBy: "orders"
|
||||
}),
|
||||
})
|
||||
```
|
||||
|
||||
At least one side of the many-to-many relationship should have the `mappedBy` property set in the second object parameter of the `manyToMany` object. Its value is the name of the relationship property in the other data model.
|
||||
|
||||
In this example, an order is associated with many products, and a product is associated with many orders.
|
||||
|
||||
---
|
||||
|
||||
@@ -18,7 +18,7 @@ A long-running workflow is a workflow that continues its execution in the backgr
|
||||
|
||||
## Configure Long-Running Workflows
|
||||
|
||||
A workflow is considered long-running if at least one step has its `async` configuration set to `true`.
|
||||
A workflow is considered long-running if at least one step has its `async` configuration set to `true` and doesn't return a step response.
|
||||
|
||||
For example, consider the following workflow and steps:
|
||||
|
||||
@@ -40,7 +40,7 @@ const step2 = createStep(
|
||||
async: true,
|
||||
},
|
||||
async () => {
|
||||
return new StepResponse({})
|
||||
console.log("Waiting to be successful...")
|
||||
}
|
||||
)
|
||||
|
||||
@@ -63,21 +63,211 @@ const myWorkflow = createWorkflow(
|
||||
export default myWorkflow
|
||||
```
|
||||
|
||||
The second step has in its configuration object `async` set to true. This indicates that this step is an asynchronous step.
|
||||
|
||||
<Note title="Important">
|
||||
|
||||
An asynchronous step must return for the execution to continue.
|
||||
|
||||
</Note>
|
||||
The second step has in its configuration object `async` set to `true` and it doesn't return a step response. This indicates that this step is an asynchronous step.
|
||||
|
||||
So, when you execute the `hello-world` workflow, it continues its execution in the background once it reaches the second step.
|
||||
|
||||
---
|
||||
|
||||
## Change Step Status
|
||||
|
||||
Once the workflow's execution reaches an async step, it'll wait in the background for the step to succeed or fail before it moves to the rest of the steps.
|
||||
|
||||
To change fail or succeed a step, use the workflow engine's main service that is registered in the Medusa Container under the `ModuleRegistrationName.WORKFLOW_ENGINE` (or `workflowsModuleService`) key.
|
||||
|
||||
### Retrieve Transaction ID
|
||||
|
||||
Before changing the status of a workflow execution's async step, you must have the execution's transaction ID.
|
||||
|
||||
When you execute the workflow, the object returned has a `transaction` property, which is an object that holds the details of the workflow execution's transaction. Use its `transactionId` to later change async steps' statuses:
|
||||
|
||||
```ts
|
||||
const { transaction } = await myWorkflow(req.scope)
|
||||
.run()
|
||||
|
||||
// use transaction.transactionId later
|
||||
```
|
||||
|
||||
### Change Step Status to Successful
|
||||
|
||||
For example, the following workflow step (used in a different workflow) resolves the workflow engine's main service and sets `step-2` of the previous workflow as successful:
|
||||
|
||||
export const successStatusHighlights = [
|
||||
["17", "transactionId", "Receive the workflow execution's transaction ID as an input to the step."],
|
||||
["20", "resolve", "Resolve the workflow engine's main service."],
|
||||
["24", "setStepSuccess", "Change the step's status to successful."],
|
||||
["28", "stepId", "The ID of the step as passed to `createStep`'s first parameter when it was created."],
|
||||
["29", "workflowId", "The ID of the workflow as passed to `createWorkflow`'s first parameter when it was created."],
|
||||
["31", "stepResponse", "The response returned by the step, since an `async` step can't return a response in its definition."]
|
||||
]
|
||||
|
||||
```ts highlights={successStatusHighlights} collapsibleLines="1-9" expandButtonLabel="Show Imports"
|
||||
import {
|
||||
ModuleRegistrationName,
|
||||
TransactionHandlerType,
|
||||
} from "@medusajs/utils";
|
||||
import {
|
||||
StepResponse,
|
||||
createStep
|
||||
} from "@medusajs/workflows-sdk";
|
||||
|
||||
type SetStepSuccessStepInput = {
|
||||
transactionId: string
|
||||
};
|
||||
|
||||
export const setStepSuccessStep = createStep(
|
||||
"set-step-success-step",
|
||||
async function (
|
||||
{ transactionId }: SetStepSuccessStepInput,
|
||||
{ container }
|
||||
) {
|
||||
const workflowEngineService = container.resolve(
|
||||
ModuleRegistrationName.WORKFLOW_ENGINE
|
||||
);
|
||||
|
||||
await workflowEngineService.setStepSuccess({
|
||||
idempotencyKey: {
|
||||
action: TransactionHandlerType.INVOKE,
|
||||
transactionId,
|
||||
stepId: "step-2",
|
||||
workflowId: "hello-world",
|
||||
},
|
||||
stepResponse: new StepResponse("Done!"),
|
||||
options: {
|
||||
container,
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
You use this step in another workflow that changes the long-running workflow's status.
|
||||
|
||||
After change the async step's status to successful, the workflow execution continues to the next step.
|
||||
|
||||
The `setStepSuccess` method of the workflow engine's main service accepts as a parameter an object having the following properties:
|
||||
|
||||
<TypeList
|
||||
types={[
|
||||
{
|
||||
name: "idempotencyKey",
|
||||
type: "`object`",
|
||||
description: "The details of the workflow execution.",
|
||||
optional: false,
|
||||
children: [
|
||||
{
|
||||
name: "action",
|
||||
type: "`invoke` | `compensate`",
|
||||
description: "If the step's compensation function is running, use `compensate`. Otherwise, use `invoke`.",
|
||||
optional: false
|
||||
},
|
||||
{
|
||||
name: "transactionId",
|
||||
type: "`string`",
|
||||
description: "The ID of the workflow execution's transaction.",
|
||||
optional: false
|
||||
},
|
||||
{
|
||||
name: "stepId",
|
||||
type: "`string`",
|
||||
description: "The ID of the step to change its status. This is the first parameter passed to `createStep` when creating the step.",
|
||||
optional: false
|
||||
},
|
||||
{
|
||||
name: "workflowId",
|
||||
type: "`string`",
|
||||
description: "The ID of the workflow. This is the first parameter passed to `createWorkflow` when creating the workflow.",
|
||||
optional: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "stepResponse",
|
||||
type: "`StepResponse`",
|
||||
description: "Set the response of the step. This is similar to the response you return in a step's definition, but since the `async` step doesn't have a response, you set its response when changing its status.",
|
||||
optional: false
|
||||
},
|
||||
{
|
||||
name: "options",
|
||||
type: "`Record<string, any>`",
|
||||
description: "Options to pass to the step.",
|
||||
optional: true,
|
||||
children: [
|
||||
{
|
||||
name: "container",
|
||||
type: "`MedusaContainer`",
|
||||
description: "An instance of the Medusa Container",
|
||||
optional: true
|
||||
}
|
||||
]
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
### Change Step Status to Failed
|
||||
|
||||
The workflow engine's main service also has a `setStepFailure` method that changes a step's status to failed. It accepts the same parameter as `setStepSuccess`.
|
||||
|
||||
For example:
|
||||
|
||||
export const failureStatusHighlights = [
|
||||
["17", "transactionId", "Receive the workflow execution's transaction ID as an input to the step."],
|
||||
["20", "resolve", "Resolve the workflow engine's main service."],
|
||||
["24", "setStepSuccess", "Change the step's status to successful."],
|
||||
["28", "stepId", "The ID of the step as passed to `createStep`'s first parameter when it was created."],
|
||||
["29", "workflowId", "The ID of the workflow as passed to `createWorkflow`'s first parameter when it was created."],
|
||||
["31", "stepResponse", "The response returned by the step, since an `async` step can't return a response in its definition."]
|
||||
]
|
||||
|
||||
```ts highlights={failureStatusHighlights} collapsibleLines="1-9" expandButtonLabel="Show Imports"
|
||||
import {
|
||||
ModuleRegistrationName,
|
||||
TransactionHandlerType,
|
||||
} from "@medusajs/utils";
|
||||
import {
|
||||
StepResponse,
|
||||
createStep
|
||||
} from "@medusajs/workflows-sdk";
|
||||
|
||||
type SetStepFailureStepInput = {
|
||||
transactionId: string
|
||||
};
|
||||
|
||||
export const setStepFailureStep = createStep(
|
||||
"set-step-success-step",
|
||||
async function (
|
||||
{ transactionId }: SetStepFailureStepInput,
|
||||
{ container }
|
||||
) {
|
||||
const workflowEngineService = container.resolve(
|
||||
ModuleRegistrationName.WORKFLOW_ENGINE
|
||||
);
|
||||
|
||||
await workflowEngineService.setStepFailure({
|
||||
idempotencyKey: {
|
||||
action: TransactionHandlerType.INVOKE,
|
||||
transactionId,
|
||||
stepId: "step-2",
|
||||
workflowId: "hello-world",
|
||||
},
|
||||
stepResponse: new StepResponse("Failed!"),
|
||||
options: {
|
||||
container,
|
||||
},
|
||||
});
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
You use this step in another workflow that changes the long-running workflow's status.
|
||||
|
||||
After change the async step's status to failed, the workflow execution fails and the compensation functions of previous steps are executed.
|
||||
|
||||
---
|
||||
|
||||
## Access Long-Running Workflow Status and Result
|
||||
|
||||
To access the status and result of a long-running workflow, use the workflow engine registered in the Medusa Container. The workflow engine provides methods to access and subscribe to workflow executions.
|
||||
To access the status and result of a long-running workflow, use the workflow engine's main service' `subscribe` and `unsubscribe` methods.
|
||||
|
||||
For example:
|
||||
|
||||
@@ -97,7 +287,7 @@ import { ModuleRegistrationName } from "@medusajs/utils"
|
||||
export async function GET(req: MedusaRequest, res: MedusaResponse) {
|
||||
const { transaction, result } = await myWorkflow(req.scope).run()
|
||||
|
||||
const workflowEngineModuleService = req.scope.resolve<
|
||||
const workflowEngineService = req.scope.resolve<
|
||||
IWorkflowEngineService
|
||||
>(
|
||||
ModuleRegistrationName.WORKFLOW_ENGINE
|
||||
@@ -109,13 +299,13 @@ export async function GET(req: MedusaRequest, res: MedusaResponse) {
|
||||
subscriberId: "hello-world-subscriber",
|
||||
}
|
||||
|
||||
await workflowEngineModuleService.subscribe({
|
||||
await workflowEngineService.subscribe({
|
||||
...subscriptionOptions,
|
||||
subscriber: async (data) => {
|
||||
if (data.eventType === "onFinish") {
|
||||
console.log("Finished execution", data.result)
|
||||
// unsubscribe
|
||||
await workflowEngineModuleService.unsubscribe({
|
||||
await workflowEngineService.unsubscribe({
|
||||
...subscriptionOptions,
|
||||
subscriberOrId: subscriptionOptions.subscriberId,
|
||||
})
|
||||
@@ -158,4 +348,4 @@ The `subscribe` method accepts an object having three properties:
|
||||
|
||||
Once the workflow execution finishes, the subscriber function is executed with the `eventType` of the received parameter set to `onFinish`. The workflow’s output is set in the `result` property of the parameter.
|
||||
|
||||
You can unsubscribe from the workflow using the workflow engine's `unsubscribe` method, which requires the same object parameter as the `subscribe` method.
|
||||
You can unsubscribe from the workflow using the workflow engine's `unsubscribe` method, which requires the same object parameter as the `subscribe` method.
|
||||
|
||||
@@ -42,13 +42,13 @@ export const generatedEditDates = {
|
||||
"app/advanced-development/workflows/conditions/page.mdx": "2024-07-31T17:01:33+03:00",
|
||||
"app/advanced-development/modules/module-link-directions/page.mdx": "2024-07-24T09:16:01+02:00",
|
||||
"app/advanced-development/admin/page.mdx": "2024-05-29T13:50:19+03:00",
|
||||
"app/advanced-development/workflows/long-running-workflow/page.mdx": "2024-07-31T17:01:33+03:00",
|
||||
"app/advanced-development/workflows/long-running-workflow/page.mdx": "2024-09-11T11:29:58.203Z",
|
||||
"app/advanced-development/workflows/constructor-constraints/page.mdx": "2024-07-17T13:19:51+01:00",
|
||||
"app/advanced-development/data-models/write-migration/page.mdx": "2024-07-15T17:46:10+02:00",
|
||||
"app/advanced-development/data-models/manage-relationships/page.mdx": "2024-09-10T11:39:51.167Z",
|
||||
"app/advanced-development/modules/remote-query/page.mdx": "2024-07-21T21:20:24+02:00",
|
||||
"app/advanced-development/modules/options/page.mdx": "2024-08-05T07:23:49+00:00",
|
||||
"app/advanced-development/data-models/relationships/page.mdx": "2024-08-15T16:30:00+03:00",
|
||||
"app/advanced-development/data-models/relationships/page.mdx": "2024-09-11T11:28:55.494Z",
|
||||
"app/advanced-development/workflows/compensation-function/page.mdx": "2024-07-31T17:01:33+03:00",
|
||||
"app/advanced-development/modules/service-factory/page.mdx": "2024-07-26T14:40:56+00:00",
|
||||
"app/advanced-development/data-models/primary-key/page.mdx": "2024-07-02T12:34:44+03:00",
|
||||
|
||||
Reference in New Issue
Block a user