|
|
|
@@ -1,35 +1,23 @@
|
|
|
|
|
import { Prerequisites } from "docs-ui"
|
|
|
|
|
|
|
|
|
|
export const metadata = {
|
|
|
|
|
title: `${pageNumber} Define Workflow to Create a Brand`,
|
|
|
|
|
title: `${pageNumber} Guide: Create Brand Workflow`,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# {metadata.title}
|
|
|
|
|
|
|
|
|
|
<Note title="Example Chapter">
|
|
|
|
|
This chapter is a follow-up to the [previous one](../module/page.mdx) where you created a Brand Module. In this chapter, you'll create a workflow that creates a brand.
|
|
|
|
|
|
|
|
|
|
This chapter covers how to define a workflow that creates a brand as part of the ["Build Custom Features" chapter](../page.mdx).
|
|
|
|
|
You implement commerce features within workflows. A workflow is a series of queries and actions, called steps, that complete a task. You construct a workflow similar to a regular function, but it's a special function that allows you to define roll-back logic, retry configurations, and more advanced features.
|
|
|
|
|
|
|
|
|
|
</Note>
|
|
|
|
|
|
|
|
|
|
## Workflows vs Services: Why use Workflows?
|
|
|
|
|
|
|
|
|
|
When manipulating data, use workflows instead of invoking a service's methods directly in your API route or other customizations.
|
|
|
|
|
|
|
|
|
|
Workflows eliminate data inconsistency in your application with its compensation mechanism that undoes changes if an error occurs. For example, if a workflow's step creates a brand, it also defines a compensation mechanism to remove the brand if an error occurs.
|
|
|
|
|
The workflow you'll create in this chapter will use the Brand Module's service to implement the feature of creating a brand. In the [next chapter](../api-route/page.mdx), you'll expose an API route that allows admin users to create a brand, and you'll use this workflow in the route's implementation.
|
|
|
|
|
|
|
|
|
|
<Note>
|
|
|
|
|
|
|
|
|
|
Learn more about workflows [in this guide](../../../basics/workflows/page.mdx).
|
|
|
|
|
Learn more about workflows in [this chapter](../../../basics/workflows/page.mdx).
|
|
|
|
|
|
|
|
|
|
</Note>
|
|
|
|
|
|
|
|
|
|
This is even more useful when you create workflows with many steps, or integrate third-party systems.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Create createBrandWorkflow
|
|
|
|
|
|
|
|
|
|
<Prerequisites
|
|
|
|
|
items={[
|
|
|
|
|
{
|
|
|
|
@@ -39,46 +27,29 @@ This is even more useful when you create workflows with many steps, or integrate
|
|
|
|
|
]}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
Create the file `src/workflows/create-brand/index.ts` with the following content:
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
import {
|
|
|
|
|
createWorkflow,
|
|
|
|
|
WorkflowResponse,
|
|
|
|
|
} from "@medusajs/framework/workflows-sdk"
|
|
|
|
|
|
|
|
|
|
export type CreateBrandInput = {
|
|
|
|
|
name: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const createBrandWorkflow = createWorkflow(
|
|
|
|
|
"create-brand",
|
|
|
|
|
(input: CreateBrandInput) => {
|
|
|
|
|
// TODO
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
For now, this workflow only defines its input. You'll create its step and use it in the workflow.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Create createBrandStep
|
|
|
|
|
## 1. Create createBrandStep
|
|
|
|
|
|
|
|
|
|
Create the file `src/workflows/create-brand/steps/create-brand.ts` with the following content:
|
|
|
|
|
A workflow consists of a series of steps, each step created in a TypeScript or JavaScript file under the `src/workflows` directory. A step is defined using the `createStep` utility function imported from `@medusajs/framework/workflows-sdk`.
|
|
|
|
|
|
|
|
|
|
```ts title="src/workflows/create-brand/steps/create-brand.ts" collapsibleLines="1-8" expandButtonLabel="Show Imports"
|
|
|
|
|
The workflow you're creating in this guide has one step to create the brand. So, create the file `src/workflows/create-brand/steps/create-brand.ts` with the following content:
|
|
|
|
|
|
|
|
|
|
```ts title="src/workflows/create-brand/steps/create-brand.ts"
|
|
|
|
|
import {
|
|
|
|
|
createStep,
|
|
|
|
|
StepResponse,
|
|
|
|
|
} from "@medusajs/framework/workflows-sdk"
|
|
|
|
|
import { CreateBrandInput } from ".."
|
|
|
|
|
import { BRAND_MODULE } from "../../../modules/brand"
|
|
|
|
|
import BrandModuleService from "../../../modules/brand/service"
|
|
|
|
|
|
|
|
|
|
export type CreateBrandStepInput = {
|
|
|
|
|
name: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const createBrandStep = createStep(
|
|
|
|
|
"create-brand-step",
|
|
|
|
|
async (input: CreateBrandInput, { container }) => {
|
|
|
|
|
async (input: CreateBrandStepInput, { container }) => {
|
|
|
|
|
const brandModuleService: BrandModuleService = container.resolve(
|
|
|
|
|
BRAND_MODULE
|
|
|
|
|
)
|
|
|
|
@@ -90,17 +61,33 @@ export const createBrandStep = createStep(
|
|
|
|
|
)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
This defines a `createBrandStep`. In the step, you resolve the Brand Module's main service and use its generated `createBrands` method, which accepts one or more objects of brands to create.
|
|
|
|
|
You create a `createBrandStep` using the `createStep` function. It accepts the step's unique name as a first parameter, and the step's function as a second parameter.
|
|
|
|
|
|
|
|
|
|
The step returns the created brand in the first parameter of the `StepResponse`'s constructor.
|
|
|
|
|
The step function receives two parameters: input passed to the step when it's invoked, and an object of general context and configurations. This object has a `container` property, which is the Medusa container.
|
|
|
|
|
|
|
|
|
|
The [Medusa container](../../../basics/medusa-container/page.mdx) is a registry of framework and commerce tools accessible in your customizations, such as a workflow's step. The Medusa application registers the services of core and custom modules in the container, allowing you to resolve and use them.
|
|
|
|
|
|
|
|
|
|
So, In the step function, you use the Medusa container to resolve the Brand Module's service and use its generated `createBrands` method, which accepts an object of brands to create.
|
|
|
|
|
|
|
|
|
|
<Note>
|
|
|
|
|
|
|
|
|
|
Learn more about the generated `create` method's usage in [this reference](!resources!/service-factory-reference/methods/create).
|
|
|
|
|
|
|
|
|
|
</Note>
|
|
|
|
|
|
|
|
|
|
A step must return an instance of `StepResponse`. Its first parameter is the data returned by the step, and the second is the data passed to the compensation function, which you'll learn about next.
|
|
|
|
|
|
|
|
|
|
### Add Compensation Function to Step
|
|
|
|
|
|
|
|
|
|
A compensation function rolls back changes made by the step if an error occurs in the workflow.
|
|
|
|
|
You define for each step a compensation function that's executed when an error occurs in the workflow. The compensation function defines the logic to roll-back the changes made by the step. This ensures your data remains consistent if an error occurs, which is especially useful when you integrate third-party services.
|
|
|
|
|
|
|
|
|
|
The second parameter of the `StepResponse`'s constructor is passed to the compensation function.
|
|
|
|
|
<Note>
|
|
|
|
|
|
|
|
|
|
To add the compensation function, pass a third parameter to `createStep`:
|
|
|
|
|
Learn more about the compensation function in [this chapter](../../../advanced-development/workflows/compensation-function/page.mdx).
|
|
|
|
|
|
|
|
|
|
</Note>
|
|
|
|
|
|
|
|
|
|
To add a compensation function to the `createBrandStep`, pass it as a third parameter to `createStep`:
|
|
|
|
|
|
|
|
|
|
```ts title="src/workflows/create-brand/steps/create-brand.ts"
|
|
|
|
|
export const createBrandStep = createStep(
|
|
|
|
@@ -115,38 +102,57 @@ export const createBrandStep = createStep(
|
|
|
|
|
)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You resolve the Brand Module's main service and use its generated `deleteBrands` method to delete the brand created by the step.
|
|
|
|
|
The compensation function's first parameter is the brand's ID which you passed as a second parameter to the step function's returned `StepResponse`. It also accepts a context object with a `container` property as a second parameter, similar to the step function.
|
|
|
|
|
|
|
|
|
|
<Note title="Tip">
|
|
|
|
|
In the compensation function, you resolve the Brand Module's service from the Medusa container, then use its generated `deleteBrands` method to delete the brand created by the step. This method accepts the ID of the brand to delete.
|
|
|
|
|
|
|
|
|
|
The `deleteBrands` method accepts an ID or an array of IDs of brands to delete.
|
|
|
|
|
<Note>
|
|
|
|
|
|
|
|
|
|
Learn more about the generated `delete` method's usage in [this reference](!resources!/service-factory-reference/methods/delete).
|
|
|
|
|
|
|
|
|
|
</Note>
|
|
|
|
|
|
|
|
|
|
So, when an error occurs during the workflow, the brand that was created by the step is deleted to maintain data consistency.
|
|
|
|
|
So, if an error occurs during the workflow's execution, the brand that was created by the step is deleted to maintain data consistency.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Add Step to Workflow
|
|
|
|
|
## 2. Create createBrandWorkflow
|
|
|
|
|
|
|
|
|
|
Go back to the workflow at `src/workflows/create-brand/index.ts` and import the step you created:
|
|
|
|
|
You can now create the workflow that runs the `createBrandStep`. A workflow is created in a TypeScript or JavaScript file under the `src/workflows` directory. In the file, you use the `createWorkflow` function imported from `@medusajs/framework/workflows-sdk` to create the workflow.
|
|
|
|
|
|
|
|
|
|
So, create the file `src/workflows/create-brand/index.ts` with the following content:
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
import {
|
|
|
|
|
createWorkflow,
|
|
|
|
|
WorkflowResponse,
|
|
|
|
|
} from "@medusajs/framework/workflows-sdk"
|
|
|
|
|
import { createBrandStep } from "./steps/create-brand"
|
|
|
|
|
|
|
|
|
|
type CreateBrandInput = {
|
|
|
|
|
name: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const createBrandWorkflow = createWorkflow(
|
|
|
|
|
"create-brand",
|
|
|
|
|
(input: CreateBrandInput) => {
|
|
|
|
|
const brand = createBrandStep(input)
|
|
|
|
|
|
|
|
|
|
return new WorkflowResponse(brand)
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Then, replace the `TODO` with the following:
|
|
|
|
|
You create the `createBrandWorkflow` using the `createWorkflow` function. This function accepts two parameters: the workflow's unique name, and the workflow's constructor function holding the workflow's implementation.
|
|
|
|
|
|
|
|
|
|
```ts
|
|
|
|
|
const brand = createBrandStep(input)
|
|
|
|
|
The constructor function accepts the workflow's input as a parameter. In the function, you invoke the `createBrandStep` you created in the previous step to create a brand.
|
|
|
|
|
|
|
|
|
|
return new WorkflowResponse(brand)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
You use the `createBrandStep` to create the brand and return it in the workflow's response.
|
|
|
|
|
A workflow must return an instance of `WorkflowResponse`. It accepts as a parameter the data to return to the workflow's executor.
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
## Next Step: Create Brand API Route
|
|
|
|
|
## Next Steps: Expose Create Brand API Route
|
|
|
|
|
|
|
|
|
|
In the next step, you'll create an API route that allows admin users to create a brand using this workflow.
|
|
|
|
|
You now have a `createBrandWorkflow` that you can execute to create a brand.
|
|
|
|
|
|
|
|
|
|
In the next chapter, you'll add an API route that allows admin users to create a brand. You'll learn how to create the API route, and execute in it the workflow you implemented in this chapter.
|
|
|
|
|