docs: fixes to long-running workflows + relationship docs (#9101)
This commit is contained in:
@@ -108,7 +108,7 @@ For example:
|
|||||||
|
|
||||||
export const manyToManyHighlights = [
|
export const manyToManyHighlights = [
|
||||||
["5", "manyToMany", "An order is associated with many products."],
|
["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}
|
```ts highlights={manyToManyHighlights}
|
||||||
@@ -116,15 +116,21 @@ import { model } from "@medusajs/utils"
|
|||||||
|
|
||||||
const Order = model.define("order", {
|
const Order = model.define("order", {
|
||||||
id: model.id().primaryKey(),
|
id: model.id().primaryKey(),
|
||||||
products: model.manyToMany(() => Product),
|
products: model.manyToMany(() => Product, {
|
||||||
|
mappedBy: "orders"
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
const Product = model.define("product", {
|
const Product = model.define("product", {
|
||||||
id: model.id().primaryKey(),
|
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.
|
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
|
## 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:
|
For example, consider the following workflow and steps:
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ const step2 = createStep(
|
|||||||
async: true,
|
async: true,
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
return new StepResponse({})
|
console.log("Waiting to be successful...")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -63,21 +63,211 @@ const myWorkflow = createWorkflow(
|
|||||||
export default myWorkflow
|
export default myWorkflow
|
||||||
```
|
```
|
||||||
|
|
||||||
The second step has in its configuration object `async` set to true. This indicates that this step is an asynchronous step.
|
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.
|
||||||
|
|
||||||
<Note title="Important">
|
|
||||||
|
|
||||||
An asynchronous step must return for the execution to continue.
|
|
||||||
|
|
||||||
</Note>
|
|
||||||
|
|
||||||
So, when you execute the `hello-world` workflow, it continues its execution in the background once it reaches the second 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
|
## 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:
|
For example:
|
||||||
|
|
||||||
@@ -97,7 +287,7 @@ import { ModuleRegistrationName } from "@medusajs/utils"
|
|||||||
export async function GET(req: MedusaRequest, res: MedusaResponse) {
|
export async function GET(req: MedusaRequest, res: MedusaResponse) {
|
||||||
const { transaction, result } = await myWorkflow(req.scope).run()
|
const { transaction, result } = await myWorkflow(req.scope).run()
|
||||||
|
|
||||||
const workflowEngineModuleService = req.scope.resolve<
|
const workflowEngineService = req.scope.resolve<
|
||||||
IWorkflowEngineService
|
IWorkflowEngineService
|
||||||
>(
|
>(
|
||||||
ModuleRegistrationName.WORKFLOW_ENGINE
|
ModuleRegistrationName.WORKFLOW_ENGINE
|
||||||
@@ -109,13 +299,13 @@ export async function GET(req: MedusaRequest, res: MedusaResponse) {
|
|||||||
subscriberId: "hello-world-subscriber",
|
subscriberId: "hello-world-subscriber",
|
||||||
}
|
}
|
||||||
|
|
||||||
await workflowEngineModuleService.subscribe({
|
await workflowEngineService.subscribe({
|
||||||
...subscriptionOptions,
|
...subscriptionOptions,
|
||||||
subscriber: async (data) => {
|
subscriber: async (data) => {
|
||||||
if (data.eventType === "onFinish") {
|
if (data.eventType === "onFinish") {
|
||||||
console.log("Finished execution", data.result)
|
console.log("Finished execution", data.result)
|
||||||
// unsubscribe
|
// unsubscribe
|
||||||
await workflowEngineModuleService.unsubscribe({
|
await workflowEngineService.unsubscribe({
|
||||||
...subscriptionOptions,
|
...subscriptionOptions,
|
||||||
subscriberOrId: subscriptionOptions.subscriberId,
|
subscriberOrId: subscriptionOptions.subscriberId,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -42,13 +42,13 @@ export const generatedEditDates = {
|
|||||||
"app/advanced-development/workflows/conditions/page.mdx": "2024-07-31T17:01:33+03:00",
|
"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/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/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/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/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/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/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/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/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/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",
|
"app/advanced-development/data-models/primary-key/page.mdx": "2024-07-02T12:34:44+03:00",
|
||||||
|
|||||||
Reference in New Issue
Block a user