Files
medusa-store/www/apps/book/app/customization/integrate-systems/schedule-task/page.mdx
arun-prasath2005 9bd99df545 Update page.mdx (#9366)
Spelling issue with perform, Fixed it!
2024-10-01 09:46:45 +00:00

322 lines
10 KiB
Plaintext

import { Prerequisites } from "docs-ui"
export const metadata = {
title: `${pageNumber} Schedule Syncing Brands from Third-Party System`,
}
# {metadata.title}
<Note title="Example Chapter">
This chapter covers how to use workflows and scheduled jobs to sync brands from the third-party system as the last step of the ["Integrate Systems" chapter](../page.mdx).
</Note>
## 1. Implement Syncing Workflow
<Prerequisites
items={[
{
text: "Brand Module",
link: "/customization/custom-features/module"
},
]}
/>
Start by defining the workflow that syncs the brand from the third-party system.
The workflow has the following steps:
1. Retrieve brands from the third-party system.
2. Create new brands in Medusa.
3. Update existing brands in Medusa.
### Retrieve Brands Step
To create the step that retrieves the brands from the third-party service, create the file `src/workflows/sync-brands-from-system/steps/retrieve-brands-from-system.ts` with the following content:
```ts title="src/workflows/sync-brands-from-system/steps/retrieve-brands-from-system.ts" collapsibleLines="1-7" expandButtonLabel="Show Imports"
import {
createStep,
StepResponse,
} from "@medusajs/framework/workflows-sdk"
import BrandModuleService from "../../../modules/brand/service"
import { BRAND_MODULE } from "../../../modules/brand"
export const retrieveBrandsFromSystemStep = createStep(
"retrieve-brands-from-system",
async (_, { container }) => {
const brandModuleService: BrandModuleService = container.resolve(
BRAND_MODULE
)
const brands = await brandModuleService.client.retrieveBrands()
return new StepResponse(brands)
}
)
```
In this step, you resolve the Brand Module's main service from the container, and use its client service to retrieve the brands from the third-party system.
The step returns the retrieved brands.
### Create Brands Step
Next, create the step that creates new brands in Medusa in the file `src/workflows/sync-brands-from-system/steps/create-brands.ts`:
export const createBrandsHighlights = [
["21", "createBrands", "Create the brands in Medusa"],
["30", "deleteBrands", "Delete the brands from Medusa"]
]
```ts title="src/workflows/sync-brands-from-system/steps/create-brands.ts" highlights={createBrandsHighlights} collapsibleLines="1-9" expandButtonLabel="Show Imports"
import {
createStep,
StepResponse,
} from "@medusajs/framework/workflows-sdk"
import { InferTypeOf } from "@medusajs/framework/types"
import BrandModuleService from "../../../modules/brand/service"
import { BRAND_MODULE } from "../../../modules/brand"
import { Brand } from "../../../modules/brand/models/brand"
type CreateBrandsInput = {
brands: InferTypeOf<typeof Brand>[]
}
export const createBrandsStep = createStep(
"create-brand-step",
async (input: CreateBrandsInput, { container }) => {
const brandModuleService: BrandModuleService = container.resolve(
BRAND_MODULE
)
const brands = await brandModuleService.createBrands(input.brands)
return new StepResponse(brands, brands.map((brand) => brand.id))
},
async (ids: string[], { container }) => {
const brandModuleService: BrandModuleService = container.resolve(
BRAND_MODULE
)
await brandModuleService.deleteBrands(ids)
}
)
```
This step receives the brands to create as input.
<Note title="Tip">
Since a data model is a variable, use the `InferTypeOf` utility imported from `@medusajs/framework/types` to infer its type.
</Note>
In the step, you resolve the Brand Module's main service and uses its `createBrands` method to create the brands.
You return the created brands and pass their IDs to the compensation function, which deletes the brands if an error occurs.
### Update Brands Step
To create the step that updates existing brands in Medusa, create the file `src/workflows/sync-brands-from-system/steps/update-brands.ts` with the following content:
export const updateBrandsHighlights = [
["21", "prevUpdatedBrands", "Retrieve the data of the brands before the update."],
["25", "updateBrands", "Update the brands in Medusa."],
["34", "updateBrands", "Revert the update by reverting the brands' to before the update."]
]
```ts title="src/workflows/sync-brands-from-system/steps/update-brands.ts" highlights={updateBrandsHighlights} collapsibleLines="1-9" expandButtonLabel="Show Imports"
import {
createStep,
StepResponse,
} from "@medusajs/framework/workflows-sdk"
import { InferTypeOf } from "@medusajs/framework/types"
import BrandModuleService from "../../../modules/brand/service"
import { BRAND_MODULE } from "../../../modules/brand"
import { Brand } from "../../../modules/brand/models/brand"
type UpdateBrandsInput = {
brands: InferTypeOf<typeof Brand>[]
}
export const updateBrandsStep = createStep(
"update-brand-step",
async ({ brands }: UpdateBrandsInput, { container }) => {
const brandModuleService: BrandModuleService = container.resolve(
BRAND_MODULE
)
const prevUpdatedBrands = await brandModuleService.listBrands({
id: brands.map((brand) => brand.id),
})
const updatedBrands = await brandModuleService.updateBrands(brands)
return new StepResponse(updatedBrands, prevUpdatedBrands)
},
async (prevUpdatedBrands, { container }) => {
const brandModuleService: BrandModuleService = container.resolve(
BRAND_MODULE
)
await brandModuleService.updateBrands(prevUpdatedBrands)
}
)
```
This step receives the brands to update as input.
In the step, you retrieve the brands first to pass them later to the compensation function, then update and return the brands.
In the compensation function, you update the brands are again but to their data before the update made by the step.
### Create Workflow
Finally, create the workflow in the file `src/workflows/sync-brands-from-system/index.ts` with the following content:
```ts title="src/workflows/sync-brands-from-system/index.ts"
import {
createWorkflow,
WorkflowResponse,
transform,
} from "@medusajs/framework/workflows-sdk"
import { InferTypeOf } from "@medusajs/framework/types"
import { retrieveBrandsFromSystemStep } from "./steps/retrieve-brands-from-system"
import { createBrandsStep } from "./steps/create-brands"
import { updateBrandsStep } from "./steps/update-brands"
import { Brand } from "../../modules/brand/models/brand"
export const syncBrandsFromSystemWorkflow = createWorkflow(
"sync-brands-from-system",
() => {
const brands = retrieveBrandsFromSystemStep()
// TODO create and update brands
}
)
```
For now, you only add the `retrieveBrandsFromSystemStep` to the workflow that retrieves the brands from the third-party system.
### Identify Brands to Create or Update in Workflow
Next, you need to identify which brands must be created or updated.
Since workflows are constructed internally and are only evaluated during execution, you can't access any data's value to perform data manipulation or checks.
Instead, use the `transform` utility function imported from `@medusajs/framework/workflows-sdk`, which gives you access to the real-time values of the data to perform actions on them.
So, replace the `TODO` with the following:
```ts title="src/workflows/sync-brands-from-system/index.ts"
const { toCreate, toUpdate } = transform(
{
brands,
},
(data) => {
const toCreate: InferTypeOf<typeof Brand>[] = []
const toUpdate: InferTypeOf<typeof Brand>[] = []
data.brands.forEach((brand) => {
if (brand.external_id) {
toUpdate.push({
...brand,
id: brand.external_id,
})
} else {
toCreate.push(brand)
}
})
return { toCreate, toUpdate }
}
)
// TODO create and update the brands
```
`transform` accepts two parameters:
1. The data to be passed to the function in the second parameter.
2. A function to execute only when the workflow is executed. Its return value can be consumed by the rest of the workflow.
In the function, you sort the brands as to be created or to be updated based on whether they have an `external_id` property.
<Note title="Tip">
This approach assumes that the third-party system stores the ID of the brand in Medusa in `external_id`.
</Note>
### Create and Update the Brands
Finally, replace the new `TODO` with the following:
```ts title="src/workflows/sync-brands-from-system/index.ts"
const created = createBrandsStep({ brands: toCreate })
const updated = updateBrandsStep({ brands: toUpdate })
return new WorkflowResponse({
created,
updated,
})
```
You pass the brands to be created to the `createBrandsStep`, and the brands to be updated to the `updateBrandsStep`.
Then, you return the created and updated brands.
---
## 2. Schedule Syncing Task
To schedule a task that syncs brands from the third-party system, create a scheduled job at `src/jobs/sync-brands-from-system.ts`:
```ts title="src/jobs/sync-brands-from-system.ts"
import { MedusaContainer } from "@medusajs/framework/types"
import { syncBrandsFromSystemWorkflow } from "../workflows/sync-brands-from-system"
export default async function (container: MedusaContainer) {
const logger = container.resolve("logger")
const { result } = await syncBrandsFromSystemWorkflow(container).run()
logger.info(
`Synced brands from third-party system: ${
result.created.length
} brands created and ${result.updated.length} brands updated.`)
}
export const config = {
name: "sync-brands-from-system",
schedule: "* * * * *",
}
```
This defines a scheduled job that runs every minute (for testing purposes).
<Note>
Learn more about scheduled jobs [in this guide](../../../basics/scheduled-jobs/page.mdx).
</Note>
The scheduled job executes the `syncBrandsFromSystemWorkflow` and prints how many brands were created and updated.
---
## Test it Out
To test it out, start the Medusa application. In a minute, the scheduled job will run and you'll see a logged message indicating how many brands were created or updated.
---
## Summary
In the previous chapters, you:
- Created a service that acts as a client integrating a third-party system.
- Implemented two-way sync of brands between the third-party system and Medusa using a subscriber and a scheduled job.