docs: customization chapter exploration (#9078)
Adds a new customizations chapter with realistic example while maintaining the linear learning journey. Preview: https://docs-v2-git-docs-customizations-chapter-medusajs.vercel.app/v2/customization
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
import { Prerequisites } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Brand Example: Handle Event to Sync Third-Party System`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
<Note title="Example Chapter">
|
||||
|
||||
This chapter covers how to emit an event when a brand is created, listen to that event in a subscriber, and create the brand in the third-party system as a step of the ["Integrate Systems" chapter](../page.mdx).
|
||||
|
||||
</Note>
|
||||
|
||||
## 1. Emit Custom Event for Brand Creation
|
||||
|
||||
<Prerequisites
|
||||
items={[
|
||||
{
|
||||
text: "Brand Module with createBrandWorkflow",
|
||||
link: "/customization/custom-features/workflow"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
To handle brand-creation event, you'll emit a custom event when a brand is created.
|
||||
|
||||
In the `createBrandWorkflow` defined in `src/workflows/create-brand/index.ts`, use the `emitEventStep` helper step imported from `@medusajs/core-flows` after the `createBrandStep`:
|
||||
|
||||
export const eventHighlights = [
|
||||
["13", "emitEventStep", "Emit an event."],
|
||||
["14", "eventName", "The event's name."],
|
||||
["15", "data", "The data to pass in the payload."]
|
||||
]
|
||||
|
||||
```ts title="src/workflows/create-brand/index.ts" highlights={eventHighlights}
|
||||
// other imports...
|
||||
import {
|
||||
emitEventStep,
|
||||
} from "@medusajs/core-flows"
|
||||
|
||||
// ...
|
||||
|
||||
export const createBrandWorkflow = createWorkflow(
|
||||
"create-brand",
|
||||
(input: CreateBrandInput) => {
|
||||
// ...
|
||||
|
||||
emitEventStep({
|
||||
eventName: "brand.created",
|
||||
data: {
|
||||
id: brand.id,
|
||||
},
|
||||
})
|
||||
|
||||
return new WorkflowResponse(brand)
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
The `emitEventStep` accepts as a parameter an object having two properties:
|
||||
|
||||
- `eventName`: The name of the event to emit.
|
||||
- `data`: The data payload to emit with the event. This is useful for subscribers to access the created brand.
|
||||
|
||||
---
|
||||
|
||||
## 2. Create Sync to Third-Party System Workflow
|
||||
|
||||
Next, you'll create the workflow that syncs the created brand to the third-party system.
|
||||
|
||||
Create the file `src/workflows/sync-brand-to-system/index.ts` with the following content:
|
||||
|
||||
```ts title="src/workflows/sync-brand-to-system/index.ts"
|
||||
import {
|
||||
createWorkflow,
|
||||
WorkflowResponse,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
|
||||
export type SyncBrandToSystemInput = {
|
||||
id: string
|
||||
}
|
||||
|
||||
export const syncBrandToSystemWorkflow = createWorkflow(
|
||||
"sync-brand-to-system",
|
||||
(input: SyncBrandToSystemInput) => {
|
||||
// ...
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
This defines an empty workflow and its expected input.
|
||||
|
||||
### Create createBrandInSystemStep
|
||||
|
||||
Next, create the step that syncs the brand in the file `src/workflows/sync-brand-to-system/steps/create-brand-in-system.ts`:
|
||||
|
||||
export const stepHighlights = [
|
||||
["18", "createBrand", "Create a brand in the third-party system."],
|
||||
["27", "deleteBrand", "Delete the brand in the third-party system if an error occurs."]
|
||||
]
|
||||
|
||||
```ts title="src/workflows/sync-brand-to-system/steps/create-brand-in-system.ts" highlights={stepHighlights} collapsibleLines="1-8" expandButtonLabel="Show Imports"
|
||||
import {
|
||||
createStep,
|
||||
StepResponse,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import { SyncBrandToSystemInput } from ".."
|
||||
import BrandModuleService from "../../../modules/brand/service"
|
||||
import { BRAND_MODULE } from "../../../modules/brand"
|
||||
|
||||
export const createBrandInSystemStep = createStep(
|
||||
"create-brand-in-system",
|
||||
async ({ id }: SyncBrandToSystemInput, { container }) => {
|
||||
const brandModuleService: BrandModuleService = container.resolve(
|
||||
BRAND_MODULE
|
||||
)
|
||||
|
||||
const brand = await brandModuleService.retrieveBrand(id)
|
||||
|
||||
await brandModuleService.client.createBrand(brand)
|
||||
|
||||
return new StepResponse(null, brand.id)
|
||||
},
|
||||
async (id, { container }) => {
|
||||
const brandModuleService: BrandModuleService = container.resolve(
|
||||
BRAND_MODULE
|
||||
)
|
||||
|
||||
await brandModuleService.client.deleteBrand(id)
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
This step resolves the Brand Module's main service and uses its `client` property to access its internal service that integrates the third-party system.
|
||||
|
||||
In the step, you use the `createBrand` method of the client to create the brand in the third-party system.
|
||||
|
||||
In the compensation function, you undo the step's action using the `deleteBrand` method of the client.
|
||||
|
||||
### Add Step to Workflow
|
||||
|
||||
Finally, add the step to the `syncBrandToSystemWorkflow` in `src/workflows/sync-brand-to-system/index.ts`:
|
||||
|
||||
```ts title="src/workflows/sync-brand-to-system/index.ts"
|
||||
// other imports...
|
||||
import { createBrandInSystemStep } from "./steps/create-brand-in-system"
|
||||
|
||||
// ...
|
||||
|
||||
export const syncBrandToSystemWorkflow = createWorkflow(
|
||||
"sync-brand-to-system",
|
||||
(input: SyncBrandToSystemInput) => {
|
||||
createBrandInSystemStep(input)
|
||||
|
||||
return new WorkflowResponse(undefined)
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
The workflow now calls the step and returns an `undefined` result.
|
||||
|
||||
---
|
||||
|
||||
## 3. Handle brand.created Event
|
||||
|
||||
To handle the `brand.created` event, create a subscriber at `src/subscribers/brand-created.ts` with the following content:
|
||||
|
||||
```ts title="src/subscribers/brand-created.ts"
|
||||
import type {
|
||||
SubscriberConfig,
|
||||
SubscriberArgs,
|
||||
} from "@medusajs/medusa"
|
||||
import { syncBrandToSystemWorkflow } from "../workflows/sync-brand-to-system"
|
||||
|
||||
export default async function brandCreatedHandler({
|
||||
event: { data },
|
||||
container,
|
||||
}: SubscriberArgs<Record<string, string>>) {
|
||||
await syncBrandToSystemWorkflow(container).run({
|
||||
input: data,
|
||||
})
|
||||
}
|
||||
|
||||
export const config: SubscriberConfig = {
|
||||
event: "brand.created",
|
||||
}
|
||||
```
|
||||
|
||||
The subscriber handler accesses the event payload in the `event.data` property of its object parameter.
|
||||
|
||||
<Note>
|
||||
|
||||
Learn more about subscribers [in this guide](../../../basics/events-and-subscribers/page.mdx).
|
||||
|
||||
</Note>
|
||||
|
||||
It then executes the `syncBrandToSystemWorkflow`, passing it the ID of the brand to create in the third-party system.
|
||||
|
||||
---
|
||||
|
||||
## Test it Out
|
||||
|
||||
To test it out, start the Medusa application and create a brand using the API route created in a [previous chapter](../../custom-features/api-route/page.mdx#test-api-route).
|
||||
|
||||
If you check the logs, you'll find the `brand.created` event was emitted, and that the request to the third-party system was simulated.
|
||||
|
||||
---
|
||||
|
||||
## Next Chapter: Sync Brand from Third-Party System to Medusa
|
||||
|
||||
In the next chapter, you'll learn how to sync brands in the third-party system into Medusa using a workflow and a scheduled job.
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Integrate Third-Party Systems`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this chapter, you'll learn how to integrate a third-party system into Medusa.
|
||||
|
||||
## How to Integrate a Third-Party System?
|
||||
|
||||
To integrate a third-party system into Medusa, you:
|
||||
|
||||
1. Implement the methods to interact with the system in a service. It can either be the main module's service, or an internal service in the module that's used by the main one.
|
||||
2. Implement in workflows custom features around the integration, such as sending data to the third-party system.
|
||||
- Workflows roll-back mechanism ensures data consistency. This is essential as you integrate multiple systems into your application.
|
||||
3. Use the workflow in other resources to expose or utilize the custom functionality.
|
||||
|
||||
---
|
||||
|
||||
## Next Chapters: Syncing Brands Example
|
||||
|
||||
In the next chapters, you'll implement an example of syncing brands with a third-party system, such as a Content Management System (CMS).
|
||||
|
||||
That requires:
|
||||
|
||||
1. Implementing the service that integrates the third-party system.
|
||||
2. Creating a brand in the third-party system when a brand is created in Medusa.
|
||||
2. Retrieving the brands from the third-party system to sync them with Medusa's brands at a scheduled interval.
|
||||
@@ -0,0 +1,309 @@
|
||||
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/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 = [
|
||||
["19", "createBrands", "Create the brands in Medusa"],
|
||||
["28", "deleteBrands", "Delete the brands from Medusa"]
|
||||
]
|
||||
|
||||
```ts title="src/workflows/sync-brands-from-system/steps/create-brands.ts" highlights={createBrandsHighlights} collapsibleLines="1-7" expandButtonLabel="Show Imports"
|
||||
import {
|
||||
createStep,
|
||||
StepResponse,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import BrandModuleService from "../../../modules/brand/service"
|
||||
import { BRAND_MODULE } from "../../../modules/brand"
|
||||
|
||||
type CreateBrandsInput = {
|
||||
brands: Record<string, string>[]
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
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 = [
|
||||
["19", "prevUpdatedBrands", "Retrieve the data of the brands before the update."],
|
||||
["23", "updateBrands", "Update the brands in Medusa."],
|
||||
["32", "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-7" expandButtonLabel="Show Imports"
|
||||
import {
|
||||
createStep,
|
||||
StepResponse,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import BrandModuleService from "../../../modules/brand/service"
|
||||
import { BRAND_MODULE } from "../../../modules/brand"
|
||||
|
||||
type UpdateBrandsInput = {
|
||||
brands: Record<string, string>[]
|
||||
}
|
||||
|
||||
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/workflows-sdk"
|
||||
import { retrieveBrandsFromSystemStep } from "./steps/retrieve-brands-from-system"
|
||||
import { createBrandsStep } from "./steps/create-brands"
|
||||
import { updateBrandsStep } from "./steps/update-brands"
|
||||
|
||||
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/workflows-sdk`, which gives you access to the real-time values of the data to perfrom 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: Record<string, string>[] = []
|
||||
const toUpdate: Record<string, string>[] = []
|
||||
|
||||
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/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.
|
||||
@@ -0,0 +1,201 @@
|
||||
import { Prerequisites } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `${pageNumber} Integrate Third-Party Brand System in a Service`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
<Note title="Example Chapter">
|
||||
|
||||
This chapter covers how to integrate a dummy third-party system in a service as a step of the ["Integrate Systems" chapter](../page.mdx).
|
||||
|
||||
</Note>
|
||||
|
||||
## 1. Create Service
|
||||
|
||||
<Prerequisites
|
||||
items={[
|
||||
{
|
||||
text: "Brand Module",
|
||||
link: "/customization/custom-features/module"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
Start by creating the file `src/modules/brand/services/client.ts` with the following content:
|
||||
|
||||
export const serviceHighlights = [
|
||||
["4", "BrandClientOptions", "Define the options that the Brand Module receives necessary for the integration."],
|
||||
["8", "InjectedDependencies", "Define the dependencies injected into the service."],
|
||||
["20", "moduleDef", "Retrieve the module's configuration."]
|
||||
]
|
||||
|
||||
```ts title="src/modules/brand/services/client.ts" highlights={serviceHighlights}
|
||||
import { Logger, ConfigModule } from "@medusajs/types"
|
||||
import { BRAND_MODULE } from ".."
|
||||
|
||||
export type BrandClientOptions = {
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
type InjectedDependencies = {
|
||||
logger: Logger
|
||||
configModule: ConfigModule
|
||||
}
|
||||
|
||||
export class BrandClient {
|
||||
private options_: BrandClientOptions
|
||||
private logger_: Logger
|
||||
|
||||
constructor({ logger, configModule }: InjectedDependencies) {
|
||||
this.logger_ = logger
|
||||
|
||||
const moduleDef = configModule.modules[BRAND_MODULE]
|
||||
if (typeof moduleDef !== "boolean") {
|
||||
this.options_ = moduleDef.options
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This creates a `BrandClient` service. Using dependency injection, you resolve the `logger` and `configModule` from the Module's container.
|
||||
|
||||
`logger` is useful to log messages, and `configModule` has configurations exported in `medusa-config.js`.
|
||||
|
||||
You also define an `options_` property in your service to store the module's options.
|
||||
|
||||
The `configModule`'s `modules` property is an object whose keys are registered module names and values are the module's configuration.
|
||||
|
||||
If the module's configuration isn't a boolean, it has an `options` property that holds the module's options. You use it to set the `options_` property's value.
|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
If the service integrating the third-party system was a main service, it receives the module's options as a second parameter.
|
||||
|
||||
</Note>
|
||||
|
||||
### Integration Methods
|
||||
|
||||
Next, add the following methods to simulate sending requests to the third-party system:
|
||||
|
||||
export const methodsHighlights = [
|
||||
["6", "sendRequest", "Since the third-party system isn't real, this method only logs a message."],
|
||||
["15", "createBrand", "A method that creates a brand in the third-party system."],
|
||||
["19", "deleteBrand", "A method that deletes a brand in the third-party system."],
|
||||
["23", "retrieveBrands", "A method that retrieves a brand from a third-party system."]
|
||||
]
|
||||
|
||||
```ts title="src/modules/brand/services/client.ts" highlights={methodsHighlights}
|
||||
export class BrandClient {
|
||||
// ...
|
||||
|
||||
// a dummy method to simulate sending a request,
|
||||
// in a realistic scenario, you'd use an SDK, fetch, or axios clients
|
||||
private async sendRequest(url: string, method: string, data?: any) {
|
||||
this.logger_.info(`Sending a ${
|
||||
method
|
||||
} request to ${url}. data: ${JSON.stringify(data, null, 2)}`)
|
||||
this.logger_.info(`Client Options: ${
|
||||
JSON.stringify(this.options_, null, 2)
|
||||
}`)
|
||||
}
|
||||
|
||||
async createBrand(brand: Record<string, string>) {
|
||||
await this.sendRequest("/brands", "POST", brand)
|
||||
}
|
||||
|
||||
async deleteBrand(id: string) {
|
||||
await this.sendRequest(`/brands/${id}`, "DELETE")
|
||||
}
|
||||
|
||||
async retrieveBrands() {
|
||||
await this.sendRequest("/brands", "GET")
|
||||
|
||||
return []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `sendRequest` method is a dummy method to simulate sending a request to a third-party system.
|
||||
|
||||
You also add three methods that use the `sendRequest` method:
|
||||
|
||||
- `createBrand` that creates a brand in the third-party system.
|
||||
- `deleteBrand` that deletes the brand in the third-party system.
|
||||
- `retrieveBrands` to retrieve a brand from the third-party system.
|
||||
|
||||
---
|
||||
|
||||
## 2. Export Service
|
||||
|
||||
If the service integrating the third-party system is the module's main service, you only need to export it in the module definition.
|
||||
|
||||
However, since this service is an internal service in the Brand Module, you must export it in a `src/modules/brand/services/index.ts` file:
|
||||
|
||||
```ts title="src/modules/brand/services/index.ts"
|
||||
export * from "./client"
|
||||
```
|
||||
|
||||
This registers the service in the module's container, allowing you to access it in the module's main service.
|
||||
|
||||
---
|
||||
|
||||
## 3. Add Internal Service in Main Service
|
||||
|
||||
In the main service at `src/modules/brand/service.ts`, add the following imports and types at the top of the file:
|
||||
|
||||
```ts title="src/modules/brand/service.ts"
|
||||
// other imports...
|
||||
import { BrandClient, BrandClientOptions } from "./services"
|
||||
|
||||
type InjectedDependencies = {
|
||||
brandClient: BrandClient
|
||||
}
|
||||
```
|
||||
|
||||
Then, add the following in the `BrandModuleService` class:
|
||||
|
||||
```ts title="src/modules/brand/service.ts"
|
||||
class BrandModuleService extends MedusaService({
|
||||
Brand,
|
||||
}) {
|
||||
public client: BrandClient
|
||||
|
||||
constructor({ brandClient }: InjectedDependencies) {
|
||||
super(...arguments)
|
||||
|
||||
this.client = brandClient
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In the main module service, you first resolve through dependency injection the `brandClient` from the container and set it in a public property `client`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Pass Options to the Module
|
||||
|
||||
To pass options in the module, change its configurations in `medusa-config.js`:
|
||||
|
||||
```js title="medusa-config.js"
|
||||
module.exports = defineConfig({
|
||||
// ...
|
||||
modules: {
|
||||
brandModuleService: {
|
||||
resolve: "./modules/brand",
|
||||
options: {
|
||||
apiKey: process.env.BRAND_API_KEY || "temp",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
A module's configuration accepts an `options` property, which can hold any options to pass to the module.
|
||||
|
||||
---
|
||||
|
||||
## Next Steps: Sync Brand From Medusa to Third-Party System
|
||||
|
||||
In the next chapter, you'll learn how to sync brands created in Medusa to the third-party system using a workflow and a scheduled job.
|
||||
Reference in New Issue
Block a user