diff --git a/www/apps/book/app/advanced-development/data-models/relationships/page.mdx b/www/apps/book/app/advanced-development/data-models/relationships/page.mdx
index 688ea9b9dd..6160d3dc03 100644
--- a/www/apps/book/app/advanced-development/data-models/relationships/page.mdx
+++ b/www/apps/book/app/advanced-development/data-models/relationships/page.mdx
@@ -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.
---
diff --git a/www/apps/book/app/advanced-development/workflows/long-running-workflow/page.mdx b/www/apps/book/app/advanced-development/workflows/long-running-workflow/page.mdx
index 34bef1eb33..7bf86cc248 100644
--- a/www/apps/book/app/advanced-development/workflows/long-running-workflow/page.mdx
+++ b/www/apps/book/app/advanced-development/workflows/long-running-workflow/page.mdx
@@ -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.
-
-
-
-An asynchronous step must return for the execution to continue.
-
-
+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:
+
+`",
+ 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.
\ No newline at end of file
+You can unsubscribe from the workflow using the workflow engine's `unsubscribe` method, which requires the same object parameter as the `subscribe` method.
diff --git a/www/apps/book/generated/edit-dates.mjs b/www/apps/book/generated/edit-dates.mjs
index 1c2bf0ae77..0e14c13afc 100644
--- a/www/apps/book/generated/edit-dates.mjs
+++ b/www/apps/book/generated/edit-dates.mjs
@@ -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",