docs: add documentation for Locking Module (#11824)
* add locking docs * fix main navbar * added implementation example links * generate refs * update architecture * fix vale error
This commit is contained in:
@@ -71,11 +71,12 @@ You can replace any of the third-party services mentioned above to build your pr
|
||||
|
||||
[Architectural modules](!resources!/architectural-modules) integrate third-party services and systems for architectural features. Medusa has the following Architectural modules:
|
||||
|
||||
- [Workflow Engine Module](!resources!/architectural-modules/workflow-engine): Orchestrates workflows that hold the business logic of your application. You can integrate [Redis](!resources!/architectural-modules/workflow-engine/redis) to orchestrate workflows.
|
||||
- [Cache Module](!resources!/architectural-modules/cache): Caches data that require heavy computation. You can integrate a custom module to handle the caching with services like Memcached, or use the existing [Redis Cache Module](!resources!/architectural-modules/cache/redis).
|
||||
- [Event Module](!resources!/architectural-modules/event): A pub/sub system that allows you to subscribe to events and trigger them. You can integrate [Redis](!resources!/architectural-modules/event/redis) as the pub/sub system.
|
||||
- [File Module](!resources!/architectural-modules/file): Manages file uploads and storage, such as upload of product images. You can integrate [AWS S3](!resources!/architectural-modules/file/s3) for file storage.
|
||||
- [Locking Module](!resources!/architectural-modules/locking): Manages access to shared resources by multiple processes or threads, preventing conflict between processes and ensuring data consistency. You can integrate [Redis](!resources!/architectural-modules/locking/redis) for locking.
|
||||
- [Notification Module](!resources!/architectural-modules/notification): Sends notifications to customers and users, such as for order updates or newsletters. You can integrate [SendGrid](!resources!/architectural-modules/notification/sendgrid) for sending emails.
|
||||
- [Workflow Engine Module](!resources!/architectural-modules/workflow-engine): Orchestrates workflows that hold the business logic of your application. You can integrate [Redis](!resources!/architectural-modules/workflow-engine/redis) to orchestrate workflows.
|
||||
|
||||
All of the third-party services mentioned above can be replaced to help you build your preferred architecture and ecosystem.
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ export const generatedEditDates = {
|
||||
"app/learn/customization/integrate-systems/service/page.mdx": "2024-12-09T11:02:39.594Z",
|
||||
"app/learn/customization/next-steps/page.mdx": "2024-12-06T14:34:53.356Z",
|
||||
"app/learn/fundamentals/modules/architectural-modules/page.mdx": "2024-10-21T13:30:21.367Z",
|
||||
"app/learn/introduction/architecture/page.mdx": "2025-03-11T15:28:01.187Z",
|
||||
"app/learn/introduction/architecture/page.mdx": "2025-03-12T14:39:09.724Z",
|
||||
"app/learn/fundamentals/data-models/infer-type/page.mdx": "2024-12-09T15:54:08.713Z",
|
||||
"app/learn/fundamentals/custom-cli-scripts/seed-data/page.mdx": "2024-12-09T14:38:06.385Z",
|
||||
"app/learn/fundamentals/environment-variables/page.mdx": "2025-03-11T08:55:03.343Z",
|
||||
|
||||
+10416
-10396
File diff suppressed because it is too large
Load Diff
+50
-10
@@ -1,22 +1,68 @@
|
||||
import { CardList } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Cache Modules`,
|
||||
title: `Cache Module`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this document, you'll learn what a Cache Module is and how to use it in your Medusa application.
|
||||
|
||||
## What is a Cache Module?
|
||||
|
||||
A Cache Module is used to cache the results of computations such as price selection or various tax calculations.
|
||||
|
||||
The underlying database, third-party service, or caching logic is flexible since it's implemented in a module. You can choose from Medusa’s cache modules or create your own to support something more suitable for your architecture.
|
||||
|
||||
### Default Cache Module
|
||||
|
||||
By default, Medusa uses the [In-Memory Cache Module](./in-memory/page.mdx). This module uses a plain JavaScript Map object to store the cache data. While this is suitable for development, it's recommended to use other Cache Modules, such as the [Redis Cache Module](./redis/page.mdx), for production. You can also [Create a Cache Module](./create/page.mdx).
|
||||
|
||||
---
|
||||
|
||||
## How to Use the Cache Module?
|
||||
|
||||
You can use the registered Cache Module as part of the [workflows](!docs!/learn/fundamentals/workflows) you build for your custom features. A workflow is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||
|
||||
In a step of your workflow, you can resolve the Cache Module's service and use its methods to cache data, retrieve cached data, or clear the cache.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { Modules } from "@medusajs/framework/utils"
|
||||
import {
|
||||
createStep,
|
||||
createWorkflow,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
const step1 = createStep(
|
||||
"step-1",
|
||||
async ({}, { container }) => {
|
||||
const cacheModuleService = container.resolve(
|
||||
Modules.CACHE
|
||||
)
|
||||
|
||||
await cacheModuleService.set("key", "value")
|
||||
}
|
||||
)
|
||||
|
||||
export const workflow = createWorkflow(
|
||||
"workflow-1",
|
||||
() => {
|
||||
step1()
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
In the example above, you create a workflow that has a step. In the step, you resolve the service of the Cache Module from the [Medusa container](!docs!/learn/fundamentals/medusa-container).
|
||||
|
||||
Then, you use the `set` method of the Cache Module to cache the value `"value"` with the key `"key"`.
|
||||
|
||||
---
|
||||
|
||||
## List of Cache Modules
|
||||
|
||||
By default, Medusa uses the In-Memory Cache Module. This module uses a plain JavaScript Map object to store the cache data.
|
||||
|
||||
This is useful for development. However, for production, it's highly recommended to use other Cache Modules, such as the Redis Cache Module.
|
||||
Medusa provides the following Cache Modules. You can use one of them, or [Create a Cache Module](./create/page.mdx).
|
||||
|
||||
<CardList
|
||||
items={[
|
||||
@@ -38,9 +84,3 @@ This is useful for development. However, for production, it's highly recommended
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
---
|
||||
|
||||
## Create a Cache Module
|
||||
|
||||
To create a cache module, refer to [this guide](./create/page.mdx).
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
export const metadata = {
|
||||
title: `Local Event Bus Module`,
|
||||
title: `Local Event Module`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
The Local Event Bus Module uses Node EventEmitter to implement Medusa's pub/sub events system. The Node EventEmitter is limited to a single process environment.
|
||||
The Local Event Module uses Node EventEmitter to implement Medusa's pub/sub events system. The Node EventEmitter is limited to a single process environment.
|
||||
|
||||
This module is useful for development and testing, but it’s not recommended to be used in production.
|
||||
|
||||
@@ -12,11 +12,11 @@ For production, it’s recommended to use modules like [Redis Event Bus Module](
|
||||
|
||||
---
|
||||
|
||||
## Register the Local Event Bus Module
|
||||
## Register the Local Event Module
|
||||
|
||||
<Note>
|
||||
|
||||
The Local Event Bus Module is registered by default in your application.
|
||||
The Local Event Module is registered by default in your application.
|
||||
|
||||
</Note>
|
||||
|
||||
|
||||
@@ -1,22 +1,80 @@
|
||||
import { CardList } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Event Modules`,
|
||||
title: `Event Module`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this document, you'll learn what an Event Module is and how to use it in your Medusa application.
|
||||
|
||||
## What is an Event Module?
|
||||
|
||||
An Event Module implements the underlying publish/subscribe system that handles queueing events, emitting them, and executing their subscribers.
|
||||
|
||||
This makes the event architecture customizable, as you can either choose one of Medusa’s event modules or create your own.
|
||||
|
||||
<Note>
|
||||
|
||||
Learn more about Medusa's event systems in the [Events and Subscribers documentation](!docs!/learn/fundamentals/events-and-subscribers).
|
||||
|
||||
</Note>
|
||||
|
||||
### Default Event Module
|
||||
|
||||
By default, Medusa uses the [Local Event Module](./local/page.mdx). This module uses Node’s EventEmitter to implement the publish/subscribe system. While this is suitable for development, it's recommended to use other Event Modules, such as the [Redis Event Module](./redis/page.mdx), for production. You can also [Create an Event Module](./create/page.mdx).
|
||||
|
||||
---
|
||||
|
||||
## How to Use the Event Module?
|
||||
|
||||
You can use the registered Event Module as part of the [workflows](!docs!/learn/fundamentals/workflows) you build for your custom features. A workflow is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||
|
||||
Medusa provides the helper step [emitEventStep](/references/helper-steps/emitEventStep) that you can use in your workflow. You can also resolve the Event Module's service in a step of your workflow and use its methods to emit events.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { Modules } from "@medusajs/framework/utils"
|
||||
import {
|
||||
createStep,
|
||||
createWorkflow,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
const step1 = createStep(
|
||||
"step-1",
|
||||
async ({}, { container }) => {
|
||||
const eventModuleService = container.resolve(
|
||||
Modules.EVENT
|
||||
)
|
||||
|
||||
await eventModuleService.emit({
|
||||
name: "custom.event",
|
||||
data: {
|
||||
id: "123",
|
||||
// other data payload
|
||||
},
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
export const workflow = createWorkflow(
|
||||
"workflow-1",
|
||||
() => {
|
||||
step1()
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
In the example above, you create a workflow that has a step. In the step, you resolve the service of the Event Module from the [Medusa container](!docs!/learn/fundamentals/medusa-container).
|
||||
|
||||
Then, you use the `emit` method of the Event Module to emit an event with the name `"custom.event"` and the data payload `{ id: "123" }`.
|
||||
|
||||
---
|
||||
|
||||
## List of Event Modules
|
||||
|
||||
By default, Medusa uses the Local Event Module. This module uses Node’s EventEmitter to implement the publish/subscribe system.
|
||||
|
||||
This is useful for development. However, for production, it’s highly recommended to use other Event Modules, Redis Event Module.
|
||||
Medusa provides the following Event Modules. You can use one of them, or [Create an Event Module](./create/page.mdx).
|
||||
|
||||
<CardList
|
||||
items={[
|
||||
@@ -38,9 +96,3 @@ This is useful for development. However, for production, it’s highly recommend
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
---
|
||||
|
||||
## Create a Event Module
|
||||
|
||||
To create an event module, refer to [this guide](./create/page.mdx).
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Table, Prerequisites } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Redis Event Bus Module`,
|
||||
title: `Redis Event Module`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
The Redis Event Bus Module uses Redis to implement Medusa's pub/sub events system.
|
||||
The Redis Event Module uses Redis to implement Medusa's pub/sub events system.
|
||||
|
||||
It's powered by BullMQ and `io-redis`. BullMQ is responsible for the message queue and worker, and `io-redis` is the underlying Redis client that BullMQ connects to for events storage.
|
||||
|
||||
@@ -14,7 +14,7 @@ In production, it's recommended to use this module.
|
||||
|
||||
---
|
||||
|
||||
## Register the Redis Events Bus Module
|
||||
## Register the Redis Event Module
|
||||
|
||||
<Prerequisites items={[
|
||||
{
|
||||
@@ -55,7 +55,7 @@ Make sure to add the following environment variables:
|
||||
EVENTS_REDIS_URL=<YOUR_REDIS_URL>
|
||||
```
|
||||
|
||||
### Redis Event Bus Module Options
|
||||
### Redis Event Module Options
|
||||
|
||||
<Table>
|
||||
<Table.Header>
|
||||
|
||||
@@ -1,18 +1,78 @@
|
||||
import { CardList } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `File Module Providers`,
|
||||
title: `File Module`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
## What is a File Module Provider?
|
||||
In this document, you'll learn about the File Module and its providers.
|
||||
|
||||
The File Module exposes the functionalities to upload assets, such as product images, to the Medusa application. A file module provider implements the underlying logic of handling uploads and downloads of assets, such as integrating third-party services. The File Module must have one file module provider configured.
|
||||
## What is the File Module?
|
||||
|
||||
By default, Medusa uses the Local File Module. This module uploads files to the `uploads` directory of your Medusa application.
|
||||
The File Module exposes the functionalities to upload assets, such as product images, to the Medusa application. Medusa uses the File Module in its core commerce features for all file operations, and you can use it in your custom features as well.
|
||||
|
||||
This is useful for development. However, for production, it’s highly recommended to use other File Modules, such as the S3 Module.
|
||||
---
|
||||
|
||||
## How to Use the File Module?
|
||||
|
||||
You can use the File Module as part of the [workflows](!docs!/learn/fundamentals/workflows) you build for your custom features. A workflow is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||
|
||||
In a step of your workflow, you can resolve the File Module's service and use its methods to upload files, retrieve files, or delete files.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { Modules } from "@medusajs/framework/utils"
|
||||
import {
|
||||
createStep,
|
||||
createWorkflow,
|
||||
StepResponse,
|
||||
WorkflowResponse,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
const step1 = createStep(
|
||||
"step-1",
|
||||
async ({}, { container }) => {
|
||||
const fileModuleService = container.resolve(
|
||||
Modules.FILE
|
||||
)
|
||||
|
||||
const { url } = await fileModuleService.retrieveFile("image.png")
|
||||
|
||||
return new StepResponse(url)
|
||||
}
|
||||
)
|
||||
|
||||
export const workflow = createWorkflow(
|
||||
"workflow-1",
|
||||
() => {
|
||||
const url = step1()
|
||||
|
||||
return new WorkflowResponse(url)
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
In the example above, you create a workflow that has a step. In the step, you resolve the service of the File Module from the [Medusa container](!docs!/learn/fundamentals/medusa-container).
|
||||
|
||||
Then, you use the `retrieveFile` method of the File Module to retrieve the URL of the file with the name `"image.png"`. The URL is then returned as a response from the step and the workflow.
|
||||
|
||||
---
|
||||
|
||||
### What is a File Module Provider?
|
||||
|
||||
A File Module Provider implements the underlying logic of handling uploads and downloads of assets, such as integrating third-party services. The File Module then uses the registered File Module Provider to handle file operations.
|
||||
|
||||
<Note>
|
||||
|
||||
Only one File Module Provider can be registered at a time. If you register multiple providers, the File Module will throw an error.
|
||||
|
||||
</Note>
|
||||
|
||||
By default, Medusa uses the [Local File Module](./local/page.mdx). This module uploads files to the `uploads` directory of your Medusa application.
|
||||
|
||||
This is useful for development. However, for production, it’s highly recommended to use other File Module Providers, such as the S3 File Module Provider. You can also [Create a File Provider](/references/file-provider-module).
|
||||
|
||||
<CardList
|
||||
items={[
|
||||
@@ -34,9 +94,3 @@ This is useful for development. However, for production, it’s highly recommend
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
---
|
||||
|
||||
## Create a File Module Provider
|
||||
|
||||
To create a file module provider, refer to [this guide](/references/file-provider-module).
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
import { Table, CardList } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Locking Module`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this document, you'll learn about the Locking Module and its providers.
|
||||
|
||||
## What is the Locking Module?
|
||||
|
||||
The Locking Module manages access to shared resources by multiple processes or threads. It prevents conflicts between processes that are trying to access the same resource at the same time, and ensures data consistency.
|
||||
|
||||
Medusa uses the Locking Module to control concurrency, avoid race conditions, and protect parts of code that should not be executed by more than one process at a time. This is especially essential in distributed or multi-threaded environments.
|
||||
|
||||
For example, Medusa uses the Locking Module in inventory management to ensure that only one transaction can update the stock levels at a time. By using the Locking Module in this scenario, Medusa prevents overselling an inventory item and keeps its quantity amounts accurate, even during high traffic periods or when receiving concurrent requests.
|
||||
|
||||
---
|
||||
|
||||
## How to Use the Locking Module?
|
||||
|
||||
You can use the Locking Module as part of the [workflows](!docs!/learn/fundamentals/workflows) you build for your custom features. A workflow is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||
|
||||
In a step of your workflow, you can resolve the Locking Module's service and use its methods to execute an asynchronous job, acquire a lock, or release locks.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { Modules } from "@medusajs/framework/utils"
|
||||
import {
|
||||
createStep,
|
||||
createWorkflow,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
const step1 = createStep(
|
||||
"step-1",
|
||||
async ({}, { container }) => {
|
||||
const lockingModuleService = container.resolve(
|
||||
Modules.LOCKING
|
||||
)
|
||||
const productModuleService = container.resolve(
|
||||
Modules.PRODUCT
|
||||
)
|
||||
|
||||
await lockingModuleService.execute("prod_123", async () => {
|
||||
await productModuleService.deleteProduct("prod_123")
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
export const workflow = createWorkflow(
|
||||
"workflow-1",
|
||||
() => {
|
||||
step1()
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
In the example above, you create a workflow that has a step. In the step, you resolve the services of the Locking and Product modules from the [Medusa container](!docs!/learn/fundamentals/medusa-container).
|
||||
|
||||
Then, you use the `execute` method of the Locking Module to acquire a lock for the product with the ID `prod_123` and execute an asynchronous function, which deletes the product.
|
||||
|
||||
---
|
||||
|
||||
## When to Use the Locking Module?
|
||||
|
||||
You should use the Locking Module when you need to ensure that only one process can access a shared resource at a time. As mentioned in the inventory example previously, you don't want customers to order quantities of inventory that are not available, or to update the stock levels of an item concurrently.
|
||||
|
||||
In those scenarios, you can use the Locking Module to acquire a lock for a resource and execute a critical section of code that should not be accessed by multiple processes simultaneously.
|
||||
|
||||
---
|
||||
|
||||
## What is a Locking Module Provider?
|
||||
|
||||
A Locking Module Provider implements the underlying logic of the Locking Module. It manages the locking mechanisms and ensures that only one process can access a shared resource at a time.
|
||||
|
||||
Medusa provides [multiple Locking Module Providers](#list-of-locking-module-providers) that are suitable for development and production. You can also create a [custom Locking Module Provider](/references/locking-module-provider) to implement custom locking mechanisms or integrate with third-party services.
|
||||
|
||||
### Default Locking Module Provider
|
||||
|
||||
By default, Medusa uses the In-Memory Locking Module Provider. This provider uses a plain JavaScript map to store the locks. While this is useful for development, it is not recommended for production environments as it is only intended for use in a single-instance environment.
|
||||
|
||||
To add more providers, you can register them in the `medusa-config.ts` file. For example:
|
||||
|
||||
```ts
|
||||
module.exports = defineConfig({
|
||||
// ...
|
||||
modules: [
|
||||
{
|
||||
resolve: "@medusajs/medusa/locking",
|
||||
options: {
|
||||
providers: [
|
||||
// add providers here...
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
When you register other providers in `medusa-config.ts`, Medusa will set the default provider based on the following scenarios:
|
||||
|
||||
<Table>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell>Scenario</Table.HeaderCell>
|
||||
<Table.HeaderCell>Default Provider</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
One provider is registered.
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
The registered provider.
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
Multiple providers are registered and none of them has an `is_default` flag.
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
In-Memory Locking Module Provider.
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
Multiple providers and one of them has an `is_default` flag.
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
The provider with the `is_default` flag.
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table>
|
||||
|
||||
---
|
||||
|
||||
## List of Locking Module Providers
|
||||
|
||||
Medusa provides the following Locking Module Providers. You can use one of them, or [Create a Locking Module Provider](./references/locking-module-provider).
|
||||
|
||||
<CardList
|
||||
items={[
|
||||
{
|
||||
title: "Redis",
|
||||
href: "/architectural-modules/locking/redis",
|
||||
badge: {
|
||||
variant: "green",
|
||||
children: "Recommended"
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "PostgreSQL",
|
||||
href: "/architectural-modules/locking/postgres",
|
||||
}
|
||||
]}
|
||||
/>
|
||||
@@ -0,0 +1,105 @@
|
||||
export const metadata = {
|
||||
title: `PostgreSQL Locking Module Provider`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
The PostgreSQL Locking Module Provider uses PostgreSQL's advisory locks to control and manage locks across multiple instances of Medusa. Advisory locks are lightweight locks that do not interfere with other database transactions. By using PostgreSQL's advisory locks, Medusa can create distributed locks directly through the database.
|
||||
|
||||
The provider uses the existing PostgreSQL database in your application to manage locks, so you don't need to set up a separate database or service to manage locks.
|
||||
|
||||
<Note>
|
||||
|
||||
While this provider is suitable for production environments, it's recommended to use the [Redis Locking Module Provider](../redis/page.mdx) if possible.
|
||||
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## Register the PostgreSQL Locking Module Provider
|
||||
|
||||
To register the PostgreSQL Locking Module Provider, add it to the list of providers of the Locking Module in `medusa-config.ts`:
|
||||
|
||||
```ts title="medusa-config.ts"
|
||||
module.exports = defineConfig({
|
||||
// ...
|
||||
modules: [
|
||||
{
|
||||
resolve: "@medusajs/medusa/locking",
|
||||
options: {
|
||||
providers: [
|
||||
{
|
||||
resolve: "@medusajs/medusa/locking-postgres",
|
||||
id: "postgres-lock",
|
||||
// set this if you want this provider to be used by default
|
||||
// and you have other Locking Module Providers registered.
|
||||
is_default: true,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
### Run Migrations
|
||||
|
||||
The PostgreSQL Locking Module Provider requires a new `locking` table in the database to store the locks. So, you must run the migrations after registering the provider:
|
||||
|
||||
```bash
|
||||
npx medusa db:migrate
|
||||
```
|
||||
|
||||
This will run the migration in the PostgreSQL Locking Module Provider and create the necessary table in the database.
|
||||
|
||||
---
|
||||
|
||||
## Use Provider with Locking Module
|
||||
|
||||
The PostgreSQL Locking Module Provider will be the default provider if you don't register any other providers, or if you set the `is_default` flag to `true`:
|
||||
|
||||
export const defaultHighlights = [
|
||||
["11", "is_default"]
|
||||
]
|
||||
|
||||
```ts title="medusa-config.ts" highlights={defaultHighlights}
|
||||
module.exports = defineConfig({
|
||||
// ...
|
||||
modules: [
|
||||
{
|
||||
resolve: "@medusajs/medusa/locking",
|
||||
options: {
|
||||
providers: [
|
||||
{
|
||||
resolve: "@medusajs/medusa/locking-postgres",
|
||||
id: "postgres-lock",
|
||||
is_default: true,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
If you use the Locking Module in your customizations, the PostgreSQL Locking Module Provider will be used by default in this case. You can also explicitly use this provider by passing its identifier `lp_locking-postgres` to the Locking Module's service methods.
|
||||
|
||||
For example, when using the `acquire` method in a [workflow step](!docs!/learn/fundamentals/workflows):
|
||||
|
||||
```ts
|
||||
import { Modules } from "@medusajs/framework/utils"
|
||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
const step1 = createStep(
|
||||
"step-1",
|
||||
async ({}, { container }) => {
|
||||
const lockingModuleService = container.resolve(
|
||||
Modules.LOCKING
|
||||
)
|
||||
|
||||
await lockingModuleService.acquire("prod_123", {
|
||||
provider: "lp_locking-postgres",
|
||||
})
|
||||
}
|
||||
)
|
||||
```
|
||||
@@ -0,0 +1,289 @@
|
||||
import { Table, Prerequisites } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Redis Locking Module Provider`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
The Redis Locking Module Provider uses Redis to manage locks across multiple instances of Medusa. Redis ensures that locks are globally available, which is ideal for distributed environments.
|
||||
|
||||
This provider is recommended for production environments where Medusa is running in a multi-instance setup.
|
||||
|
||||
---
|
||||
|
||||
## Register the Redis Locking Module Provider
|
||||
|
||||
<Prerequisites
|
||||
items={[
|
||||
{
|
||||
text: "A redis server set up locally or a database in your deployed application.",
|
||||
link: "https://redis.io/download",
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
To register the Redis Locking Module Provider, add it to the list of providers of the Locking Module in `medusa-config.ts`:
|
||||
|
||||
```ts title="medusa-config.ts"
|
||||
module.exports = defineConfig({
|
||||
// ...
|
||||
modules: [
|
||||
{
|
||||
resolve: "@medusajs/medusa/locking",
|
||||
options: {
|
||||
providers: [
|
||||
{
|
||||
resolve: "@medusajs/medusa/locking-redis",
|
||||
id: "redis-lock",
|
||||
// set this if you want this provider to be used by default
|
||||
// and you have other Locking Module Providers registered.
|
||||
is_default: true,
|
||||
options: {
|
||||
redisUrl: process.env.LOCKING_REDIS_URL,
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Make sure to add the following environment variable:
|
||||
|
||||
```bash
|
||||
LOCKING_REDIS_URL=<YOUR_LOCKING_REDIS_URL>
|
||||
```
|
||||
|
||||
Where `<YOUR_LOCKING_REDIS_URL>` is the URL of your Redis server, either locally or in the deployed environment.
|
||||
|
||||
<Note title="Tip">
|
||||
|
||||
The default Redis URL in a local environment is `redis://localhost:6379`.
|
||||
|
||||
</Note>
|
||||
|
||||
### Redis Locking Module Provider Options
|
||||
|
||||
<Table>
|
||||
<Table.Header>
|
||||
<Table.Row>
|
||||
<Table.HeaderCell>Option</Table.HeaderCell>
|
||||
<Table.HeaderCell>Description</Table.HeaderCell>
|
||||
<Table.HeaderCell>Required</Table.HeaderCell>
|
||||
<Table.HeaderCell>Default</Table.HeaderCell>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
|
||||
`redisUrl`
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
A string indicating the Redis connection URL.
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
Yes
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
\-
|
||||
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
|
||||
`redisOptions`
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
An object of Redis options. Refer to the [Redis API Reference](https://redis.github.io/ioredis/index.html#RedisOptions) for details on accepted properties.
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
No
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
\-
|
||||
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
|
||||
`namespace`
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
A string used to prefix all locked keys with `{namespace}`.
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
No
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
`medusa_lock:`. So, all locked keys are prefixed with `medusa_lock:`.
|
||||
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
|
||||
`waitLockingTimeout`
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
A number indicating the default timeout (in seconds) to wait while acquiring a lock. This timeout is used when no timeout is specified when executing an asynchronous job or acquiring a lock.
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
No
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
`5`
|
||||
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
|
||||
`defaultRetryInterval`
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
A number indicating the time (in milliseconds) to wait before retrying to acquire a lock.
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
No
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
`5`
|
||||
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
|
||||
`maximumRetryInterval`
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
A number indicating the maximum time (in milliseconds) to wait before retrying to acquire a lock.
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
No
|
||||
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
|
||||
`200`
|
||||
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table.Body>
|
||||
</Table>
|
||||
|
||||
---
|
||||
|
||||
## Test out the Module
|
||||
|
||||
To test out the Redis Locking Module Provider, start the Medusa application:
|
||||
|
||||
```bash npm2yarn
|
||||
npm run dev
|
||||
```
|
||||
|
||||
You'll see the following message logged in the terminal:
|
||||
|
||||
```bash
|
||||
info: Connection to Redis in "locking-redis" provider established
|
||||
```
|
||||
|
||||
This message indicates that the Redis Locking Module Provider has successfully connected to the Redis server.
|
||||
|
||||
If you set the `is_default` flag to `true` in the provider options or you only registered the Redis Locking Module Provider, the Locking Module will use it by default for all locking operations.
|
||||
|
||||
---
|
||||
|
||||
## Use Provider with Locking Module
|
||||
|
||||
The Redis Locking Module Provider will be the default provider if you don't register any other providers, or if you set the `is_default` flag to `true`:
|
||||
|
||||
export const defaultHighlights = [
|
||||
["11", "is_default"]
|
||||
]
|
||||
|
||||
```ts title="medusa-config.ts" highlights={defaultHighlights}
|
||||
module.exports = defineConfig({
|
||||
// ...
|
||||
modules: [
|
||||
{
|
||||
resolve: "@medusajs/medusa/locking",
|
||||
options: {
|
||||
providers: [
|
||||
{
|
||||
resolve: "@medusajs/medusa/locking-redis",
|
||||
id: "redis-lock",
|
||||
is_default: true,
|
||||
options: {
|
||||
// ...
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
If you use the Locking Module in your customizations, the Redis Locking Module Provider will be used by default in this case. You can also explicitly use this provider by passing its identifier `lp_locking-redis` to the Locking Module's service methods.
|
||||
|
||||
For example, when using the `acquire` method in a [workflow step](!docs!/learn/fundamentals/workflows):
|
||||
|
||||
```ts
|
||||
import { Modules } from "@medusajs/framework/utils"
|
||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
const step1 = createStep(
|
||||
"step-1",
|
||||
async ({}, { container }) => {
|
||||
const lockingModuleService = container.resolve(
|
||||
Modules.LOCKING
|
||||
)
|
||||
|
||||
await lockingModuleService.acquire("prod_123", {
|
||||
provider: "lp_locking-redis",
|
||||
})
|
||||
}
|
||||
)
|
||||
```
|
||||
@@ -1,22 +1,73 @@
|
||||
import { CardList } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Notification Module Providers`,
|
||||
title: `Notification Module`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
In this document, you'll learn about the Notification Module and its providers.
|
||||
|
||||
## What is the Notification Module?
|
||||
|
||||
The Notification Module exposes the functionalities to send a notification to a customer or user. For example, sending an order confirmation email. Medusa uses the Notification Module in its core commerce features for notification operations, and you an use it in your custom features as well.
|
||||
|
||||
---
|
||||
|
||||
## How to Use the Notification Module?
|
||||
|
||||
You can use the Notification Module as part of the [workflows](!docs!/learn/fundamentals/workflows) you build for your custom features. A workflow is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||
|
||||
In a step of your workflow, you can resolve the Notification Module's service and use its methods to send notifications.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { Modules } from "@medusajs/framework/utils"
|
||||
import {
|
||||
createStep,
|
||||
createWorkflow,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
const step1 = createStep(
|
||||
"step-1",
|
||||
async ({}, { container }) => {
|
||||
const notificationModuleService = container.resolve(
|
||||
Modules.NOTIFICATION
|
||||
)
|
||||
|
||||
await notificationModuleService.createNotifications({
|
||||
to: "customer@gmail.com",
|
||||
channel: "email",
|
||||
template: "product-created",
|
||||
data,
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
export const workflow = createWorkflow(
|
||||
"workflow-1",
|
||||
() => {
|
||||
step1()
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
In the example above, you create a workflow that has a step. In the step, you resolve the service of the Notification Module from the [Medusa container](!docs!/learn/fundamentals/medusa-container).
|
||||
|
||||
Then, you use the `createNotifications` method of the Notification Module to send an email notification.
|
||||
|
||||
Find a full example of sending a notification in the [Send Notification guide](./send-notification/page.mdx).
|
||||
|
||||
---
|
||||
|
||||
## What is a Notification Module Provider?
|
||||
|
||||
The Notification Module exposes the functionalities to send a notification to a customer or user. For example, sending an order confirmation email.
|
||||
A Notification Module Provider implements the underlying logic of sending notification. It either integrates a third-party service or uses custom logic to send the notification.
|
||||
|
||||
You can resolve the Notification Module and send notifications in API routes, subscribers, or other resources.
|
||||
By default, Medusa uses the [Local Notification Module](./local/page.mdx) which only simulates sending the notification by logging a message in the terminal.
|
||||
|
||||
A notification module provider implements the underlying logic of sending notification. It either integrates a third-party service or uses custom logic to send the notification.
|
||||
|
||||
By default, Medusa uses the Local Notification Module which only simulates sending the notification by logging a message in the terminal.
|
||||
|
||||
Medusa provides other Notification Modules that actually send notifications, such as the SendGrid Notification Module Provider.
|
||||
Medusa provides other Notification Modules that actually send notifications, such as the [SendGrid Notification Module Provider](./send-notification/page.mdx). You can also [Create a Notification Module Provider](/references/notification-provider-module).
|
||||
|
||||
<CardList
|
||||
items={[
|
||||
@@ -43,7 +94,9 @@ Medusa provides other Notification Modules that actually send notifications, suc
|
||||
|
||||
## Notification Module Provider Channels
|
||||
|
||||
When you send a notification, you specify the channel to send it through, such as `email` or `sms`. Each provider defined in the Notification Module's `providers` option has a `channels` option specifying which channels it can be used in. Only one provider can be setup for each channel.
|
||||
When you send a notification, you specify the channel to send it through, such as `email` or `sms`.
|
||||
|
||||
You register providers of the Notification Module in `medusa-config.ts`. For each provider, you pass a `channels` option specifying which channels the provider can be used in. Only one provider can be setup for each channel.
|
||||
|
||||
For example:
|
||||
|
||||
@@ -76,9 +129,3 @@ module.exports = {
|
||||
```
|
||||
|
||||
The `channels` option is an array of strings indicating the channels this provider is used for.
|
||||
|
||||
---
|
||||
|
||||
## Create a Notification Module Provider
|
||||
|
||||
To create a notification module provider, refer to [this guide](/references/notification-provider-module).
|
||||
|
||||
@@ -6,17 +6,17 @@ export const metadata = {
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
Medusa's architectural functionalities, such as emitting and subscribing to events or caching data, are all implemented in architectural modules. An architectural module is a package that can be installed and used in any Medusa application. These modules allow you to choose and integrate custom services for architectural purposes.
|
||||
Medusa's architectural functionalities, such as emitting and subscribing to events or caching data, are all implemented in Architectural Modules. An Architectural Module is a package that can be installed and used in any Medusa application. These modules allow you to choose and integrate custom services for architectural purposes.
|
||||
|
||||
For example, you can use our [Redis Event Module](./event/redis/page.mdx) to handle event functionalities, or create a custom module that implements these functionalities with Memcached. Learn more about Medusa's architecture in [this documentation](!docs!/learn/introduction/architecture).
|
||||
For example, you can use our [Redis Event Module](./event/redis/page.mdx) to handle event functionalities, or create a custom module that implements these functionalities with Memcached. Learn more in [the Architecture documentation](!docs!/learn/introduction/architecture).
|
||||
|
||||
This section of the documentation showcases official architectural modules and how to configure them in your application.
|
||||
This section of the documentation showcases Medusa's Architectural Modules, how they work, and how to use them in your Medusa application.
|
||||
|
||||
## Cache Modules
|
||||
## Cache Module
|
||||
|
||||
A Cache Module is used to cache the results of computations such as price selection or various tax calculations. Learn more in [this documentation](./cache/page.mdx).
|
||||
|
||||
The following modules are provided by Medusa. You can also create your own cache module as explained in [this guide](./cache/create/page.mdx).
|
||||
The following Cache modules are provided by Medusa. You can also create your own cache module as explained in [this guide](./cache/create/page.mdx).
|
||||
|
||||
<CardList
|
||||
items={[
|
||||
@@ -41,11 +41,11 @@ The following modules are provided by Medusa. You can also create your own cache
|
||||
|
||||
---
|
||||
|
||||
## Event Modules
|
||||
## Event Module
|
||||
|
||||
An Event Module implements the underlying publish/subscribe system that handles queueing events, emitting them, and executing their subscribers. Learn more in [this documentation](./event/page.mdx).
|
||||
|
||||
The following modules are provided by Medusa. You can also create your own event module as explained in [this guide](./event/create/page.mdx).
|
||||
The following Event modules are provided by Medusa. You can also create your own event module as explained in [this guide](./event/create/page.mdx).
|
||||
|
||||
<CardList
|
||||
items={[
|
||||
@@ -70,11 +70,11 @@ The following modules are provided by Medusa. You can also create your own event
|
||||
|
||||
---
|
||||
|
||||
## File Module Providers
|
||||
## File Module
|
||||
|
||||
A file module provider implements the logic of handling uploads and downloads of assets, such as integrating third-party services. Learn more in [this documentation](./file/page.mdx).
|
||||
The File Module handles file upload and storage of assets, such as product images. Refer to the [File Module documentation](./file/page.mdx) to learn more about it.
|
||||
|
||||
The following modules are provided by Medusa. You can also create your own file module as explained in [this guide](/references/file-provider-module).
|
||||
The File Module has module providers that implement the underlying logic of handling uploads and downloads of assets, such as integrating third-party services. The following File Module Providers are provided by Medusa. You can also create a custom provider as explained in the [Create File Module Provider guide](/references/file-provider-module).
|
||||
|
||||
<CardList
|
||||
items={[
|
||||
@@ -99,11 +99,36 @@ The following modules are provided by Medusa. You can also create your own file
|
||||
|
||||
---
|
||||
|
||||
## Notification Module Providers
|
||||
## Locking Module
|
||||
|
||||
A notification module provider implements the logic of sending notifications, typically through integrating a third-party service. Learn more in [this documentation](./notification/page.mdx).
|
||||
The Locking Module manages access to shared resources by multiple processes or threads. It prevents conflicts between processes and ensures data consistency. Refer to the [Locking Module documentation](./locking/page.mdx) to learn more about it.
|
||||
|
||||
The following modules are provided by Medusa. You can also create your own file module as explained in [this guide](/references/notification-provider-module).
|
||||
The Locking Module uses module providers that implement the underlying logic of the locking mechanism. The following Locking Module Providers are provided by Medusa. You can also create a custom provider as explained in the [Create Locking Module Provider guide](/references/locking-provider-module).
|
||||
|
||||
<CardList
|
||||
items={[
|
||||
{
|
||||
title: "Redis",
|
||||
href: "/architectural-modules/locking/redis",
|
||||
badge: {
|
||||
variant: "green",
|
||||
children: "Recommended"
|
||||
}
|
||||
},
|
||||
{
|
||||
title: "PostgreSQL",
|
||||
href: "/architectural-modules/locking/postgres",
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
---
|
||||
|
||||
## Notification Module
|
||||
|
||||
The Notification Module handles sending notifications to users or customers, such as reset password instructions or newsletters. Refer to the [Notifcation Module documentation](./notification/page.mdx) to learn more about it.
|
||||
|
||||
The Notification Module has module providers that implement the underlying logic of sending notifications, typically through integrating a third-party service. The following modules are provided by Medusa. You can also create a custom provider as explained in the [Create Notification Module Provider guide](/references/notification-provider-module).
|
||||
|
||||
<CardList
|
||||
items={[
|
||||
@@ -151,11 +176,11 @@ The following modules are provided by Medusa. You can also create your own file
|
||||
|
||||
---
|
||||
|
||||
## Workflow Engine Modules
|
||||
## Workflow Engine Module
|
||||
|
||||
Workflow engine modules handle tracking and recording the transactions and statuses of workflows and their steps. Learn more in [this documentation](./workflow-engine/page.mdx).
|
||||
A Workflow Engine Module handles tracking and recording the transactions and statuses of workflows and their steps. Learn more about it in the [Worklow Engine Module documentation](./workflow-engine/page.mdx).
|
||||
|
||||
The following modules are provided by Medusa.
|
||||
The following Workflow Engine modules are provided by Medusa.
|
||||
|
||||
<CardList
|
||||
items={[
|
||||
|
||||
@@ -1,14 +1,74 @@
|
||||
import { CardList } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Workflow Engine Modules`,
|
||||
title: `Workflow Engine Module`,
|
||||
}
|
||||
|
||||
# {metadata.title}
|
||||
|
||||
Workflow engine modules handle tracking and recording the transactions and statuses of workflows and their steps.
|
||||
In this document, you'll learn what a Workflow Engine Module is and how to use it in your Medusa application.
|
||||
|
||||
Medusa uses the In-Memory Workflow Engine Module by default. For production purposes, it's recommended to use the Redis Engine Module instead.
|
||||
## What is a Workflow Engine Module?
|
||||
|
||||
A Workflow Engine Module handles tracking and recording the transactions and statuses of workflows and their steps. It can use custom mechanism or integrate a third-party service.
|
||||
|
||||
### Default Workflow Engine Module
|
||||
|
||||
Medusa uses the [In-Memory Workflow Engine Module](./in-memory/page.mdx) by default. For production purposes, it's recommended to use the [Redis Workflow Engine Module](./redis/page.mdx) instead.
|
||||
|
||||
---
|
||||
|
||||
## How to Use the Workflow Engine Module?
|
||||
|
||||
You can use the registered Workflow Engine Module as part of the [workflows](!docs!/learn/fundamentals/workflows) you build for your custom features. A workflow is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
|
||||
|
||||
In a step of your workflow, you can resolve the Workflow Engine Module's service and use its methods to track and record the transactions and statuses of workflows and their steps.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
import { Modules } from "@medusajs/framework/utils"
|
||||
import {
|
||||
createStep,
|
||||
createWorkflow,
|
||||
StepResponse,
|
||||
WorkflowResponse,
|
||||
} from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
const step1 = createStep(
|
||||
"step-1",
|
||||
async ({}, { container }) => {
|
||||
const workflowEngineService = container.resolve(
|
||||
Modules.WORKFLOW_ENGINE
|
||||
)
|
||||
|
||||
const [workflowExecution] = await workflowEngineService.listWorkflowExecutions({
|
||||
transaction_id: transaction_id,
|
||||
})
|
||||
|
||||
return new StepResponse(workflowExecution)
|
||||
}
|
||||
)
|
||||
|
||||
export const workflow = createWorkflow(
|
||||
"workflow-1",
|
||||
() => {
|
||||
const workflowExecution = step1()
|
||||
|
||||
return new WorkflowResponse(workflowExecution)
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
In the example above, you create a workflow that has a step. In the step, you resolve the service of the Workflow Engine Module from the [Medusa container](!docs!/learn/fundamentals/medusa-container).
|
||||
|
||||
Then, you use the `listWorkflowExecutions` method of the Workflow Engine Module to list the workflow executions with the transaction ID `transaction_id`. The workflow execution is then returned as a response from the step and the workflow.
|
||||
|
||||
---
|
||||
|
||||
## List of Workflow Engine Modules
|
||||
|
||||
Medusa provides the following Workflow Engine Modules.
|
||||
|
||||
<CardList
|
||||
items={[
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChildDocs } from "docs-ui"
|
||||
import { CardList } from "docs-ui"
|
||||
|
||||
export const metadata = {
|
||||
title: `Commerce Modules`,
|
||||
@@ -14,7 +14,82 @@ A commerce module also defines data models, representing tables in the database.
|
||||
|
||||
## Commerce Modules List
|
||||
|
||||
<ChildDocs onlyTopLevel={true} defaultItemsPerRow={2} hideItems={["Overview"]} />
|
||||
<CardList
|
||||
items={[
|
||||
{
|
||||
title: "API Key Module",
|
||||
href: "/commerce-modules/api-key"
|
||||
},
|
||||
{
|
||||
title: "Auth Module",
|
||||
href: "/commerce-modules/auth"
|
||||
},
|
||||
{
|
||||
title: "Cart Module",
|
||||
href: "/commerce-modules/cart"
|
||||
},
|
||||
{
|
||||
title: "Currency Module",
|
||||
href: "/commerce-modules/currency"
|
||||
},
|
||||
{
|
||||
title: "Customer Module",
|
||||
href: "/commerce-modules/customer"
|
||||
},
|
||||
{
|
||||
title: "Fulfillment Module",
|
||||
href: "/commerce-modules/fulfillment"
|
||||
},
|
||||
{
|
||||
title: "Inventory Module",
|
||||
href: "/commerce-modules/inventory"
|
||||
},
|
||||
{
|
||||
title: "Order Module",
|
||||
href: "/commerce-modules/order"
|
||||
},
|
||||
{
|
||||
title: "Payment Module",
|
||||
href: "/commerce-modules/payment"
|
||||
},
|
||||
{
|
||||
title: "Pricing Module",
|
||||
href: "/commerce-modules/pricing"
|
||||
},
|
||||
{
|
||||
title: "Product Module",
|
||||
href: "/commerce-modules/product"
|
||||
},
|
||||
{
|
||||
title: "Promotion Module",
|
||||
href: "/commerce-modules/promotion"
|
||||
},
|
||||
{
|
||||
title: "Region Module",
|
||||
href: "/commerce-modules/region"
|
||||
},
|
||||
{
|
||||
title: "Sales Channel Module",
|
||||
href: "/commerce-modules/sales-channel"
|
||||
},
|
||||
{
|
||||
title: "Stock Location Module",
|
||||
href: "/commerce-modules/stock-location"
|
||||
},
|
||||
{
|
||||
title: "Store Module",
|
||||
href: "/commerce-modules/store"
|
||||
},
|
||||
{
|
||||
title: "Tax Module",
|
||||
href: "/commerce-modules/tax"
|
||||
},
|
||||
{
|
||||
title: "User Module",
|
||||
href: "/commerce-modules/user"
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -194,29 +194,29 @@ export const generatedEditDates = {
|
||||
"app/commerce-modules/auth/auth-identity-and-actor-types/page.mdx": "2025-01-07T09:02:27.235Z",
|
||||
"app/commerce-modules/api-key/page.mdx": "2025-02-26T11:18:00.337Z",
|
||||
"app/commerce-modules/auth/create-actor-type/page.mdx": "2024-12-25T13:26:27.176Z",
|
||||
"app/architectural-modules/page.mdx": "2025-01-08T12:13:50.333Z",
|
||||
"app/architectural-modules/page.mdx": "2025-03-12T10:28:44.544Z",
|
||||
"app/architectural-modules/workflow-engine/redis/page.mdx": "2024-10-15T12:50:59.507Z",
|
||||
"app/architectural-modules/notification/sendgrid/page.mdx": "2025-02-26T11:43:13.143Z",
|
||||
"app/commerce-modules/api-key/concepts/page.mdx": "2024-10-07T13:59:37.529Z",
|
||||
"app/architectural-modules/workflow-engine/page.mdx": "2025-01-08T12:16:04.157Z",
|
||||
"app/architectural-modules/workflow-engine/page.mdx": "2025-03-12T11:55:18.789Z",
|
||||
"app/_events-reference/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||
"app/architectural-modules/cache/page.mdx": "2025-01-08T12:14:37.925Z",
|
||||
"app/architectural-modules/cache/page.mdx": "2025-03-12T11:52:56.014Z",
|
||||
"app/architectural-modules/file/s3/page.mdx": "2025-01-24T13:49:01.104Z",
|
||||
"app/commerce-modules/api-key/_events/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||
"app/commerce-modules/api-key/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00",
|
||||
"app/architectural-modules/cache/redis/page.mdx": "2024-10-15T12:50:01.730Z",
|
||||
"app/architectural-modules/event/redis/page.mdx": "2024-10-15T12:50:40.506Z",
|
||||
"app/architectural-modules/event/local/page.mdx": "2024-10-15T12:50:37.512Z",
|
||||
"app/architectural-modules/event/redis/page.mdx": "2025-03-12T11:56:15.922Z",
|
||||
"app/architectural-modules/event/local/page.mdx": "2025-03-12T11:56:02.160Z",
|
||||
"app/architectural-modules/workflow-engine/in-memory/page.mdx": "2024-10-15T12:50:57.249Z",
|
||||
"app/architectural-modules/cache/in-memory/page.mdx": "2024-10-15T12:49:57.608Z",
|
||||
"app/architectural-modules/notification/local/page.mdx": "2024-10-15T12:51:21.284Z",
|
||||
"app/architectural-modules/file/local/page.mdx": "2024-10-16T15:48:42.839Z",
|
||||
"app/architectural-modules/notification/send-notification/page.mdx": "2024-09-30T08:43:53.151Z",
|
||||
"app/architectural-modules/file/page.mdx": "2025-01-08T12:15:19.981Z",
|
||||
"app/architectural-modules/event/page.mdx": "2025-01-08T12:15:08.454Z",
|
||||
"app/architectural-modules/file/page.mdx": "2025-03-12T10:46:14.005Z",
|
||||
"app/architectural-modules/event/page.mdx": "2025-03-12T11:52:51.218Z",
|
||||
"app/architectural-modules/cache/create/page.mdx": "2024-10-16T08:51:35.074Z",
|
||||
"app/admin-widget-injection-zones/page.mdx": "2024-12-24T08:48:36.154Z",
|
||||
"app/architectural-modules/notification/page.mdx": "2025-01-08T12:15:40.159Z",
|
||||
"app/architectural-modules/notification/page.mdx": "2025-03-12T11:49:47.552Z",
|
||||
"app/architectural-modules/event/create/page.mdx": "2024-12-09T14:46:40.248Z",
|
||||
"references/core_flows/Order/functions/core_flows.Order.orderEditUpdateItemQuantityValidationStep/page.mdx": "2024-08-20T00:10:58.913Z",
|
||||
"references/core_flows/Order/functions/core_flows.Order.orderEditUpdateItemQuantityWorkflow/page.mdx": "2024-08-20T00:10:58.949Z",
|
||||
@@ -553,7 +553,7 @@ export const generatedEditDates = {
|
||||
"references/types/NotificationTypes/interfaces/types.NotificationTypes.Attachment/page.mdx": "2024-12-17T16:57:21.128Z",
|
||||
"references/modules/fulfillment_models/page.mdx": "2024-12-23T12:30:31.974Z",
|
||||
"references/modules/order_models/page.mdx": "2025-01-27T11:43:58.772Z",
|
||||
"references/notification/classes/notification.AbstractNotificationProviderService/page.mdx": "2025-02-11T11:36:55.004Z",
|
||||
"references/notification/classes/notification.AbstractNotificationProviderService/page.mdx": "2025-03-12T12:28:32.206Z",
|
||||
"references/order/interfaces/order.IOrderModuleService/page.mdx": "2025-03-04T13:33:53.711Z",
|
||||
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminCampaignResponse/page.mdx": "2024-12-09T13:21:33.353Z",
|
||||
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminProductCategory/page.mdx": "2025-02-11T11:36:49.726Z",
|
||||
@@ -845,7 +845,7 @@ export const generatedEditDates = {
|
||||
"references/auth/IAuthModuleService/methods/auth.IAuthModuleService.authenticate/page.mdx": "2025-01-07T12:54:18.941Z",
|
||||
"references/auth/IAuthModuleService/methods/auth.IAuthModuleService.validateCallback/page.mdx": "2025-01-07T12:54:18.948Z",
|
||||
"references/auth/interfaces/auth.AuthenticationResponse/page.mdx": "2024-12-09T13:21:36.233Z",
|
||||
"references/auth_provider/classes/auth_provider.AbstractAuthModuleProvider/page.mdx": "2025-02-07T10:35:24.400Z",
|
||||
"references/auth_provider/classes/auth_provider.AbstractAuthModuleProvider/page.mdx": "2025-03-12T12:28:32.072Z",
|
||||
"references/core_flows/Invite/Workflows_Invite/functions/core_flows.Invite.Workflows_Invite.refreshInviteTokensWorkflow/page.mdx": "2025-03-04T13:33:41.373Z",
|
||||
"references/types/CommonTypes/types/types.CommonTypes.BatchMethodResponse/page.mdx": "2024-12-09T13:21:32.849Z",
|
||||
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminAddReturnShipping/page.mdx": "2024-12-09T13:21:34.545Z",
|
||||
@@ -1161,7 +1161,7 @@ export const generatedEditDates = {
|
||||
"references/dml/entity/classes/dml.entity.DmlEntity/page.mdx": "2025-01-13T18:05:57.749Z",
|
||||
"references/dml/entity_builder/EntityBuilder/methods/dml.entity_builder.EntityBuilder.define/page.mdx": "2025-01-13T18:05:57.746Z",
|
||||
"references/dml/entity_builder/types/dml.entity_builder.ManyToManyOptions/page.mdx": "2025-01-13T18:05:57.744Z",
|
||||
"references/file/classes/file.AbstractFileProviderService/page.mdx": "2025-02-24T10:48:42.996Z",
|
||||
"references/file/classes/file.AbstractFileProviderService/page.mdx": "2025-03-12T12:28:32.076Z",
|
||||
"references/fulfillment/IFulfillmentModuleService/methods/fulfillment.IFulfillmentModuleService.createFulfillmentSets/page.mdx": "2024-12-09T13:21:55.940Z",
|
||||
"references/fulfillment/IFulfillmentModuleService/methods/fulfillment.IFulfillmentModuleService.createGeoZones/page.mdx": "2025-01-13T18:05:53.686Z",
|
||||
"references/fulfillment/IFulfillmentModuleService/methods/fulfillment.IFulfillmentModuleService.createServiceZones/page.mdx": "2024-12-09T13:21:55.976Z",
|
||||
@@ -1223,7 +1223,7 @@ export const generatedEditDates = {
|
||||
"references/fulfillment/interfaces/fulfillment.OrderLineItemDTO/page.mdx": "2024-12-23T13:57:04.802Z",
|
||||
"references/fulfillment/interfaces/fulfillment.OrderLineItemTaxLineDTO/page.mdx": "2024-12-23T13:57:04.793Z",
|
||||
"references/fulfillment/types/fulfillment.JoinerRelationship/page.mdx": "2024-12-09T13:21:56.268Z",
|
||||
"references/fulfillment_provider/classes/fulfillment_provider.AbstractFulfillmentProviderService/page.mdx": "2025-02-24T10:48:43.021Z",
|
||||
"references/fulfillment_provider/classes/fulfillment_provider.AbstractFulfillmentProviderService/page.mdx": "2025-03-12T12:28:32.087Z",
|
||||
"references/inventory_next/IInventoryService/methods/inventory_next.IInventoryService.adjustInventory/page.mdx": "2024-12-09T13:21:56.800Z",
|
||||
"references/inventory_next/IInventoryService/methods/inventory_next.IInventoryService.confirmInventory/page.mdx": "2024-12-09T13:21:56.804Z",
|
||||
"references/inventory_next/IInventoryService/methods/inventory_next.IInventoryService.createInventoryItems/page.mdx": "2024-12-09T13:21:56.744Z",
|
||||
@@ -1363,7 +1363,7 @@ export const generatedEditDates = {
|
||||
"references/payment/interfaces/payment.JoinerServiceConfig/page.mdx": "2025-01-13T18:05:56.394Z",
|
||||
"references/payment/interfaces/payment.JoinerServiceConfigAlias/page.mdx": "2024-12-09T13:22:02.308Z",
|
||||
"references/payment/types/payment.JoinerRelationship/page.mdx": "2024-12-09T13:22:02.304Z",
|
||||
"references/payment_provider/classes/payment_provider.AbstractPaymentProvider/page.mdx": "2025-03-04T13:33:56.703Z",
|
||||
"references/payment_provider/classes/payment_provider.AbstractPaymentProvider/page.mdx": "2025-03-12T12:28:32.214Z",
|
||||
"references/pricing/IPricingModuleService/methods/pricing.IPricingModuleService.addPriceListPrices/page.mdx": "2024-12-09T13:22:02.928Z",
|
||||
"references/pricing/IPricingModuleService/methods/pricing.IPricingModuleService.addPrices/page.mdx": "2024-12-09T13:22:02.868Z",
|
||||
"references/pricing/IPricingModuleService/methods/pricing.IPricingModuleService.calculatePrices/page.mdx": "2024-12-09T13:22:02.832Z",
|
||||
@@ -1590,7 +1590,7 @@ export const generatedEditDates = {
|
||||
"references/tax/interfaces/tax.TaxRateRuleDTO/page.mdx": "2024-12-10T14:55:13.509Z",
|
||||
"references/tax/interfaces/tax.UpdateTaxRateDTO/page.mdx": "2024-12-10T14:55:13.523Z",
|
||||
"references/tax/types/tax.JoinerRelationship/page.mdx": "2024-12-09T13:22:04.496Z",
|
||||
"references/tax_provider/interfaces/tax_provider.ITaxProvider/page.mdx": "2024-12-09T13:22:04.468Z",
|
||||
"references/tax_provider/interfaces/tax_provider.ITaxProvider/page.mdx": "2025-03-12T12:28:31.902Z",
|
||||
"references/tax_provider/types/tax_provider.ItemTaxCalculationLine/page.mdx": "2024-12-09T13:22:04.464Z",
|
||||
"references/tax_provider/types/tax_provider.ShippingTaxCalculationLine/page.mdx": "2024-12-09T13:22:04.460Z",
|
||||
"references/types/DAL/interfaces/types.DAL.RepositoryService/page.mdx": "2025-03-04T13:33:50.003Z",
|
||||
@@ -6043,5 +6043,11 @@ export const generatedEditDates = {
|
||||
"references/types/interfaces/types.ITaxProvider/page.mdx": "2025-03-04T13:33:52.645Z",
|
||||
"app/how-to-tutorials/page.mdx": "2025-03-10T09:33:49.208Z",
|
||||
"app/tools/page.mdx": "2025-03-10T12:38:56.520Z",
|
||||
"app/references-overview/page.mdx": "2025-03-10T12:55:49.803Z"
|
||||
"app/references-overview/page.mdx": "2025-03-10T12:55:49.803Z",
|
||||
"app/architectural-modules/locking/page.mdx": "2025-03-12T11:40:25.695Z",
|
||||
"app/architectural-modules/locking/postgres/page.mdx": "2025-03-12T10:07:48.820Z",
|
||||
"app/architectural-modules/locking/redis/page.mdx": "2025-03-12T10:07:43.867Z",
|
||||
"references/locking/interfaces/locking.ILockingModule/page.mdx": "2025-03-12T12:28:30.425Z",
|
||||
"references/locking/interfaces/locking.ILockingProvider/page.mdx": "2025-03-12T12:28:30.422Z",
|
||||
"references/modules/locking/page.mdx": "2025-03-12T12:28:30.419Z"
|
||||
}
|
||||
@@ -91,6 +91,18 @@ export const filesMap = [
|
||||
"filePath": "/www/apps/resources/app/architectural-modules/file/s3/page.mdx",
|
||||
"pathname": "/architectural-modules/file/s3"
|
||||
},
|
||||
{
|
||||
"filePath": "/www/apps/resources/app/architectural-modules/locking/page.mdx",
|
||||
"pathname": "/architectural-modules/locking"
|
||||
},
|
||||
{
|
||||
"filePath": "/www/apps/resources/app/architectural-modules/locking/postgres/page.mdx",
|
||||
"pathname": "/architectural-modules/locking/postgres"
|
||||
},
|
||||
{
|
||||
"filePath": "/www/apps/resources/app/architectural-modules/locking/redis/page.mdx",
|
||||
"pathname": "/architectural-modules/locking/redis"
|
||||
},
|
||||
{
|
||||
"filePath": "/www/apps/resources/app/architectural-modules/notification/local/page.mdx",
|
||||
"pathname": "/architectural-modules/notification/local"
|
||||
@@ -12799,6 +12811,14 @@ export const filesMap = [
|
||||
"filePath": "/www/apps/resources/references/js_sdk/store/classes/js_sdk.store.Store/page.mdx",
|
||||
"pathname": "/references/js_sdk/store/classes/js_sdk.store.Store"
|
||||
},
|
||||
{
|
||||
"filePath": "/www/apps/resources/references/locking/interfaces/locking.ILockingModule/page.mdx",
|
||||
"pathname": "/references/locking/interfaces/locking.ILockingModule"
|
||||
},
|
||||
{
|
||||
"filePath": "/www/apps/resources/references/locking/interfaces/locking.ILockingProvider/page.mdx",
|
||||
"pathname": "/references/locking/interfaces/locking.ILockingProvider"
|
||||
},
|
||||
{
|
||||
"filePath": "/www/apps/resources/references/medusa/classes/medusa.RestrictedFields/page.mdx",
|
||||
"pathname": "/references/medusa/classes/medusa.RestrictedFields"
|
||||
@@ -13011,6 +13031,10 @@ export const filesMap = [
|
||||
"filePath": "/www/apps/resources/references/modules/js_sdk/page.mdx",
|
||||
"pathname": "/references/modules/js_sdk"
|
||||
},
|
||||
{
|
||||
"filePath": "/www/apps/resources/references/modules/locking/page.mdx",
|
||||
"pathname": "/references/modules/locking"
|
||||
},
|
||||
{
|
||||
"filePath": "/www/apps/resources/references/modules/medusa/page.mdx",
|
||||
"pathname": "/references/modules/medusa"
|
||||
|
||||
@@ -17,7 +17,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "category",
|
||||
"title": "Cache Modules",
|
||||
"title": "Cache Module",
|
||||
"initialOpen": true,
|
||||
"children": [
|
||||
{
|
||||
@@ -31,18 +31,26 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/cache/in-memory",
|
||||
"title": "In-Memory",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/cache/redis",
|
||||
"title": "Redis",
|
||||
"children": []
|
||||
"type": "sub-category",
|
||||
"title": "Modules",
|
||||
"children": [
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/cache/in-memory",
|
||||
"title": "In-Memory",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/cache/redis",
|
||||
"title": "Redis",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
@@ -66,7 +74,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "category",
|
||||
"title": "Event Modules",
|
||||
"title": "Event Module",
|
||||
"initialOpen": true,
|
||||
"children": [
|
||||
{
|
||||
@@ -80,18 +88,26 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/event/local",
|
||||
"title": "Local",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/event/redis",
|
||||
"title": "Redis",
|
||||
"children": []
|
||||
"type": "sub-category",
|
||||
"title": "Modules",
|
||||
"children": [
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/event/local",
|
||||
"title": "Local",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/event/redis",
|
||||
"title": "Redis",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
@@ -115,7 +131,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "category",
|
||||
"title": "File Module Providers",
|
||||
"title": "File Module",
|
||||
"initialOpen": true,
|
||||
"children": [
|
||||
{
|
||||
@@ -129,18 +145,26 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/file/local",
|
||||
"title": "Local",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/file/s3",
|
||||
"title": "AWS S3 (and Compatible APIs)",
|
||||
"children": []
|
||||
"type": "sub-category",
|
||||
"title": "Providers",
|
||||
"children": [
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/file/local",
|
||||
"title": "Local",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/file/s3",
|
||||
"title": "AWS S3 (and Compatible APIs)",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
@@ -164,7 +188,72 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "category",
|
||||
"title": "Notification Module Providers",
|
||||
"title": "Locking Module",
|
||||
"initialOpen": true,
|
||||
"children": [
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/locking",
|
||||
"title": "Overview",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "sub-category",
|
||||
"title": "Providers",
|
||||
"children": [
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/locking/redis",
|
||||
"title": "Redis",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/locking/postgres",
|
||||
"title": "PostgreSQL",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "sub-category",
|
||||
"title": "Guides",
|
||||
"children": [
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/references/locking-module-provider",
|
||||
"title": "Create Locking Provider",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/references/locking-service",
|
||||
"title": "Use Locking Module",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "category",
|
||||
"title": "Notification Module",
|
||||
"initialOpen": true,
|
||||
"children": [
|
||||
{
|
||||
@@ -178,18 +267,26 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/notification/local",
|
||||
"title": "Local",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/notification/sendgrid",
|
||||
"title": "SendGrid",
|
||||
"children": []
|
||||
"type": "sub-category",
|
||||
"title": "Providers",
|
||||
"children": [
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/notification/local",
|
||||
"title": "Local",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/notification/sendgrid",
|
||||
"title": "SendGrid",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
@@ -208,7 +305,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"type": "ref",
|
||||
"path": "/integrations/guides/resend",
|
||||
"title": "Integrate Resend",
|
||||
"children": []
|
||||
@@ -229,7 +326,7 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "category",
|
||||
"title": "Workflow Engine Modules",
|
||||
"title": "Workflow Engine Module",
|
||||
"initialOpen": true,
|
||||
"children": [
|
||||
{
|
||||
@@ -243,18 +340,26 @@ const generatedgeneratedArchitecturalModulesSidebarSidebar = {
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/workflow-engine/in-memory",
|
||||
"title": "In-Memory",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/workflow-engine/redis",
|
||||
"title": "Redis",
|
||||
"children": []
|
||||
"type": "sub-category",
|
||||
"title": "Modules",
|
||||
"children": [
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/workflow-engine/in-memory",
|
||||
"title": "In-Memory",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "link",
|
||||
"path": "/architectural-modules/workflow-engine/redis",
|
||||
"title": "Redis",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -78,6 +78,14 @@ const generatedgeneratedHowToTutorialsSidebarSidebar = {
|
||||
"path": "https://docs.medusajs.com/resources/references/fulfillment/provider",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "ref",
|
||||
"title": "Create Locking Provider",
|
||||
"path": "https://docs.medusajs.com/resources/references/locking-module-provider",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
@@ -133,6 +141,14 @@ const generatedgeneratedHowToTutorialsSidebarSidebar = {
|
||||
"title": "Send Notification",
|
||||
"path": "https://docs.medusajs.com/resources/architectural-modules/notification/send-notification",
|
||||
"children": []
|
||||
},
|
||||
{
|
||||
"loaded": true,
|
||||
"isPathHref": true,
|
||||
"type": "ref",
|
||||
"title": "Use Locking Module",
|
||||
"path": "https://docs.medusajs.com/resources/references/locking-service",
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -4704,6 +4704,16 @@ export const slugChanges = [
|
||||
"newSlug": "/references/js-sdk/store",
|
||||
"filePath": "/www/apps/resources/references/js_sdk/store/classes/js_sdk.store.Store/page.mdx"
|
||||
},
|
||||
{
|
||||
"origSlug": "/references/locking/interfaces/locking.ILockingModule",
|
||||
"newSlug": "/references/locking-service",
|
||||
"filePath": "/www/apps/resources/references/locking/interfaces/locking.ILockingModule/page.mdx"
|
||||
},
|
||||
{
|
||||
"origSlug": "/references/locking/interfaces/locking.ILockingProvider",
|
||||
"newSlug": "/references/locking-module-provider",
|
||||
"filePath": "/www/apps/resources/references/locking/interfaces/locking.ILockingProvider/page.mdx"
|
||||
},
|
||||
{
|
||||
"origSlug": "/references/medusa_config/interfaces/medusa_config.ConfigModule",
|
||||
"newSlug": "/references/medusa-config",
|
||||
|
||||
@@ -25,6 +25,7 @@ import { TypeList } from "docs-ui"
|
||||
- [inventory-next](modules/inventory_next/page.mdx)
|
||||
- [inventory-next-models](modules/inventory_next_models/page.mdx)
|
||||
- [js-sdk](modules/js_sdk/page.mdx)
|
||||
- [locking](modules/locking/page.mdx)
|
||||
- [medusa](modules/medusa/page.mdx)
|
||||
- [medusa-config](modules/medusa_config/page.mdx)
|
||||
- [modules-sdk](modules/modules_sdk/page.mdx)
|
||||
|
||||
+18
-10
@@ -9,9 +9,17 @@ sidebar_label: Create Auth Provider
|
||||
|
||||
import { TypeList } from "docs-ui"
|
||||
|
||||
# How to Create an Auth Provider Module
|
||||
# How to Create an Auth Module Provider
|
||||
|
||||
In this document, you’ll learn how to create an auth provider module and the methods you must implement in its main service.
|
||||
In this document, you’ll learn how to create an Auth Module Provider and the methods you must implement in its main service.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Example
|
||||
|
||||
As you implement your Auth Module Provider, it can be useful to refer to an existing provider and how it's implemeted.
|
||||
|
||||
If you need to refer to an existing implementation as an example, check the [Google Auth Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/auth-google).
|
||||
|
||||
---
|
||||
|
||||
@@ -31,9 +39,9 @@ The rest of this guide always uses the `src/modules/my-auth` directory as an exa
|
||||
|
||||
---
|
||||
|
||||
## 2. Create the Auth Provider Service
|
||||
## 2. Create the Auth Module Provider's Service
|
||||
|
||||
Create the file `src/modules/my-auth/service.ts` that holds the module's main service. It must extend the `AbstractAuthModuleProvider` class imported from `@medusajs/framework/utils`:
|
||||
Create the file `src/modules/my-auth/service.ts` that holds the module provider's main service. It must extend the `AbstractAuthModuleProvider` class imported from `@medusajs/framework/utils`:
|
||||
|
||||
```ts title="src/modules/my-auth/service.ts"
|
||||
import { AbstractAuthModuleProvider } from "@medusajs/framework/utils"
|
||||
@@ -453,7 +461,7 @@ class MyAuthProviderService extends AbstractAuthModuleProvider {
|
||||
|
||||
---
|
||||
|
||||
## 3. Create Module Definition File
|
||||
## 3. Create Module Provider Definition File
|
||||
|
||||
Create the file `src/modules/my-auth/index.ts` with the following content:
|
||||
|
||||
@@ -469,11 +477,11 @@ export default ModuleProvider(Modules.AUTH, {
|
||||
})
|
||||
```
|
||||
|
||||
This exports the module's definition, indicating that the `MyAuthProviderService` is the module's service.
|
||||
This exports the module provider's definition, indicating that the `MyAuthProviderService` is the module provider's service.
|
||||
|
||||
---
|
||||
|
||||
## 4. Use Module
|
||||
## 4. Use Module Provider
|
||||
|
||||
To use your Auth Module Provider, add it to the `providers` array of the Auth Module in `medusa-config.ts`:
|
||||
|
||||
@@ -513,9 +521,9 @@ module.exports = defineConfig({
|
||||
|
||||
## 5. Test it Out
|
||||
|
||||
To test out your authentication provider, use any of the [Authentication Routes](https://docs.medusajs.com/v2/resources/commerce-modules/auth/authentication-route), using your provider's ID as a path parameter.
|
||||
To test out your Authentication Module Provider, use any of the [Authentication Routes](https://docs.medusajs.com/v2/resources/commerce-modules/auth/authentication-route), using your provider's ID as a path parameter.
|
||||
|
||||
For example, to get a registration token for an admin user, send a `POST` request to `/auth/user/my-auth/register` replacing `my-auth` with your authentication provider's ID:
|
||||
For example, to get a registration token for an admin user, send a `POST` request to `/auth/user/my-auth/register` replacing `my-auth` with your Authentication Module Provider's ID:
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:9000/auth/user/my-auth/register
|
||||
@@ -525,6 +533,6 @@ curl -X POST http://localhost:9000/auth/user/my-auth/register
|
||||
}'
|
||||
```
|
||||
|
||||
Change the request body to pass the data required for your authentication provider to register the user.
|
||||
Change the request body to pass the data required for your Authentication Module Provider to register the user.
|
||||
|
||||
If registration is successful, the response will have a `token` property.
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = createCartsStep({
|
||||
"currency_code": "jpy"
|
||||
"currency_code": "php"
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = retrieveCartStep({
|
||||
"id": "id_NC6OB61K97Y"
|
||||
"id": "id_8UxqXFwvGKp1K"
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = updateCartPromotionsStep({
|
||||
"id": "id_DffiOIkGMKk1ORZHgv"
|
||||
"id": "id_QjunYRvNPLqC"
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
+4
-4
@@ -34,7 +34,7 @@ export async function POST(
|
||||
.run({
|
||||
input: {
|
||||
"id": [
|
||||
"id_dCWncxGdqNJe1Xa8"
|
||||
"id_bemxq4oJiSTZn"
|
||||
]
|
||||
}
|
||||
})
|
||||
@@ -61,7 +61,7 @@ export default async function handleOrderPlaced({
|
||||
.run({
|
||||
input: {
|
||||
"id": [
|
||||
"id_dCWncxGdqNJe1Xa8"
|
||||
"id_bemxq4oJiSTZn"
|
||||
]
|
||||
}
|
||||
})
|
||||
@@ -88,7 +88,7 @@ export default async function myCustomJob(
|
||||
.run({
|
||||
input: {
|
||||
"id": [
|
||||
"id_dCWncxGdqNJe1Xa8"
|
||||
"id_bemxq4oJiSTZn"
|
||||
]
|
||||
}
|
||||
})
|
||||
@@ -116,7 +116,7 @@ const myWorkflow = createWorkflow(
|
||||
.runAsStep({
|
||||
input: {
|
||||
"id": [
|
||||
"id_dCWncxGdqNJe1Xa8"
|
||||
"id_bemxq4oJiSTZn"
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = createCustomerGroupsStep({
|
||||
"name": "Oswald"
|
||||
"name": "Reuben"
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = createFulfillmentSets({
|
||||
"name": "Otha",
|
||||
"name": "Princess",
|
||||
"type": "{value}"
|
||||
})
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = createServiceZonesStep({
|
||||
"name": "Braulio",
|
||||
"name": "Chesley",
|
||||
"fulfillment_set_id": "{value}"
|
||||
})
|
||||
}
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ const myWorkflow = createWorkflow(
|
||||
const data = createShippingOptionRulesStep({
|
||||
"data": [{
|
||||
"attribute": "{value}",
|
||||
"operator": "eq",
|
||||
"operator": "lte",
|
||||
"value": "{value}",
|
||||
"shipping_option_id": "{value}"
|
||||
}]
|
||||
|
||||
+3
-3
@@ -22,10 +22,10 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = createShippingOptionsPriceSetsStep({
|
||||
"id": "id_W67Buk6sFY",
|
||||
"id": "id_HIsRdDyns3y1mST4",
|
||||
"prices": [{
|
||||
"currency_code": "bsd",
|
||||
"amount": 50
|
||||
"currency_code": "mur",
|
||||
"amount": 49
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = createShippingProfilesStep({
|
||||
"name": "Terry",
|
||||
"name": "Roslyn",
|
||||
"type": "{value}"
|
||||
})
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ const myWorkflow = createWorkflow(
|
||||
() => {
|
||||
const data = deleteShippingOptionRulesStep({
|
||||
"ids": [
|
||||
"id_nM64LyjlsAZ"
|
||||
"id_w0STpU4jGRJcMAB"
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = updateInventoryItemsStep({
|
||||
"id": "id_mQEVzVbUaYJLgjQJY"
|
||||
"id": "id_vM0T4AzOH45pTg"
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
+2
-2
@@ -20,8 +20,8 @@ const myWorkflow = createWorkflow(
|
||||
() => {
|
||||
const data = validateInventoryDeleteStep({
|
||||
"inventory_items": [{
|
||||
"id": "id_rGGlYBgTmX",
|
||||
"reserved_quantity": 32
|
||||
"id": "id_rwtTvcCy9pEA1AzENO8",
|
||||
"reserved_quantity": 13
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
+2
-2
@@ -23,8 +23,8 @@ const myWorkflow = createWorkflow(
|
||||
() => {
|
||||
const data = addOrderTransactionStep({
|
||||
"order_id": "order_123",
|
||||
"amount": 48,
|
||||
"currency_code": "idr"
|
||||
"amount": 11,
|
||||
"currency_code": "szl"
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = cancelOrderChangeStep({
|
||||
"id": "id_DaKY1rN96N4Dye1i4UZ"
|
||||
"id": "id_ZFBssHt8s8B1wyV"
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ const myWorkflow = createWorkflow(
|
||||
() => {
|
||||
const data = createOrderClaimsStep([{
|
||||
"order_id": "order_123",
|
||||
"type": "refund"
|
||||
"type": "replace"
|
||||
}])
|
||||
}
|
||||
)
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = declineOrderChangeStep({
|
||||
"id": "id_Ou7TQ3XETnVPfiyeBxn"
|
||||
"id": "id_2wLDxjDAU1BmB7Z"
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ const myWorkflow = createWorkflow(
|
||||
() => {
|
||||
const data = deleteClaimsStep({
|
||||
"ids": [
|
||||
"id_5kuTB79Xd8e14Y9BW6MH"
|
||||
"id_8ubWMJmV8tFQACwSBRwB"
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ const myWorkflow = createWorkflow(
|
||||
() => {
|
||||
const data = deleteExchangesStep({
|
||||
"ids": [
|
||||
"id_ocY7t3llf5aL1"
|
||||
"id_IS3vD87dhABzz"
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ const myWorkflow = createWorkflow(
|
||||
() => {
|
||||
const data = deleteOrderChangeActionsStep({
|
||||
"ids": [
|
||||
"id_DoNSMAC7x7yoiK67f3m"
|
||||
"id_VTfP6NhpWdAnGb9b"
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ const myWorkflow = createWorkflow(
|
||||
() => {
|
||||
const data = deleteOrderChangesStep({
|
||||
"ids": [
|
||||
"id_s5kEPzEoBHwZamIZf"
|
||||
"id_aL7s50hQv4X"
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ const myWorkflow = createWorkflow(
|
||||
() => {
|
||||
const data = deleteOrderLineItems({
|
||||
"ids": [
|
||||
"id_qCoJvWGyVfQq5TC4LX"
|
||||
"id_WQTEJb19KEWjtGsjr8MQ"
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ const myWorkflow = createWorkflow(
|
||||
() => {
|
||||
const data = deleteOrderShippingMethods({
|
||||
"ids": [
|
||||
"id_R9Ui7kOsEp"
|
||||
"id_kB1A5qCI99"
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ const myWorkflow = createWorkflow(
|
||||
() => {
|
||||
const data = deleteReturnsStep({
|
||||
"ids": [
|
||||
"id_BTC6cHX3wCg6Ff"
|
||||
"id_G4woLBjcA55As"
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = updateOrderChangeActionsStep({
|
||||
"id": "id_YOUTShFPTOVb9hr"
|
||||
"id": "id_vYJUMkfGqAUG"
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = updateOrderChangesStep({
|
||||
"id": "id_2FI3REzv0cccw9"
|
||||
"id": "id_39fPc4du4SYpubPg2"
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = updateOrderShippingMethodsStep({
|
||||
"id": "id_xwOv7C5wTuE"
|
||||
"id": "id_YxuvxvhYAEfz"
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = updateReturnsStep({
|
||||
"id": "id_V3qdW375AIzZCxbK8FRK"
|
||||
"id": "id_y6VNwEVxTmUDauPsD2L"
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
+4
-4
@@ -39,7 +39,7 @@ export async function POST(
|
||||
const { result } = await cancelOrderChangeWorkflow(req.scope)
|
||||
.run({
|
||||
input: {
|
||||
"id": "id_TifTyReowGmg5yEQQLT"
|
||||
"id": "id_7cSs3D0glvQGX5o89r"
|
||||
}
|
||||
})
|
||||
|
||||
@@ -64,7 +64,7 @@ export default async function handleOrderPlaced({
|
||||
const { result } = await cancelOrderChangeWorkflow(container)
|
||||
.run({
|
||||
input: {
|
||||
"id": "id_TifTyReowGmg5yEQQLT"
|
||||
"id": "id_7cSs3D0glvQGX5o89r"
|
||||
}
|
||||
})
|
||||
|
||||
@@ -89,7 +89,7 @@ export default async function myCustomJob(
|
||||
const { result } = await cancelOrderChangeWorkflow(container)
|
||||
.run({
|
||||
input: {
|
||||
"id": "id_TifTyReowGmg5yEQQLT"
|
||||
"id": "id_7cSs3D0glvQGX5o89r"
|
||||
}
|
||||
})
|
||||
|
||||
@@ -115,7 +115,7 @@ const myWorkflow = createWorkflow(
|
||||
const result = cancelOrderChangeWorkflow
|
||||
.runAsStep({
|
||||
input: {
|
||||
"id": "id_TifTyReowGmg5yEQQLT"
|
||||
"id": "id_7cSs3D0glvQGX5o89r"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
+4
-4
@@ -39,7 +39,7 @@ export async function POST(
|
||||
const { result } = await createOrderChangeActionsWorkflow(req.scope)
|
||||
.run({
|
||||
input: [{
|
||||
"action": "UPDATE_ORDER_PROPERTIES"
|
||||
"action": "DELIVER_ITEM"
|
||||
}]
|
||||
})
|
||||
|
||||
@@ -64,7 +64,7 @@ export default async function handleOrderPlaced({
|
||||
const { result } = await createOrderChangeActionsWorkflow(container)
|
||||
.run({
|
||||
input: [{
|
||||
"action": "UPDATE_ORDER_PROPERTIES"
|
||||
"action": "DELIVER_ITEM"
|
||||
}]
|
||||
})
|
||||
|
||||
@@ -89,7 +89,7 @@ export default async function myCustomJob(
|
||||
const { result } = await createOrderChangeActionsWorkflow(container)
|
||||
.run({
|
||||
input: [{
|
||||
"action": "UPDATE_ORDER_PROPERTIES"
|
||||
"action": "DELIVER_ITEM"
|
||||
}]
|
||||
})
|
||||
|
||||
@@ -115,7 +115,7 @@ const myWorkflow = createWorkflow(
|
||||
const result = createOrderChangeActionsWorkflow
|
||||
.runAsStep({
|
||||
input: [{
|
||||
"action": "UPDATE_ORDER_PROPERTIES"
|
||||
"action": "DELIVER_ITEM"
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
+4
-4
@@ -39,7 +39,7 @@ export async function POST(
|
||||
const { result } = await declineOrderChangeWorkflow(req.scope)
|
||||
.run({
|
||||
input: {
|
||||
"id": "id_feqdUcXMDHpl5BhrF1"
|
||||
"id": "id_wjqnrdNeurl7p"
|
||||
}
|
||||
})
|
||||
|
||||
@@ -64,7 +64,7 @@ export default async function handleOrderPlaced({
|
||||
const { result } = await declineOrderChangeWorkflow(container)
|
||||
.run({
|
||||
input: {
|
||||
"id": "id_feqdUcXMDHpl5BhrF1"
|
||||
"id": "id_wjqnrdNeurl7p"
|
||||
}
|
||||
})
|
||||
|
||||
@@ -89,7 +89,7 @@ export default async function myCustomJob(
|
||||
const { result } = await declineOrderChangeWorkflow(container)
|
||||
.run({
|
||||
input: {
|
||||
"id": "id_feqdUcXMDHpl5BhrF1"
|
||||
"id": "id_wjqnrdNeurl7p"
|
||||
}
|
||||
})
|
||||
|
||||
@@ -115,7 +115,7 @@ const myWorkflow = createWorkflow(
|
||||
const result = declineOrderChangeWorkflow
|
||||
.runAsStep({
|
||||
input: {
|
||||
"id": "id_feqdUcXMDHpl5BhrF1"
|
||||
"id": "id_wjqnrdNeurl7p"
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
+4
-4
@@ -40,7 +40,7 @@ export async function POST(
|
||||
.run({
|
||||
input: {
|
||||
"ids": [
|
||||
"id_ejESZlHpfen57ANk8MzQ"
|
||||
"id_LZttQo8M2Bg"
|
||||
]
|
||||
}
|
||||
})
|
||||
@@ -67,7 +67,7 @@ export default async function handleOrderPlaced({
|
||||
.run({
|
||||
input: {
|
||||
"ids": [
|
||||
"id_ejESZlHpfen57ANk8MzQ"
|
||||
"id_LZttQo8M2Bg"
|
||||
]
|
||||
}
|
||||
})
|
||||
@@ -94,7 +94,7 @@ export default async function myCustomJob(
|
||||
.run({
|
||||
input: {
|
||||
"ids": [
|
||||
"id_ejESZlHpfen57ANk8MzQ"
|
||||
"id_LZttQo8M2Bg"
|
||||
]
|
||||
}
|
||||
})
|
||||
@@ -122,7 +122,7 @@ const myWorkflow = createWorkflow(
|
||||
.runAsStep({
|
||||
input: {
|
||||
"ids": [
|
||||
"id_ejESZlHpfen57ANk8MzQ"
|
||||
"id_LZttQo8M2Bg"
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
+4
-4
@@ -40,7 +40,7 @@ export async function POST(
|
||||
.run({
|
||||
input: {
|
||||
"ids": [
|
||||
"id_cVndBSTOaDeZrGfM6"
|
||||
"id_VmvGq4qPHDULT"
|
||||
]
|
||||
}
|
||||
})
|
||||
@@ -67,7 +67,7 @@ export default async function handleOrderPlaced({
|
||||
.run({
|
||||
input: {
|
||||
"ids": [
|
||||
"id_cVndBSTOaDeZrGfM6"
|
||||
"id_VmvGq4qPHDULT"
|
||||
]
|
||||
}
|
||||
})
|
||||
@@ -94,7 +94,7 @@ export default async function myCustomJob(
|
||||
.run({
|
||||
input: {
|
||||
"ids": [
|
||||
"id_cVndBSTOaDeZrGfM6"
|
||||
"id_VmvGq4qPHDULT"
|
||||
]
|
||||
}
|
||||
})
|
||||
@@ -122,7 +122,7 @@ const myWorkflow = createWorkflow(
|
||||
.runAsStep({
|
||||
input: {
|
||||
"ids": [
|
||||
"id_cVndBSTOaDeZrGfM6"
|
||||
"id_VmvGq4qPHDULT"
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
+6
-6
@@ -23,15 +23,15 @@ const myWorkflow = createWorkflow(
|
||||
() => {
|
||||
const data = throwUnlessStatusIsNotPaid({
|
||||
"paymentCollection": {
|
||||
"id": "id_eAJuwhOu8bg3",
|
||||
"currency_code": "bbd",
|
||||
"id": "id_vGqsmproyL2X3fOeWS",
|
||||
"currency_code": "bhd",
|
||||
"amount": {
|
||||
"numeric": 20
|
||||
"numeric": 43
|
||||
},
|
||||
"status": "completed",
|
||||
"status": "not_paid",
|
||||
"payment_providers": [{
|
||||
"id": "id_z5AQyPaFFn0",
|
||||
"is_enabled": true
|
||||
"id": "id_LMlRiwKBdHvYo",
|
||||
"is_enabled": false
|
||||
}]
|
||||
}
|
||||
})
|
||||
|
||||
+4
-4
@@ -39,7 +39,7 @@ export async function POST(
|
||||
const { result } = await updateOrderChangeActionsWorkflow(req.scope)
|
||||
.run({
|
||||
input: [{
|
||||
"id": "id_Iy3nRPjiHKo9sWnOrk"
|
||||
"id": "id_NhKl53pnyDSMMKE2I"
|
||||
}]
|
||||
})
|
||||
|
||||
@@ -64,7 +64,7 @@ export default async function handleOrderPlaced({
|
||||
const { result } = await updateOrderChangeActionsWorkflow(container)
|
||||
.run({
|
||||
input: [{
|
||||
"id": "id_Iy3nRPjiHKo9sWnOrk"
|
||||
"id": "id_NhKl53pnyDSMMKE2I"
|
||||
}]
|
||||
})
|
||||
|
||||
@@ -89,7 +89,7 @@ export default async function myCustomJob(
|
||||
const { result } = await updateOrderChangeActionsWorkflow(container)
|
||||
.run({
|
||||
input: [{
|
||||
"id": "id_Iy3nRPjiHKo9sWnOrk"
|
||||
"id": "id_NhKl53pnyDSMMKE2I"
|
||||
}]
|
||||
})
|
||||
|
||||
@@ -115,7 +115,7 @@ const myWorkflow = createWorkflow(
|
||||
const result = updateOrderChangeActionsWorkflow
|
||||
.runAsStep({
|
||||
input: [{
|
||||
"id": "id_Iy3nRPjiHKo9sWnOrk"
|
||||
"id": "id_NhKl53pnyDSMMKE2I"
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
+4
-4
@@ -39,7 +39,7 @@ export async function POST(
|
||||
const { result } = await updateOrderChangesWorkflow(req.scope)
|
||||
.run({
|
||||
input: [{
|
||||
"id": "id_HCKR2lBPbpaAtaU"
|
||||
"id": "id_5n831DKhYtSIYgCvx"
|
||||
}]
|
||||
})
|
||||
|
||||
@@ -64,7 +64,7 @@ export default async function handleOrderPlaced({
|
||||
const { result } = await updateOrderChangesWorkflow(container)
|
||||
.run({
|
||||
input: [{
|
||||
"id": "id_HCKR2lBPbpaAtaU"
|
||||
"id": "id_5n831DKhYtSIYgCvx"
|
||||
}]
|
||||
})
|
||||
|
||||
@@ -89,7 +89,7 @@ export default async function myCustomJob(
|
||||
const { result } = await updateOrderChangesWorkflow(container)
|
||||
.run({
|
||||
input: [{
|
||||
"id": "id_HCKR2lBPbpaAtaU"
|
||||
"id": "id_5n831DKhYtSIYgCvx"
|
||||
}]
|
||||
})
|
||||
|
||||
@@ -115,7 +115,7 @@ const myWorkflow = createWorkflow(
|
||||
const result = updateOrderChangesWorkflow
|
||||
.runAsStep({
|
||||
input: [{
|
||||
"id": "id_HCKR2lBPbpaAtaU"
|
||||
"id": "id_5n831DKhYtSIYgCvx"
|
||||
}]
|
||||
})
|
||||
}
|
||||
|
||||
+2
-2
@@ -24,8 +24,8 @@ const myWorkflow = createWorkflow(
|
||||
const data = createPaymentSessionStep({
|
||||
"payment_collection_id": "{value}",
|
||||
"provider_id": "{value}",
|
||||
"amount": 33,
|
||||
"currency_code": "bnd"
|
||||
"amount": 27,
|
||||
"currency_code": "rwf"
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ const myWorkflow = createWorkflow(
|
||||
() => {
|
||||
const data = deletePaymentSessionsStep({
|
||||
"ids": [
|
||||
"id_CCBxiUKjH8BnhzJDbGgD"
|
||||
"id_q21Y5traw5UZlU0"
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = updateRefundReasonsStep({
|
||||
"id": "id_88bbRFUmjXhbI8wsvZn7"
|
||||
"id": "id_ydhQzcRGB4f"
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = validatePriceListsStep({
|
||||
"id": "id_eT8yS6k97cLV0Xq42"
|
||||
"id": "id_r2rvymnVIzUW"
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = createCollectionsStep([{
|
||||
"title": "amplexus"
|
||||
"title": "vestrum"
|
||||
}])
|
||||
}
|
||||
)
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ const myWorkflow = createWorkflow(
|
||||
() => {
|
||||
const data = updatePromotionsValidationStep({
|
||||
"promotionsData": [{
|
||||
"id": "id_8RHhRkuHKZdQcFyvzazV",
|
||||
"id": "id_e4yTROodl3VoMJIda0",
|
||||
"status": "draft"
|
||||
}]
|
||||
})
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ const myWorkflow = createWorkflow(
|
||||
"my-workflow",
|
||||
() => {
|
||||
const data = createStockLocations({
|
||||
"name": "Eula"
|
||||
"name": "Maybell"
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
+16
-8
@@ -4,9 +4,17 @@ slug: /references/file-provider-module
|
||||
|
||||
import { TypeList } from "docs-ui"
|
||||
|
||||
# How to Create a File Provider Module
|
||||
# How to Create a File Module Provider
|
||||
|
||||
In this document, you’ll learn how to create a file provider module and the methods you must implement in its main service.
|
||||
In this document, you’ll learn how to create a File Module Provider and the methods you must implement in its main service.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Example
|
||||
|
||||
As you implement your File Module Provider, it can be useful to refer to an existing provider and how it's implemeted.
|
||||
|
||||
If you need to refer to an existing implementation as an example, check the [S3 File Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/file-s3).
|
||||
|
||||
---
|
||||
|
||||
@@ -26,9 +34,9 @@ The rest of this guide always uses the `src/modules/my-file` directory as an exa
|
||||
|
||||
---
|
||||
|
||||
## 2. Create the File Provider Service
|
||||
## 2. Create the File Module Provider's Service
|
||||
|
||||
Create the file `src/modules/my-file/service.ts` that holds the implementation of the module's main service. It must extend the `AbstractFileProviderService` class imported from `@medusajs/framework/utils`:
|
||||
Create the file `src/modules/my-file/service.ts` that holds the implementation of the module provider's main service. It must extend the `AbstractFileProviderService` class imported from `@medusajs/framework/utils`:
|
||||
|
||||
```ts title="src/modules/my-file/service.ts"
|
||||
import { AbstractFileProviderService } from "@medusajs/framework/utils"
|
||||
@@ -224,7 +232,7 @@ class MyFileProviderService extends AbstractFileProviderService {
|
||||
|
||||
---
|
||||
|
||||
## 3. Create Module Definition File
|
||||
## 3. Create Module Provider Definition File
|
||||
|
||||
Create the file `src/modules/my-file/index.ts` with the following content:
|
||||
|
||||
@@ -240,11 +248,11 @@ export default ModuleProvider(Modules.FILE, {
|
||||
})
|
||||
```
|
||||
|
||||
This exports the module's definition, indicating that the `MyFileProviderService` is the module's service.
|
||||
This exports the module provider's definition, indicating that the `MyFileProviderService` is the module provider's service.
|
||||
|
||||
---
|
||||
|
||||
## 4. Use Module
|
||||
## 4. Use Module Provider
|
||||
|
||||
To use your File Module Provider, add it to the `providers` array of the File Module in `medusa-config.ts`:
|
||||
|
||||
@@ -286,4 +294,4 @@ module.exports = defineConfig({
|
||||
|
||||
## 5. Test it Out
|
||||
|
||||
To test out your file provider, use the Medusa Admin or the [Upload API route](https://docs.medusajs.com/v2/api/admin#uploads_postuploads) to upload a file.
|
||||
To test out your File Module Provider, use the Medusa Admin or the [Upload API route](https://docs.medusajs.com/v2/api/admin#uploads_postuploads) to upload a file.
|
||||
|
||||
+12
-12
@@ -9,17 +9,17 @@ sidebar_label: Create Fulfillment Provider
|
||||
|
||||
import { TypeList } from "docs-ui"
|
||||
|
||||
# How to Create a Fulfillment Provider Module
|
||||
# How to Create a Fulfillment Module Provider
|
||||
|
||||
In this document, you’ll learn how to create a fulfillment provider module and the methods you must implement in its main service.
|
||||
In this document, you’ll learn how to create a Fulfillment Module Provider and the methods you must implement in its main service.
|
||||
|
||||
---
|
||||
|
||||
## Understanding Fulfillment Provider Implementation
|
||||
## Understanding Fulfillment Module Provider Implementation
|
||||
|
||||
The Fulfillment Module Provider handles processing fulfillments and shipments with a third-party provirder. However, it's not responsible for managing fulfillment concepts within Medusa, such as creating a fulfillment or its shipments. The Fulfillment Module uses your fulfillment provider within core operations.
|
||||
The Fulfillment Module Provider handles processing fulfillments and shipments with a third-party provirder. However, it's not responsible for managing fulfillment concepts within Medusa, such as creating a fulfillment or its shipments. The Fulfillment Module uses your Fulfillment Module Provider within core operations.
|
||||
|
||||
For example, when the merchant creates a fulfillment for an order, the Fulfillment Module uses your fulfillment provider to create the fulfillment in the third-party system, then creates the fulfillment in Medusa. So, you only have to implement the third-party fulfillment processing logic in your fulfillment provider.
|
||||
For example, when the merchant creates a fulfillment for an order, the Fulfillment Module uses your Fulfillment Module Provider to create the fulfillment in the third-party system, then creates the fulfillment in Medusa. So, you only have to implement the third-party fulfillment processing logic in your Fulfillment Module Provider.
|
||||
|
||||
---
|
||||
|
||||
@@ -39,9 +39,9 @@ The rest of this guide always uses the `src/modules/my-fulfillment` directory as
|
||||
|
||||
---
|
||||
|
||||
## 2. Create the Fulfillment Provider Service
|
||||
## 2. Create the Fulfillment Module Provider Service
|
||||
|
||||
Create the file `src/modules/my-fulfillment/service.ts` that holds the module's main service. It must extend the `AbstractFulfillmentProviderService` class imported from `@medusajs/framework/utils`:
|
||||
Create the file `src/modules/my-fulfillment/service.ts` that holds the module provider's main service. It must extend the `AbstractFulfillmentProviderService` class imported from `@medusajs/framework/utils`:
|
||||
|
||||
```ts title="src/modules/my-fulfillment/service.ts"
|
||||
import { AbstractFulfillmentProviderService } from "@medusajs/framework/utils"
|
||||
@@ -519,7 +519,7 @@ class MyFulfillmentProviderService extends AbstractFulfillmentProviderService {
|
||||
|
||||
---
|
||||
|
||||
## 3. Create Module Definition File
|
||||
## 3. Create Module Provider Definition File
|
||||
|
||||
Create the file `src/modules/my-fulfillment/index.ts` with the following content:
|
||||
|
||||
@@ -535,11 +535,11 @@ export default ModuleProvider(Modules.FULFILLMENT, {
|
||||
})
|
||||
```
|
||||
|
||||
This exports the module's definition, indicating that the `MyFulfillmentProviderService` is the module's service.
|
||||
This exports the module provider's definition, indicating that the `MyFulfillmentProviderService` is the module provider's service.
|
||||
|
||||
---
|
||||
|
||||
## 4. Use Module
|
||||
## 4. Use Module Provider
|
||||
|
||||
To use your Fulfillment Module Provider, add it to the `providers` array of the Fulfillment Module in `medusa-config.ts`:
|
||||
|
||||
@@ -575,9 +575,9 @@ module.exports = defineConfig({
|
||||
|
||||
## 5. Test it Out
|
||||
|
||||
Before you use your fulfillment provider, in the Medusa Admin:
|
||||
Before you use your Fulfillment Module Provider, in the Medusa Admin:
|
||||
|
||||
1. Add the fulfillment provider to a location.
|
||||
1. Add the Fulfillment Module Provider to a location.
|
||||
2. Add in the location a delivery shipping option that uses the provider.
|
||||
|
||||
Then, place an order, choosing the shipping option you created during checkout, and create a fulfillment in the Medusa Admin. The fulfillment is created using your provider.
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
---
|
||||
slug: /references/locking-service
|
||||
tags:
|
||||
- locking
|
||||
- server
|
||||
- how to
|
||||
sidebar_label: Use Locking Module
|
||||
---
|
||||
|
||||
import { TypeList } from "docs-ui"
|
||||
|
||||
# How to Use Locking Module
|
||||
|
||||
In this document, you’ll learn about the different methods in the Locking Module's service and how to use them.
|
||||
|
||||
---
|
||||
|
||||
## Resolve Locking Module's Service
|
||||
|
||||
In your workflow's step, you can resolve the Locking Module's service from the Medusa container:
|
||||
|
||||
```ts
|
||||
import { Modules } from "@medusajs/framework/utils"
|
||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
const step1 = createStep(
|
||||
"step-1",
|
||||
async ({}, { container }) => {
|
||||
const lockingModuleService = container.resolve(
|
||||
Modules.LOCKING
|
||||
)
|
||||
|
||||
// TODO use lockingModuleService
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
You can then use the Locking Module's service's methods in the step. The rest of this guide details these methods.
|
||||
|
||||
---
|
||||
|
||||
## execute
|
||||
|
||||
This method executes a giuven asynchronous job with a lock on the given keys. You can optionally pass a
|
||||
provider name to be used for locking. If no provider is passed, the default provider (in-memory or the
|
||||
provider configuerd in `medusa-config.ts`) will be used.
|
||||
|
||||
### Example
|
||||
|
||||
For example, to use the lock module when deleting a product:
|
||||
|
||||
```ts
|
||||
await lockingModuleService.execute("prod_123", async () => {
|
||||
// assuming you've resolved the product service from the container
|
||||
await productModuleService.delete("prod_123")
|
||||
})
|
||||
```
|
||||
|
||||
In the above example, the product of ID `prod_123` is locked while it's being deleted.
|
||||
|
||||
To specify the provider to use for locking, you can pass the provider name in the `args` argument:
|
||||
|
||||
```ts
|
||||
await lockingModuleService.execute("prod_123", async () => {
|
||||
// assuming you've resolved the product service from the container
|
||||
await productModuleService.delete("prod_123")
|
||||
}, {
|
||||
provider: "lp_my-lock"
|
||||
})
|
||||
```
|
||||
|
||||
### Type Parameters
|
||||
|
||||
<TypeList types={[{"name":"T","type":"`object`","description":"The type of the job's result.","optional":true,"defaultValue":"","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="execute"/>
|
||||
|
||||
### Parameters
|
||||
|
||||
<TypeList types={[{"name":"keys","type":"`string` \\| `string`[]","description":"The keys to lock durng the job's execution.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"job","type":"() => Promise<T>","description":"The asynchronous job to execute while the keys are locked.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"args","type":"`object`","description":"Additional arguments for the job execution.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"timeout","type":"`number`","description":"The timeout (in seconds) for acquiring the lock. If the time out is passed, the job is canceled and the lock is released.\nIts value defaults to `5` seconds if no value is passed or if you pass a value less than `1`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"provider","type":"`string`","description":"The provider name to use for locking. If no provider is passed, the default provider (in-memory or the provider configuerd in `medusa-config.ts`) will be used.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"sharedContext","type":"[Context](../../../types/interfaces/types.Context/page.mdx)","description":"A context used to share resources, such as transaction manager, between the application and the module.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"transactionManager","type":"TManager","description":"An instance of a transaction manager of type `TManager`, which is a typed parameter passed to the context to specify the type of the `transactionManager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"manager","type":"TManager","description":"An instance of a manager, typically an entity manager, of type `TManager`, which is a typed parameter passed to the context to specify the type of the `manager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationLevel","type":"`string`","description":"A string indicating the isolation level of the context. Possible values are `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, or `SERIALIZABLE`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"enableNestedTransactions","type":"`boolean`","description":"A boolean value indicating whether nested transactions are enabled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"eventGroupId","type":"`string`","description":"A string indicating the ID of the group to aggregate the events to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"transactionId","type":"`string`","description":"A string indicating the ID of the current transaction.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"messageAggregator","type":"[IMessageAggregator](../../../types/interfaces/types.IMessageAggregator/page.mdx)","description":"An instance of a message aggregator, which is used to aggregate messages to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"requestId","type":"`string`","description":"A string indicating the ID of the current request.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"idempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the current workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"parentStepIdempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the parent workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="execute"/>
|
||||
|
||||
### Returns
|
||||
|
||||
<TypeList types={[{"name":"Promise","type":"Promise<T>","optional":false,"defaultValue":"","description":"The result of the job execution.","expandable":false,"children":[{"name":"T","type":"T","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="execute"/>
|
||||
|
||||
___
|
||||
|
||||
## acquire
|
||||
|
||||
This method acquires a lock on the given keys. You can optionally pass a provider name to be used for locking.
|
||||
If no provider is passed, the default provider (in-memory or the provider configuerd in `medusa-config.ts`) will be used.
|
||||
|
||||
You can pass an owner for the lock, which limits who can extend or release the acquired lock. Then, if you use this method again
|
||||
passing the same owner, the lock's expiration time is extended with the value passed in the `expire` argument. Otherwise, if you pass a
|
||||
different owner, the method throws an error.
|
||||
|
||||
### Example
|
||||
|
||||
For example, to acquire a lock on a product with ID `prod_123` for a user with ID `user_123`:
|
||||
|
||||
```ts
|
||||
await lockingModuleService.acquire("prod_123", {
|
||||
ownerId: "user_123",
|
||||
expire: 60
|
||||
})
|
||||
```
|
||||
|
||||
In this example, you acquire a lock on the product with ID `prod_123` for the user with ID `user_123`. You extend the
|
||||
lock's expiration time by `60` seconds.
|
||||
|
||||
To specify the provider to use for locking, you can pass the provider name in the `args` argument:
|
||||
|
||||
```ts
|
||||
await lockingModuleService.acquire("prod_123", {
|
||||
ownerId: "user_123",
|
||||
expire: 60,
|
||||
provider: "lp_my-lock"
|
||||
})
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
<TypeList types={[{"name":"keys","type":"`string` \\| `string`[]","description":"The keys to acquire the lock on.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"args","type":"`object`","description":"Additional arguments for acquiring the lock.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"ownerId","type":"`null` \\| `string`","description":"The owner ID for the lock. If specified, only the owner can release the lock or extend its expiration time.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"expire","type":"`number`","description":"The expiration time (in seconds) for the lock. If the lock is already acquired and the owner is the same, the expiration time is extended\nby the value passed. If not specified, the lock does not expire.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"provider","type":"`string`","description":"The provider name to use for locking. If no provider is passed, the default provider (in-memory or the provider configuerd in `medusa-config.ts`) will be used.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"sharedContext","type":"[Context](../../../types/interfaces/types.Context/page.mdx)","description":"A context used to share resources, such as transaction manager, between the application and the module.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"transactionManager","type":"TManager","description":"An instance of a transaction manager of type `TManager`, which is a typed parameter passed to the context to specify the type of the `transactionManager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"manager","type":"TManager","description":"An instance of a manager, typically an entity manager, of type `TManager`, which is a typed parameter passed to the context to specify the type of the `manager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationLevel","type":"`string`","description":"A string indicating the isolation level of the context. Possible values are `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, or `SERIALIZABLE`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"enableNestedTransactions","type":"`boolean`","description":"A boolean value indicating whether nested transactions are enabled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"eventGroupId","type":"`string`","description":"A string indicating the ID of the group to aggregate the events to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"transactionId","type":"`string`","description":"A string indicating the ID of the current transaction.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"messageAggregator","type":"[IMessageAggregator](../../../types/interfaces/types.IMessageAggregator/page.mdx)","description":"An instance of a message aggregator, which is used to aggregate messages to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"requestId","type":"`string`","description":"A string indicating the ID of the current request.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"idempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the current workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"parentStepIdempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the parent workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="acquire"/>
|
||||
|
||||
### Returns
|
||||
|
||||
<TypeList types={[{"name":"Promise","type":"Promise<void>","optional":false,"defaultValue":"","description":"Resolves when the lock is acquired.","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="acquire"/>
|
||||
|
||||
___
|
||||
|
||||
## release
|
||||
|
||||
This method releases a lock on the given keys. You can optionally pass a provider name to be used for locking.
|
||||
If no provider is passed, the default provider (in-memory or the provider configuerd in `medusa-config.ts`) will be used.
|
||||
|
||||
If the lock has an owner, you must pass the same owner to release the lock.
|
||||
|
||||
### Example
|
||||
|
||||
For example, to release a lock on a product with ID `prod_123` for a user with ID `user_123`:
|
||||
|
||||
```ts
|
||||
await lockingModuleService.release("prod_123", {
|
||||
ownerId: "user_123"
|
||||
})
|
||||
```
|
||||
|
||||
In this example, you release the lock on the product with ID `prod_123` for the user with ID `user_123`.
|
||||
|
||||
To specify the provider to use for locking, you can pass the provider name in the `args` argument:
|
||||
|
||||
```ts
|
||||
await lockingModuleService.release("prod_123", {
|
||||
ownerId: "user_123",
|
||||
provider: "lp_my-lock"
|
||||
})
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
<TypeList types={[{"name":"keys","type":"`string` \\| `string`[]","description":"The keys to release the lock from.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"args","type":"`object`","description":"Additional arguments for releasing the lock.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"ownerId","type":"`null` \\| `string`","description":"The ID of the lock's owner. The lock can be released either if it doesn't have an owner, or \nif its owner ID matches the one passed in this property.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"provider","type":"`string`","description":"The provider name to use for locking. If no provider is passed, the default provider (in-memory or the provider configuerd in `medusa-config.ts`) will be used.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"sharedContext","type":"[Context](../../../types/interfaces/types.Context/page.mdx)","description":"A context used to share resources, such as transaction manager, between the application and the module.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"transactionManager","type":"TManager","description":"An instance of a transaction manager of type `TManager`, which is a typed parameter passed to the context to specify the type of the `transactionManager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"manager","type":"TManager","description":"An instance of a manager, typically an entity manager, of type `TManager`, which is a typed parameter passed to the context to specify the type of the `manager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationLevel","type":"`string`","description":"A string indicating the isolation level of the context. Possible values are `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, or `SERIALIZABLE`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"enableNestedTransactions","type":"`boolean`","description":"A boolean value indicating whether nested transactions are enabled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"eventGroupId","type":"`string`","description":"A string indicating the ID of the group to aggregate the events to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"transactionId","type":"`string`","description":"A string indicating the ID of the current transaction.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"messageAggregator","type":"[IMessageAggregator](../../../types/interfaces/types.IMessageAggregator/page.mdx)","description":"An instance of a message aggregator, which is used to aggregate messages to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"requestId","type":"`string`","description":"A string indicating the ID of the current request.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"idempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the current workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"parentStepIdempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the parent workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="release"/>
|
||||
|
||||
### Returns
|
||||
|
||||
<TypeList types={[{"name":"Promise","type":"Promise<boolean>","optional":false,"defaultValue":"","description":"Whether the lock was successfully released. If the lock has a different owner than the one passed, the method returns `false`.","expandable":false,"children":[{"name":"boolean","type":"`boolean`","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="release"/>
|
||||
|
||||
___
|
||||
|
||||
## releaseAll
|
||||
|
||||
This method releases all locks. If you specify an owner ID, then all locks that the owner has acquired are released.
|
||||
|
||||
You can also pass a provider name to be used for locking. If no provider is passed, the default provider (in-memory or the provider configuerd in `medusa-config.ts`) will be used.
|
||||
|
||||
### Example
|
||||
|
||||
For example, to release all locks for a user with ID `user_123`:
|
||||
|
||||
```ts
|
||||
await lockingModuleService.releaseAll({
|
||||
ownerId: "user_123"
|
||||
})
|
||||
```
|
||||
|
||||
In this example, you release all locks for the user with ID `user_123`.
|
||||
|
||||
To specify the provider to use for locking, you can pass the provider name in the `args` argument:
|
||||
|
||||
```ts
|
||||
await lockingModuleService.releaseAll({
|
||||
ownerId: "user_123",
|
||||
provider: "lp_my-lock"
|
||||
})
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
<TypeList types={[{"name":"args","type":"`object`","description":"Additional arguments for releasing the locks.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"ownerId","type":"`null` \\| `string`","description":"The ID of a lock owner. If specified, all locks that the owner has acquired are released.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"provider","type":"`string`","description":"The provider name to use for locking. If no provider is passed, the default provider (in-memory or the provider configuerd in `medusa-config.ts`) will be used.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"sharedContext","type":"[Context](../../../types/interfaces/types.Context/page.mdx)","description":"A context used to share resources, such as transaction manager, between the application and the module.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"transactionManager","type":"TManager","description":"An instance of a transaction manager of type `TManager`, which is a typed parameter passed to the context to specify the type of the `transactionManager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"manager","type":"TManager","description":"An instance of a manager, typically an entity manager, of type `TManager`, which is a typed parameter passed to the context to specify the type of the `manager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationLevel","type":"`string`","description":"A string indicating the isolation level of the context. Possible values are `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, or `SERIALIZABLE`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"enableNestedTransactions","type":"`boolean`","description":"A boolean value indicating whether nested transactions are enabled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"eventGroupId","type":"`string`","description":"A string indicating the ID of the group to aggregate the events to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"transactionId","type":"`string`","description":"A string indicating the ID of the current transaction.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"messageAggregator","type":"[IMessageAggregator](../../../types/interfaces/types.IMessageAggregator/page.mdx)","description":"An instance of a message aggregator, which is used to aggregate messages to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"requestId","type":"`string`","description":"A string indicating the ID of the current request.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"idempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the current workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"parentStepIdempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the parent workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="releaseAll"/>
|
||||
|
||||
### Returns
|
||||
|
||||
<TypeList types={[{"name":"Promise","type":"Promise<void>","optional":false,"defaultValue":"","description":"This method releases all locks. If you specify an owner ID, then all locks that the owner has acquired are released.\n\nYou can also pass a provider name to be used for locking. If no provider is passed, the default provider (in-memory or the provider configuerd in `medusa-config.ts`) will be used.","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="releaseAll"/>
|
||||
@@ -0,0 +1,440 @@
|
||||
---
|
||||
slug: /references/locking-module-provider
|
||||
tags:
|
||||
- locking
|
||||
- server
|
||||
- how to
|
||||
sidebar_label: Create Locking Provider
|
||||
---
|
||||
|
||||
import { TypeList } from "docs-ui"
|
||||
|
||||
# How to Create a Locking Module Provider
|
||||
|
||||
In this document, you’ll learn how to create a Locking Module Provider and the methods you must implement in its main service.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Example
|
||||
|
||||
As you implement your Locking Module Provider, it can be useful to refer to an existing provider and how it's implemeted.
|
||||
|
||||
If you need to refer to an existing implementation as an example, check the [Redis Locking Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/locking-redis).
|
||||
|
||||
---
|
||||
|
||||
## 1. Create Module Provider Directory
|
||||
|
||||
Start by creating a new directory for your module provider.
|
||||
|
||||
If you're creating the module provider in a Medusa application, create it under the `src/modules` directory. For example, `src/modules/my-locking`.
|
||||
|
||||
If you're creating the module provider in a plugin, create it under the `src/providers` directory. For example, `src/providers/my-locking`.
|
||||
|
||||
<Note>
|
||||
|
||||
The rest of this guide always uses the `src/modules/my-locking` directory as an example.
|
||||
|
||||
</Note>
|
||||
|
||||
---
|
||||
|
||||
## 2. Create the Locking Module Provider Service
|
||||
|
||||
Create the file `src/modules/my-locking/service.ts` that holds the module provider's main service. It must implement the `ILockingProvider` interface imported from `@medusajs/framework/types`:
|
||||
|
||||
```ts title="src/modules/my-locking/service.ts"
|
||||
import { ILockingProvider } from "@medusajs/framework/types"
|
||||
|
||||
type Options = {
|
||||
url: string
|
||||
}
|
||||
|
||||
class MyLockingProviderService implements ILockingProvider {
|
||||
// TODO implement methods
|
||||
}
|
||||
|
||||
export default MyLockingProviderService
|
||||
```
|
||||
|
||||
### constructor
|
||||
|
||||
The constructor allows you to access resources from the module's container using the first parameter,
|
||||
and the module's options using the second parameter.
|
||||
|
||||
If you're creating a client or establishing a connection with a third-party service, do it in the constructor.
|
||||
|
||||
#### Example
|
||||
|
||||
```ts
|
||||
import { ILockingProvider } from "@medusajs/framework/types"
|
||||
import { Logger } from "@medusajs/framework/types"
|
||||
|
||||
type InjectedDependencies = {
|
||||
logger: Logger
|
||||
}
|
||||
|
||||
type Options = {
|
||||
url: string
|
||||
}
|
||||
|
||||
class MyLockingProviderService implements ILockingProvider {
|
||||
static identifier = "my-lock"
|
||||
protected logger_: Logger
|
||||
protected options_: Options
|
||||
// assuming you're initializing a client
|
||||
protected client
|
||||
|
||||
constructor (
|
||||
{ logger }: InjectedDependencies,
|
||||
options: Options
|
||||
) {
|
||||
this.logger_ = logger
|
||||
this.options_ = options
|
||||
|
||||
// assuming you're initializing a client
|
||||
this.client = new Client(options)
|
||||
}
|
||||
|
||||
// ...
|
||||
}
|
||||
|
||||
export default MyLockingProviderService
|
||||
```
|
||||
|
||||
### Identifier
|
||||
|
||||
Every locking module provider must have an `identifier` static property. The provider's ID
|
||||
will be stored as `lp_{identifier}`.
|
||||
|
||||
For example:
|
||||
|
||||
```ts
|
||||
class MyLockingProviderService implements ILockingProvider {
|
||||
static identifier = "my-lock"
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### execute
|
||||
|
||||
This method executes a given asynchronous job with a lock on the given keys. The Locking Module uses this method
|
||||
when you call its `execute` method and your provider is the default provider, or you pass your provider's identifier to its `execute` method.
|
||||
|
||||
In the method, you should first try to acquire the lock on the given keys before the specified timeout passes.
|
||||
Then, once the lock is acquired, you execute the job. Otherwise, if the timeout passes before the lock is acquired, you cancel the job.
|
||||
|
||||
#### Example
|
||||
|
||||
An example of how to implement the `execute` method:
|
||||
|
||||
```ts
|
||||
// other imports...
|
||||
import { Context } from "@medusajs/framework/types"
|
||||
import { setTimeout } from "node:timers/promises"
|
||||
|
||||
class MyLockingProviderService implements ILockingProvider {
|
||||
// ...
|
||||
async execute<T>(
|
||||
keys: string | string[],
|
||||
job: () => Promise<T>,
|
||||
args?: { timeout?: number },
|
||||
sharedContext?: Context
|
||||
): Promise<T> {
|
||||
// TODO you can add actions using the third-party client you initialized in the constructor
|
||||
const timeout = Math.max(args?.timeout ?? 5, 1)
|
||||
const timeoutSeconds = Number.isNaN(timeout) ? 1 : timeout
|
||||
const cancellationToken = { cancelled: false }
|
||||
const promises: Promise<any>[] = []
|
||||
|
||||
if (timeoutSeconds > 0) {
|
||||
promises.push(this.getTimeout(timeoutSeconds, cancellationToken))
|
||||
}
|
||||
|
||||
promises.push(
|
||||
this.acquire_(
|
||||
keys,
|
||||
{
|
||||
expire: args?.timeout ? timeoutSeconds : 0,
|
||||
},
|
||||
cancellationToken
|
||||
)
|
||||
)
|
||||
|
||||
await Promise.race(promises)
|
||||
|
||||
try {
|
||||
return await job()
|
||||
} finally {
|
||||
await this.release(keys)
|
||||
}
|
||||
}
|
||||
|
||||
private async getTimeout(
|
||||
seconds: number,
|
||||
cancellationToken: { cancelled: boolean }
|
||||
): Promise<void> {
|
||||
return new Promise(async (_, reject) => {
|
||||
await setTimeout(seconds * 1000)
|
||||
cancellationToken.cancelled = true
|
||||
reject(new Error("Timed-out acquiring lock."))
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, you first determine the timeout for acquiring the lock. You also create a `cancellationToken` object that you'll use to determine if the lock aquisition has timed out.
|
||||
|
||||
You then create an array of the following promises:
|
||||
|
||||
- A timeout promise that, if the lock acquisition takes longer than the timeout, sets the `cancelled` property of the `cancellationToken` object to `true`.
|
||||
- A promise that acquires the lock. You use a private `acquire_` method which you can find its implementation in the `aquire` method's example. If the first promise
|
||||
resolves and cancels the lock acquisition, the lock will not be acquired.
|
||||
|
||||
Finally, if the lock is acquired, you execute the job and release the lock after the job is done using the `release` method.
|
||||
|
||||
#### Type Parameters
|
||||
|
||||
<TypeList types={[{"name":"T","type":"`object`","description":"The type of the job's result.","optional":true,"defaultValue":"","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="execute"/>
|
||||
|
||||
#### Parameters
|
||||
|
||||
<TypeList types={[{"name":"keys","type":"`string` \\| `string`[]","description":"The keys to lock during the job's execution.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"job","type":"() => Promise<T>","description":"The asynchronous job to execute while the keys are locked.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"args","type":"`object`","description":"Additional arguments for the job execution.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"timeout","type":"`number`","description":"The timeout (in seconds) for acquiring the lock. If the time out is passed, the job is canceled and the lock is released.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"sharedContext","type":"[Context](../../../types/interfaces/types.Context/page.mdx)","description":"A context used to share resources, such as transaction manager, between the application and the module.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"transactionManager","type":"TManager","description":"An instance of a transaction manager of type `TManager`, which is a typed parameter passed to the context to specify the type of the `transactionManager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"manager","type":"TManager","description":"An instance of a manager, typically an entity manager, of type `TManager`, which is a typed parameter passed to the context to specify the type of the `manager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationLevel","type":"`string`","description":"A string indicating the isolation level of the context. Possible values are `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, or `SERIALIZABLE`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"enableNestedTransactions","type":"`boolean`","description":"A boolean value indicating whether nested transactions are enabled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"eventGroupId","type":"`string`","description":"A string indicating the ID of the group to aggregate the events to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"transactionId","type":"`string`","description":"A string indicating the ID of the current transaction.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"messageAggregator","type":"[IMessageAggregator](../../../types/interfaces/types.IMessageAggregator/page.mdx)","description":"An instance of a message aggregator, which is used to aggregate messages to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"requestId","type":"`string`","description":"A string indicating the ID of the current request.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"idempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the current workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"parentStepIdempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the parent workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="execute"/>
|
||||
|
||||
#### Returns
|
||||
|
||||
<TypeList types={[{"name":"Promise","type":"Promise<T>","optional":false,"defaultValue":"","description":"The result of the job.","expandable":false,"children":[{"name":"T","type":"T","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="execute"/>
|
||||
|
||||
### acquire
|
||||
|
||||
This method acquires a lock on the given keys. The Locking Module uses this method when you call its `acquire` method and your provider is the default provider,
|
||||
or you pass your provider's identifier to its `acquire` method.
|
||||
|
||||
In this method, you should only aquire the lock if the timeout hasn't passed. As explained in the [execute](page.mdx#execute) method's example,
|
||||
you can use a `cancellationToken` object to determine if the lock acquisition has timed out.
|
||||
|
||||
If the lock aquisition isn't canceled, you should aquire the lock, setting its expiry and owner. You should account for the following scenarios:
|
||||
|
||||
- The lock doesn't have an owner and you don't pass an owner, in which case the lock can be extended or released by anyone.
|
||||
- The lock doesn't have an owner or has the same owner that you pass, in which case you can extend the lock's expiration time and set the owner.
|
||||
- The lock has an owner, but you pass a different owner, in which case the method should throw an error.
|
||||
|
||||
#### Example
|
||||
|
||||
An example of how to implement the `acquire` method:
|
||||
|
||||
```ts
|
||||
type ResolvablePromise = {
|
||||
promise: Promise<any>
|
||||
resolve: () => void
|
||||
}
|
||||
|
||||
class MyLockingProviderService implements ILockingProvider {
|
||||
// ...
|
||||
async acquire(
|
||||
keys: string | string[],
|
||||
args?: {
|
||||
ownerId?: string | null
|
||||
expire?: number
|
||||
awaitQueue?: boolean
|
||||
}
|
||||
): Promise<void> {
|
||||
return this.acquire_(keys, args)
|
||||
}
|
||||
|
||||
async acquire_(
|
||||
keys: string | string[],
|
||||
args?: {
|
||||
ownerId?: string | null
|
||||
expire?: number
|
||||
awaitQueue?: boolean
|
||||
},
|
||||
cancellationToken?: { cancelled: boolean }
|
||||
): Promise<void> {
|
||||
keys = Array.isArray(keys) ? keys : [keys]
|
||||
const { ownerId, expire } = args ?? {}
|
||||
|
||||
for (const key of keys) {
|
||||
if (cancellationToken?.cancelled) {
|
||||
throw new Error("Timed-out acquiring lock.")
|
||||
}
|
||||
|
||||
// assuming your client has this method and it validates the owner and expiration
|
||||
const result = await this.client.acquireLock(key, ownerId, expire)
|
||||
|
||||
if (result !== 1) {
|
||||
throw new Error(`Failed to acquire lock for key "${key}"`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, you add a private `acquire_` method that you use to acquire the lock. This method accepts an additional `cancellationToken` argument that you can use to determine if the lock acquisition has timed out.
|
||||
You can then use this method in other methods, such as the `execute` method.
|
||||
|
||||
In the `acquire_` method, you loop through the keys and try to acquire the lock on each key if the lock acquisition hasn't timed out. If the lock acquisition fails, you throw an error.
|
||||
This method assumes that the client you're integrating has a method called `acquireLock` that validates the owner and expiration time, and returns `1` if the lock is successfully acquired.
|
||||
|
||||
#### Parameters
|
||||
|
||||
<TypeList types={[{"name":"keys","type":"`string` \\| `string`[]","description":"The keys to acquire the lock on.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"args","type":"`object`","description":"Additional arguments for acquiring the lock.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"ownerId","type":"`null` \\| `string`","description":"The ID of the lock's owner. If specified, only the owner can release the lock or extend its expiration time.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"expire","type":"`number`","description":"The expiration time (in seconds) for the lock. If the lock is already acquired and the owner is the same, the expiration time is extended\nby the value passed. If not specified, the lock does not expire.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"sharedContext","type":"[Context](../../../types/interfaces/types.Context/page.mdx)","description":"A context used to share resources, such as transaction manager, between the application and the module.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"transactionManager","type":"TManager","description":"An instance of a transaction manager of type `TManager`, which is a typed parameter passed to the context to specify the type of the `transactionManager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"manager","type":"TManager","description":"An instance of a manager, typically an entity manager, of type `TManager`, which is a typed parameter passed to the context to specify the type of the `manager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationLevel","type":"`string`","description":"A string indicating the isolation level of the context. Possible values are `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, or `SERIALIZABLE`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"enableNestedTransactions","type":"`boolean`","description":"A boolean value indicating whether nested transactions are enabled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"eventGroupId","type":"`string`","description":"A string indicating the ID of the group to aggregate the events to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"transactionId","type":"`string`","description":"A string indicating the ID of the current transaction.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"messageAggregator","type":"[IMessageAggregator](../../../types/interfaces/types.IMessageAggregator/page.mdx)","description":"An instance of a message aggregator, which is used to aggregate messages to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"requestId","type":"`string`","description":"A string indicating the ID of the current request.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"idempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the current workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"parentStepIdempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the parent workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="acquire"/>
|
||||
|
||||
#### Returns
|
||||
|
||||
<TypeList types={[{"name":"Promise","type":"Promise<void>","optional":false,"defaultValue":"","description":"Resolves when the lock is acquired.","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="acquire"/>
|
||||
|
||||
### release
|
||||
|
||||
This method releases a lock on the given keys. The Locking Module uses this method when you call its `release` method and your provider is the default provider,
|
||||
or you pass your provider's identifier to its `release` method.
|
||||
|
||||
In this method, you should release the lock on the given keys. If the lock has an owner, you should only release the lock if the owner is the same as the one passed.
|
||||
|
||||
#### Example
|
||||
|
||||
An example of how to implement the `release` method:
|
||||
|
||||
```ts
|
||||
// other imports...
|
||||
import { promiseAll } from "@medusajs/framework/utils"
|
||||
|
||||
class MyLockingProviderService implements ILockingProvider {
|
||||
// ...
|
||||
async release(
|
||||
keys: string | string[],
|
||||
args?: { ownerId?: string | null },
|
||||
sharedContext?: Context
|
||||
): Promise<boolean> {
|
||||
const ownerId = args?.ownerId ?? "*"
|
||||
keys = Array.isArray(keys) ? keys : [keys]
|
||||
|
||||
const releasePromises = keys.map(async (key) => {
|
||||
// assuming your client has this method and it validates the owner
|
||||
const result = await this.client.releaseLock(key, ownerId)
|
||||
return result === 1
|
||||
})
|
||||
|
||||
const results = await promiseAll(releasePromises)
|
||||
|
||||
return results.every((released) => released)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, you loop through the keys and try to release the lock on each key using the client you're integrating. This implementation assumes that the client validates
|
||||
ownership of the lock and returns a result of `1` if the lock is successfully released.
|
||||
|
||||
#### Parameters
|
||||
|
||||
<TypeList types={[{"name":"keys","type":"`string` \\| `string`[]","description":"The keys to release the lock from.","optional":false,"defaultValue":"","expandable":false,"children":[]},{"name":"args","type":"`object`","description":"Additional arguments for releasing the lock.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"ownerId","type":"`null` \\| `string`","description":"The ID of the lock's owner. The lock can be released either if it doesn't have an owner, or\nif its owner ID matches the one passed in this property.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"sharedContext","type":"[Context](../../../types/interfaces/types.Context/page.mdx)","description":"A context used to share resources, such as transaction manager, between the application and the module.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"transactionManager","type":"TManager","description":"An instance of a transaction manager of type `TManager`, which is a typed parameter passed to the context to specify the type of the `transactionManager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"manager","type":"TManager","description":"An instance of a manager, typically an entity manager, of type `TManager`, which is a typed parameter passed to the context to specify the type of the `manager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationLevel","type":"`string`","description":"A string indicating the isolation level of the context. Possible values are `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, or `SERIALIZABLE`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"enableNestedTransactions","type":"`boolean`","description":"A boolean value indicating whether nested transactions are enabled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"eventGroupId","type":"`string`","description":"A string indicating the ID of the group to aggregate the events to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"transactionId","type":"`string`","description":"A string indicating the ID of the current transaction.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"messageAggregator","type":"[IMessageAggregator](../../../types/interfaces/types.IMessageAggregator/page.mdx)","description":"An instance of a message aggregator, which is used to aggregate messages to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"requestId","type":"`string`","description":"A string indicating the ID of the current request.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"idempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the current workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"parentStepIdempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the parent workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="release"/>
|
||||
|
||||
#### Returns
|
||||
|
||||
<TypeList types={[{"name":"Promise","type":"Promise<boolean>","optional":false,"defaultValue":"","description":"Whether the lock was successfully released. If the lock has a different owner than the one passed, the method returns `false`.","expandable":false,"children":[{"name":"boolean","type":"`boolean`","optional":false,"defaultValue":"","description":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="release"/>
|
||||
|
||||
### releaseAll
|
||||
|
||||
This method releases all locks. The Locking Module uses this method when you call its `releaseAll` method and your provider is the default provider,
|
||||
or you pass your provider's identifier to its `releaseAll` method.
|
||||
|
||||
In this method, you should release all locks if no owner is passed. If an owner is passed, you should only release the locks that the owner has acquired.
|
||||
|
||||
#### Example
|
||||
|
||||
An example of how to implement the `releaseAll` method:
|
||||
|
||||
```ts
|
||||
class MyLockingProviderService implements ILockingProvider {
|
||||
// ...
|
||||
async releaseAll(
|
||||
args?: { ownerId?: string | null },
|
||||
sharedContext?: Context
|
||||
): Promise<void> {
|
||||
const ownerId = args?.ownerId ?? "*"
|
||||
|
||||
await this.client.releaseAllLock(ownerId)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In this example, you release all locks either of all owners or the owner passed as an argument. This implementation assumes that the client you're integrating has a method called `releaseAllLock` that releases all locks
|
||||
for all owners or a specific owner.
|
||||
|
||||
#### Parameters
|
||||
|
||||
<TypeList types={[{"name":"args","type":"`object`","description":"Additional arguments for releasing the locks.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"ownerId","type":"`null` \\| `string`","description":"The ID of a lock owner. If specified, all locks that the owner has acquired are released.","optional":true,"defaultValue":"","expandable":false,"children":[]}]},{"name":"sharedContext","type":"[Context](../../../types/interfaces/types.Context/page.mdx)","description":"A context used to share resources, such as transaction manager, between the application and the module.","optional":true,"defaultValue":"","expandable":false,"children":[{"name":"transactionManager","type":"TManager","description":"An instance of a transaction manager of type `TManager`, which is a typed parameter passed to the context to specify the type of the `transactionManager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"manager","type":"TManager","description":"An instance of a manager, typically an entity manager, of type `TManager`, which is a typed parameter passed to the context to specify the type of the `manager`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"isolationLevel","type":"`string`","description":"A string indicating the isolation level of the context. Possible values are `READ UNCOMMITTED`, `READ COMMITTED`, `REPEATABLE READ`, or `SERIALIZABLE`.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"enableNestedTransactions","type":"`boolean`","description":"A boolean value indicating whether nested transactions are enabled.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"eventGroupId","type":"`string`","description":"A string indicating the ID of the group to aggregate the events to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"transactionId","type":"`string`","description":"A string indicating the ID of the current transaction.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"messageAggregator","type":"[IMessageAggregator](../../../types/interfaces/types.IMessageAggregator/page.mdx)","description":"An instance of a message aggregator, which is used to aggregate messages to be emitted at a later point.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"requestId","type":"`string`","description":"A string indicating the ID of the current request.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"idempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the current workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]},{"name":"parentStepIdempotencyKey","type":"`string`","description":"A string indicating the idempotencyKey of the parent workflow execution.","optional":true,"defaultValue":"","expandable":false,"children":[]}]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="releaseAll"/>
|
||||
|
||||
#### Returns
|
||||
|
||||
<TypeList types={[{"name":"Promise","type":"Promise<void>","optional":false,"defaultValue":"","description":"This method releases all locks. The Locking Module uses this method when you call its `releaseAll` method and your provider is the default provider,\nor you pass your provider's identifier to its `releaseAll` method.\n\nIn this method, you should release all locks if no owner is passed. If an owner is passed, you should only release the locks that the owner has acquired.","expandable":false,"children":[]}]} expandUrl="https://docs.medusajs.com/learn/fundamentals/data-models/manage-relationships#retrieve-records-of-relation" sectionTitle="releaseAll"/>
|
||||
|
||||
---
|
||||
|
||||
## 3. Create Module Definition File
|
||||
|
||||
Create the file `src/modules/my-locking/index.ts` with the following content:
|
||||
|
||||
```ts title="src/modules/my-locking/index.ts"
|
||||
import { ModuleProvider, Modules } from "@medusajs/framework/utils"
|
||||
import MyLockingProviderService from "./service"
|
||||
|
||||
export default ModuleProvider(Modules.LOCKING, {
|
||||
services: [MyLockingProviderService],
|
||||
})
|
||||
```
|
||||
|
||||
This exports the module provider's definition, indicating that the `MyLockingProviderService` is the module provider's service.
|
||||
|
||||
---
|
||||
|
||||
## 4. Use Module Provider
|
||||
|
||||
To use your Locking Module Provider, add it to the `providers` array of the Locking Module in `medusa-config.ts`:
|
||||
|
||||
```ts title="medusa-config.ts"
|
||||
module.exports = defineConfig({
|
||||
// ...
|
||||
modules: [
|
||||
{
|
||||
resolve: "@medusajs/medusa/payment",
|
||||
options: {
|
||||
providers: [
|
||||
{
|
||||
// if module provider is in a plugin, use `plugin-name/providers/my-locking`
|
||||
resolve: "./src/modules/my-locking",
|
||||
id: "my-lock",
|
||||
// set this if you want this provider to be used by default
|
||||
// and you have other Locking Module Providers registered.
|
||||
is_default: true,
|
||||
options: {
|
||||
url: "http://example.com",
|
||||
// provider options...
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Test it Out
|
||||
|
||||
When you start the Medusa application, if your Locking Module Provider is the only registered provider without enabling `is_default`, you'll see the following message:
|
||||
|
||||
```bash
|
||||
info: Locking module: Using "my-lock" as default.
|
||||
```
|
||||
|
||||
This indicates that your Locking Module Provider is being used as the default provider.
|
||||
|
||||
The Locking Module will now use your provider to handle all locking operations.
|
||||
|
||||
---
|
||||
|
||||
## Useful Guides
|
||||
|
||||
- [How to Use Locking Module](/references/locking-service)
|
||||
@@ -0,0 +1,8 @@
|
||||
import { TypeList } from "docs-ui"
|
||||
|
||||
# locking
|
||||
|
||||
## Interfaces
|
||||
|
||||
- [ILockingModule](../../locking/interfaces/locking.ILockingModule/page.mdx)
|
||||
- [ILockingProvider](../../locking/interfaces/locking.ILockingProvider/page.mdx)
|
||||
+15
-7
@@ -9,9 +9,17 @@ sidebar_label: Create Notification Provider
|
||||
|
||||
import { TypeList } from "docs-ui"
|
||||
|
||||
# How to Create a Notification Provider Module
|
||||
# How to Create a Notification Module Provider
|
||||
|
||||
In this document, you’ll learn how to create a notification provider module and the methods you must implement in it.
|
||||
In this document, you’ll learn how to create a Notification Module Provider and the methods you must implement in it.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Example
|
||||
|
||||
As you implement your Notification Module Provider, it can be useful to refer to an existing provider and how it's implemeted.
|
||||
|
||||
If you need to refer to an existing implementation as an example, check the [SendGrid Notification Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/notification-sendgrid).
|
||||
|
||||
---
|
||||
|
||||
@@ -31,11 +39,11 @@ The rest of this guide always uses the `src/modules/my-notification` directory a
|
||||
|
||||
---
|
||||
|
||||
## 2. Create the Notification Provider Service
|
||||
## 2. Create the Notification Module Provider's Service
|
||||
|
||||
Create the file `src/modules/my-notification/service.ts` that holds the implementation of the notification service.
|
||||
|
||||
The Notification Provider Module's main service must extend the `AbstractNotificationProviderService` class imported from `@medusajs/framework/utils`:
|
||||
The Notification Module Provider's main service must extend the `AbstractNotificationProviderService` class imported from `@medusajs/framework/utils`:
|
||||
|
||||
```ts title="src/modules/my-notification/service.ts"
|
||||
import {
|
||||
@@ -172,7 +180,7 @@ class MyNotificationProviderService extends AbstractNotificationProviderService
|
||||
|
||||
---
|
||||
|
||||
## 3. Create Module Definition File
|
||||
## 3. Create Module Provider Definition File
|
||||
|
||||
Create the file `src/modules/my-notification/index.ts` with the following content:
|
||||
|
||||
@@ -188,11 +196,11 @@ export default ModuleProvider(Modules.NOTIFICATION, {
|
||||
})
|
||||
```
|
||||
|
||||
This exports the module's definition, indicating that the `MyNotificationProviderService` is the module's service.
|
||||
This exports the module provider's definition, indicating that the `MyNotificationProviderService` is the module provider's service.
|
||||
|
||||
---
|
||||
|
||||
## 4. Use Module
|
||||
## 4. Use Module Provider
|
||||
|
||||
To use your Notification Module Provider, add it to the `providers` array of the Notification Module in `medusa-config.ts`:
|
||||
|
||||
|
||||
+21
-13
@@ -9,17 +9,25 @@ sidebar_label: Create Payment Provider
|
||||
|
||||
import { TypeList } from "docs-ui"
|
||||
|
||||
# How to Create a Payment Provider
|
||||
# How to Create a Payment Module Provider
|
||||
|
||||
In this document, you’ll learn how to create a Payment Provider to be used with the Payment Module.
|
||||
In this document, you’ll learn how to create a Payment Module Provider to be used with the Payment Module.
|
||||
|
||||
---
|
||||
|
||||
## Understanding Payment Provider Implementation
|
||||
## Implementation Example
|
||||
|
||||
As you implement your Payment Module Provider, it can be useful to refer to an existing provider and how it's implemeted.
|
||||
|
||||
The Payment Module Provider handles processing payment with a third-party provirder. However, it's not responsible for managing payment concepts within Medusa, such as payment sessions or collections. These concepts are handled by the Payment Module which uses your payment provider within core operations.
|
||||
If you need to refer to an existing implementation as an example, check the [Stripe Payment Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/payment-stripe).
|
||||
|
||||
For example, when the merchant captures an order's payment, the Payment Module uses the payment provider to capture the payment, the makes updates to the `Payment` record associated with the order. So, you only have to implement the third-party payment processing logic in your payment provider.
|
||||
---
|
||||
|
||||
## Understanding Payment Module Provider Implementation
|
||||
|
||||
The Payment Module Provider handles processing payment with a third-party provirder. However, it's not responsible for managing payment concepts within Medusa, such as payment sessions or collections. These concepts are handled by the Payment Module which uses your Payment Module Provider within core operations.
|
||||
|
||||
For example, when the merchant captures an order's payment, the Payment Module uses the Payment Module Provider to capture the payment, the makes updates to the `Payment` record associated with the order. So, you only have to implement the third-party payment processing logic in your Payment Module Provider.
|
||||
|
||||
---
|
||||
|
||||
@@ -39,9 +47,9 @@ The rest of this guide always uses the `src/modules/my-payment` directory as an
|
||||
|
||||
---
|
||||
|
||||
## 2. Create the Payment Provider Service
|
||||
## 2. Create the Payment Module Provider's Service
|
||||
|
||||
Create the file `src/modules/my-payment/service.ts` that holds the module's main service. It must extend the `AbstractPaymentProvider` class imported from `@medusajs/framework/utils`:
|
||||
Create the file `src/modules/my-payment/service.ts` that holds the module provider's main service. It must extend the `AbstractPaymentProvider` class imported from `@medusajs/framework/utils`:
|
||||
|
||||
```ts title="src/modules/my-payment/service.ts"
|
||||
import { AbstractPaymentProvider } from "@medusajs/framework/utils"
|
||||
@@ -630,7 +638,7 @@ class MyPaymentProviderService extends AbstractPaymentProvider<
|
||||
|
||||
---
|
||||
|
||||
## 3. Create Module Definition File
|
||||
## 3. Create Module Provider Definition File
|
||||
|
||||
Create the file `src/modules/my-payment/index.ts` with the following content:
|
||||
|
||||
@@ -646,11 +654,11 @@ export default ModuleProvider(Modules.PAYMENT, {
|
||||
})
|
||||
```
|
||||
|
||||
This exports the module's definition, indicating that the `MyPaymentProviderService` is the module's service.
|
||||
This exports the module provider's definition, indicating that the `MyPaymentProviderService` is the module provider's service.
|
||||
|
||||
---
|
||||
|
||||
## 4. Use Module
|
||||
## 4. Use Module Provider
|
||||
|
||||
To use your Payment Module Provider, add it to the `providers` array of the Payment Module in `medusa-config.ts`:
|
||||
|
||||
@@ -682,12 +690,12 @@ module.exports = defineConfig({
|
||||
|
||||
## 5. Test it Out
|
||||
|
||||
Before you use your payment provider, enable it in a region using the Medusa Admin.
|
||||
Before you use your Payment Module Provider, enable it in a region using the Medusa Admin.
|
||||
|
||||
Then, go through checkout to place an order. Your payment provider is used to authorize the payment.
|
||||
Then, go through checkout to place an order. Your Payment Module Provider is used to authorize the payment.
|
||||
|
||||
---
|
||||
|
||||
## Useful Guides
|
||||
|
||||
- [Storefront Guide: how to implement UI for your payment provider during checkout](https://docs.medusajs.com/resources/storefront-development/checkout/payment)
|
||||
- [Storefront Guide: how to implement UI for your Payment Module Provider during checkout](https://docs.medusajs.com/resources/storefront-development/checkout/payment)
|
||||
|
||||
+11
-11
@@ -9,23 +9,23 @@ sidebar_label: Create Tax Provider
|
||||
|
||||
import { TypeList } from "docs-ui"
|
||||
|
||||
# How to Create a Tax Provider
|
||||
# How to Create a Tax Module Provider
|
||||
|
||||
In this document, you’ll learn how to create a tax provider to use with the Tax Module, and the methods to implement.
|
||||
In this document, you’ll learn how to create a Tax Module Provider to use with the Tax Module, and the methods to implement.
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
A tax provider is used to retrieve the tax lines in a provided context. The Tax Module provides a default `system` provider. You can create your own tax provider, either in a plugin, in a module provider, or directly in your Medusa application's codebase, then use it in any tax region.
|
||||
A Tax Module Provider is used to retrieve the tax lines in a provided context. The Tax Module provides a default `system` provider. You can create your own Tax Module Provider, either in a plugin, in a module provider, or directly in your Medusa application's codebase, then use it in any tax region.
|
||||
|
||||
---
|
||||
|
||||
## Understanding Tax Provider Implementation
|
||||
## Understanding Tax Module Provider Implementation
|
||||
|
||||
The Tax Module Provider handles calculating taxes with a third-party provirder. However, it's not responsible for managing tax concepts within Medusa, such as creating a tax region. The Tax Module uses your tax provider within core operations.
|
||||
The Tax Module Provider handles calculating taxes with a third-party provirder. However, it's not responsible for managing tax concepts within Medusa, such as creating a tax region. The Tax Module uses your Tax Module Provider within core operations.
|
||||
|
||||
For example, during checkout, the tax provider of the tax region that the customer is in is used to calculate the tax for the cart and order. So, you only have to implement the third-party tax calculation logic in your tax provider.
|
||||
For example, during checkout, the Tax Module Provider of the tax region that the customer is in is used to calculate the tax for the cart and order. So, you only have to implement the third-party tax calculation logic in your Tax Module Provider.
|
||||
|
||||
---
|
||||
|
||||
@@ -45,9 +45,9 @@ The rest of this guide always uses the `src/modules/my-tax` directory as an exam
|
||||
|
||||
---
|
||||
|
||||
## 2. Create the Tax Provider Service
|
||||
## 2. Create the Tax Module Provider's Service
|
||||
|
||||
Create the file `src/modules/my-tax/service.ts` that holds the module's main service. It must extend the `ITaxProvider` class imported from `@medusajs/framework/types`:
|
||||
Create the file `src/modules/my-tax/service.ts` that holds the module provider's main service. It must extend the `ITaxProvider` class imported from `@medusajs/framework/types`:
|
||||
|
||||
```ts title="src/modules/my-tax/service.ts"
|
||||
import { ITaxProvider } from "@medusajs/framework/types"
|
||||
@@ -156,7 +156,7 @@ export default class SystemTaxService implements ITaxProvider {
|
||||
|
||||
---
|
||||
|
||||
## 3. Create Module Definition File
|
||||
## 3. Create Module Provider Definition File
|
||||
|
||||
Create the file `src/modules/my-tax/index.ts` with the following content:
|
||||
|
||||
@@ -172,11 +172,11 @@ export default ModuleProvider(Modules.TAX, {
|
||||
})
|
||||
```
|
||||
|
||||
This exports the module's definition, indicating that the `MyTaxProvider` is the module's service.
|
||||
This exports the module provider's definition, indicating that the `MyTaxProvider` is the module provider's service.
|
||||
|
||||
---
|
||||
|
||||
## 4. Use Module
|
||||
## 4. Use Module Provider
|
||||
|
||||
To use your Tax Module Provider, add it to the `providers` array of the Tax Module in `medusa-config.ts`:
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ export const architecturalModulesSidebar = [
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
title: "Cache Modules",
|
||||
title: "Cache Module",
|
||||
initialOpen: true,
|
||||
children: [
|
||||
{
|
||||
@@ -19,14 +19,20 @@ export const architecturalModulesSidebar = [
|
||||
title: "Overview",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/cache/in-memory",
|
||||
title: "In-Memory",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/cache/redis",
|
||||
title: "Redis",
|
||||
type: "sub-category",
|
||||
title: "Modules",
|
||||
children: [
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/cache/in-memory",
|
||||
title: "In-Memory",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/cache/redis",
|
||||
title: "Redis",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "sub-category",
|
||||
@@ -43,7 +49,7 @@ export const architecturalModulesSidebar = [
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
title: "Event Modules",
|
||||
title: "Event Module",
|
||||
initialOpen: true,
|
||||
children: [
|
||||
{
|
||||
@@ -52,14 +58,20 @@ export const architecturalModulesSidebar = [
|
||||
title: "Overview",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/event/local",
|
||||
title: "Local",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/event/redis",
|
||||
title: "Redis",
|
||||
type: "sub-category",
|
||||
title: "Modules",
|
||||
children: [
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/event/local",
|
||||
title: "Local",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/event/redis",
|
||||
title: "Redis",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "sub-category",
|
||||
@@ -76,7 +88,7 @@ export const architecturalModulesSidebar = [
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
title: "File Module Providers",
|
||||
title: "File Module",
|
||||
initialOpen: true,
|
||||
children: [
|
||||
{
|
||||
@@ -85,14 +97,20 @@ export const architecturalModulesSidebar = [
|
||||
title: "Overview",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/file/local",
|
||||
title: "Local",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/file/s3",
|
||||
title: "AWS S3 (and Compatible APIs)",
|
||||
type: "sub-category",
|
||||
title: "Providers",
|
||||
children: [
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/file/local",
|
||||
title: "Local",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/file/s3",
|
||||
title: "AWS S3 (and Compatible APIs)",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "sub-category",
|
||||
@@ -109,7 +127,51 @@ export const architecturalModulesSidebar = [
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
title: "Notification Module Providers",
|
||||
title: "Locking Module",
|
||||
initialOpen: true,
|
||||
children: [
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/locking",
|
||||
title: "Overview",
|
||||
},
|
||||
{
|
||||
type: "sub-category",
|
||||
title: "Providers",
|
||||
children: [
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/locking/redis",
|
||||
title: "Redis",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/locking/postgres",
|
||||
title: "PostgreSQL",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "sub-category",
|
||||
title: "Guides",
|
||||
children: [
|
||||
{
|
||||
type: "link",
|
||||
path: "/references/locking-module-provider",
|
||||
title: "Create Locking Provider",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
path: "/references/locking-service",
|
||||
title: "Use Locking Module",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
title: "Notification Module",
|
||||
initialOpen: true,
|
||||
children: [
|
||||
{
|
||||
@@ -118,14 +180,20 @@ export const architecturalModulesSidebar = [
|
||||
title: "Overview",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/notification/local",
|
||||
title: "Local",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/notification/sendgrid",
|
||||
title: "SendGrid",
|
||||
type: "sub-category",
|
||||
title: "Providers",
|
||||
children: [
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/notification/local",
|
||||
title: "Local",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/notification/sendgrid",
|
||||
title: "SendGrid",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: "sub-category",
|
||||
@@ -137,7 +205,7 @@ export const architecturalModulesSidebar = [
|
||||
title: "Create Notification Provider",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
type: "ref",
|
||||
path: "/integrations/guides/resend",
|
||||
title: "Integrate Resend",
|
||||
},
|
||||
@@ -152,7 +220,7 @@ export const architecturalModulesSidebar = [
|
||||
},
|
||||
{
|
||||
type: "category",
|
||||
title: "Workflow Engine Modules",
|
||||
title: "Workflow Engine Module",
|
||||
initialOpen: true,
|
||||
children: [
|
||||
{
|
||||
@@ -161,14 +229,20 @@ export const architecturalModulesSidebar = [
|
||||
title: "Overview",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/workflow-engine/in-memory",
|
||||
title: "In-Memory",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/workflow-engine/redis",
|
||||
title: "Redis",
|
||||
type: "sub-category",
|
||||
title: "Modules",
|
||||
children: [
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/workflow-engine/in-memory",
|
||||
title: "In-Memory",
|
||||
},
|
||||
{
|
||||
type: "link",
|
||||
path: "/architectural-modules/workflow-engine/redis",
|
||||
title: "Redis",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -59,6 +59,47 @@ const sidebarMappings: {
|
||||
"/nextjs-starter",
|
||||
],
|
||||
},
|
||||
{
|
||||
module: async () =>
|
||||
import(
|
||||
"@/generated/generated-architectural-modules-sidebar.mjs"
|
||||
) as Promise<{
|
||||
default: Sidebar.Sidebar
|
||||
}>,
|
||||
paths: [
|
||||
"/architectural-modules",
|
||||
"/references/file-provider-module",
|
||||
"/references/locking",
|
||||
"/references/notification-provider-module",
|
||||
],
|
||||
},
|
||||
{
|
||||
module: async () =>
|
||||
import("@/generated/generated-commerce-modules-sidebar.mjs") as Promise<{
|
||||
default: Sidebar.Sidebar
|
||||
}>,
|
||||
paths: [
|
||||
"/commerce-modules",
|
||||
"/references/api-key",
|
||||
"/references/auth",
|
||||
"/references/cart",
|
||||
"/references/currency",
|
||||
"/references/customer",
|
||||
"/references/fulfillment",
|
||||
"/references/inventory",
|
||||
"/references/order",
|
||||
"/references/payment",
|
||||
"/references/pricing",
|
||||
"/references/product",
|
||||
"/references/promotion",
|
||||
"/references/region",
|
||||
"/references/sales-channel",
|
||||
"/references/stock-location",
|
||||
"/references/store",
|
||||
"/references/tax",
|
||||
"/references/user",
|
||||
],
|
||||
},
|
||||
{
|
||||
module: async () =>
|
||||
import("@/generated/generated-references-sidebar.mjs") as Promise<{
|
||||
@@ -78,22 +119,6 @@ const sidebarMappings: {
|
||||
"/references-overview",
|
||||
],
|
||||
},
|
||||
{
|
||||
module: async () =>
|
||||
import(
|
||||
"@/generated/generated-architectural-modules-sidebar.mjs"
|
||||
) as Promise<{
|
||||
default: Sidebar.Sidebar
|
||||
}>,
|
||||
paths: ["/architectural-modules"],
|
||||
},
|
||||
{
|
||||
module: async () =>
|
||||
import("@/generated/generated-commerce-modules-sidebar.mjs") as Promise<{
|
||||
default: Sidebar.Sidebar
|
||||
}>,
|
||||
paths: ["/commerce-modules"],
|
||||
},
|
||||
{
|
||||
module: async () =>
|
||||
import("@/generated/generated-troubleshooting-sidebar.mjs") as Promise<{
|
||||
|
||||
@@ -38,6 +38,7 @@ export const MainNavItemDropdown = ({
|
||||
className={clsx("transition-transform", isOpen && "rotate-180")}
|
||||
/>
|
||||
}
|
||||
className="!flex"
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -9,19 +9,22 @@ type MainNavItemLinkProps = {
|
||||
item: NavigationItemLink
|
||||
isActive: boolean
|
||||
icon?: React.ReactNode
|
||||
className?: string
|
||||
}
|
||||
|
||||
export const MainNavItemLink = ({
|
||||
item,
|
||||
isActive,
|
||||
icon,
|
||||
className,
|
||||
}: MainNavItemLinkProps) => {
|
||||
return (
|
||||
<LinkButton
|
||||
href={item.link}
|
||||
className={clsx(
|
||||
isActive && "text-medusa-fg-base",
|
||||
!isActive && "text-medusa-fg-muted hover:text-medusa-fg-subtle"
|
||||
!isActive && "text-medusa-fg-muted hover:text-medusa-fg-subtle",
|
||||
className
|
||||
)}
|
||||
>
|
||||
{item.title}
|
||||
|
||||
@@ -30,6 +30,7 @@ export const navDropdownItems: NavigationItem[] = [
|
||||
{
|
||||
type: "dropdown",
|
||||
title: "Build",
|
||||
project: "resources",
|
||||
children: [
|
||||
{
|
||||
type: "link",
|
||||
@@ -57,6 +58,7 @@ export const navDropdownItems: NavigationItem[] = [
|
||||
type: "dropdown",
|
||||
title: "Tools",
|
||||
link: "/resources/tools",
|
||||
project: "resources",
|
||||
children: [
|
||||
{
|
||||
type: "sub-menu",
|
||||
@@ -94,6 +96,8 @@ export const navDropdownItems: NavigationItem[] = [
|
||||
{
|
||||
type: "dropdown",
|
||||
title: "Reference",
|
||||
project: "resources",
|
||||
link: "/resources/references-overview",
|
||||
children: [
|
||||
{
|
||||
type: "link",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { usePathname } from "next/navigation"
|
||||
import React, { createContext, useContext, useMemo } from "react"
|
||||
import { NavigationItem } from "types"
|
||||
import { MenuItem, NavigationItem, NavigationItemDropdown } from "types"
|
||||
import { useSiteConfig } from "../SiteConifg"
|
||||
|
||||
export type MainNavContext = {
|
||||
@@ -28,6 +28,46 @@ export const MainNavProvider = ({
|
||||
|
||||
const baseUrl = `${config.baseUrl}${config.basePath}`
|
||||
|
||||
const findActiveItem = (
|
||||
items: NavigationItemDropdown["children"],
|
||||
currentUrl: string
|
||||
) => {
|
||||
let item: MenuItem | undefined
|
||||
let fallbackIndex: number | undefined
|
||||
items.some((childItem, index) => {
|
||||
if (childItem.type !== "link" && childItem.type !== "sub-menu") {
|
||||
return false
|
||||
}
|
||||
|
||||
if (childItem.type === "sub-menu") {
|
||||
const activeChildRes = findActiveItem(childItem.items, currentUrl)
|
||||
item = activeChildRes.item
|
||||
fallbackIndex = activeChildRes.fallbackIndex
|
||||
return !!item
|
||||
}
|
||||
|
||||
const isItemActive = currentUrl.startsWith(childItem.link)
|
||||
|
||||
if (!isItemActive) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (childItem.useAsFallback && fallbackIndex === undefined) {
|
||||
fallbackIndex = index
|
||||
return false
|
||||
}
|
||||
|
||||
item = childItem
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
return {
|
||||
item,
|
||||
fallbackIndex,
|
||||
}
|
||||
}
|
||||
|
||||
const activeItemIndex = useMemo(() => {
|
||||
const currentUrl = `${baseUrl}${pathname}`.replace(/\/$/, "")
|
||||
|
||||
@@ -35,24 +75,15 @@ export const MainNavProvider = ({
|
||||
|
||||
const index = navItems.findIndex((item, index) => {
|
||||
if (item.type === "dropdown") {
|
||||
return item.children.some((childItem) => {
|
||||
if (childItem.type !== "link") {
|
||||
return
|
||||
}
|
||||
const { item: activeChild, fallbackIndex: childFallbackIndex } =
|
||||
findActiveItem(item.children, currentUrl)
|
||||
|
||||
const isItemActive = currentUrl.startsWith(childItem.link)
|
||||
if (activeChild) {
|
||||
fallbackIndex = childFallbackIndex
|
||||
return true
|
||||
}
|
||||
|
||||
if (
|
||||
isItemActive &&
|
||||
childItem.useAsFallback &&
|
||||
fallbackIndex === undefined
|
||||
) {
|
||||
fallbackIndex = index
|
||||
return false
|
||||
}
|
||||
|
||||
return isItemActive
|
||||
})
|
||||
return item.link && currentUrl.startsWith(item.link)
|
||||
}
|
||||
|
||||
if (item.project && item.project !== config.project.key) {
|
||||
|
||||
@@ -35,6 +35,14 @@ export const howTo = [
|
||||
"title": "Create Fulfillment Provider",
|
||||
"path": "https://docs.medusajs.com/resources/references/fulfillment/provider"
|
||||
},
|
||||
{
|
||||
"title": "Use Locking Module",
|
||||
"path": "https://docs.medusajs.com/resources/references/locking-service"
|
||||
},
|
||||
{
|
||||
"title": "Create Locking Provider",
|
||||
"path": "https://docs.medusajs.com/resources/references/locking-module-provider"
|
||||
},
|
||||
{
|
||||
"title": "Create Notification Provider",
|
||||
"path": "https://docs.medusajs.com/resources/references/notification-provider-module"
|
||||
|
||||
@@ -1,45 +1,45 @@
|
||||
export * from "./user-guide.js"
|
||||
export * from "./fulfillment.js"
|
||||
export * from "./order.js"
|
||||
export * from "./payment.js"
|
||||
export * from "./cache.js"
|
||||
export * from "./how-to.js"
|
||||
export * from "./pricing.js"
|
||||
export * from "./server.js"
|
||||
export * from "./product.js"
|
||||
export * from "./order.js"
|
||||
export * from "./inventory.js"
|
||||
export * from "./auth.js"
|
||||
export * from "./pricing.js"
|
||||
export * from "./event.js"
|
||||
export * from "./promotion.js"
|
||||
export * from "./customer.js"
|
||||
export * from "./user.js"
|
||||
export * from "./notification.js"
|
||||
export * from "./auth.js"
|
||||
export * from "./api-key.js"
|
||||
export * from "./workflow.js"
|
||||
export * from "./stock-location.js"
|
||||
export * from "./notification.js"
|
||||
export * from "./region.js"
|
||||
export * from "./cart.js"
|
||||
export * from "./tutorial.js"
|
||||
export * from "./inventory.js"
|
||||
export * from "./sales-channel.js"
|
||||
export * from "./tax.js"
|
||||
export * from "./user.js"
|
||||
export * from "./region.js"
|
||||
export * from "./store.js"
|
||||
export * from "./query.js"
|
||||
export * from "./checkout.js"
|
||||
export * from "./storefront.js"
|
||||
export * from "./cart.js"
|
||||
export * from "./currency.js"
|
||||
export * from "./extend-module.js"
|
||||
export * from "./example.js"
|
||||
export * from "./product-category.js"
|
||||
export * from "./stripe.js"
|
||||
export * from "./tutorial.js"
|
||||
export * from "./fulfillment.js"
|
||||
export * from "./tax.js"
|
||||
export * from "./concept.js"
|
||||
export * from "./remote-query.js"
|
||||
export * from "./step.js"
|
||||
export * from "./link.js"
|
||||
export * from "./stripe.js"
|
||||
export * from "./storefront.js"
|
||||
export * from "./product-category.js"
|
||||
export * from "./query.js"
|
||||
export * from "./checkout.js"
|
||||
export * from "./publishable-api-key.js"
|
||||
export * from "./logger.js"
|
||||
export * from "./event-bus.js"
|
||||
export * from "./locking.js"
|
||||
export * from "./product-collection.js"
|
||||
export * from "./step.js"
|
||||
export * from "./example.js"
|
||||
export * from "./customer.js"
|
||||
export * from "./locking.js"
|
||||
export * from "./remote-query.js"
|
||||
export * from "./link.js"
|
||||
export * from "./event-bus.js"
|
||||
export * from "./logger.js"
|
||||
export * from "./file.js"
|
||||
export * from "./js-sdk.js"
|
||||
export * from "./admin.js"
|
||||
|
||||
@@ -42,5 +42,13 @@ export const locking = [
|
||||
{
|
||||
"title": "createReservationsWorkflow",
|
||||
"path": "https://docs.medusajs.com/resources/references/medusa-workflows/createReservationsWorkflow"
|
||||
},
|
||||
{
|
||||
"title": "Use Locking Module",
|
||||
"path": "https://docs.medusajs.com/resources/references/locking-service"
|
||||
},
|
||||
{
|
||||
"title": "Create Locking Provider",
|
||||
"path": "https://docs.medusajs.com/resources/references/locking-module-provider"
|
||||
}
|
||||
]
|
||||
@@ -63,6 +63,14 @@ export const server = [
|
||||
"title": "Create Fulfillment Provider",
|
||||
"path": "https://docs.medusajs.com/resources/references/fulfillment/provider"
|
||||
},
|
||||
{
|
||||
"title": "Use Locking Module",
|
||||
"path": "https://docs.medusajs.com/resources/references/locking-service"
|
||||
},
|
||||
{
|
||||
"title": "Create Locking Provider",
|
||||
"path": "https://docs.medusajs.com/resources/references/locking-module-provider"
|
||||
},
|
||||
{
|
||||
"title": "Create Notification Provider",
|
||||
"path": "https://docs.medusajs.com/resources/references/notification-provider-module"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,11 @@ const customOptions: Record<string, Partial<TypeDocOptions>> = {
|
||||
name: "auth-provider",
|
||||
parentIgnore: true,
|
||||
}),
|
||||
locking: getOptions({
|
||||
entryPointPath: "packages/core/types/src/locking/index.ts",
|
||||
tsConfigName: "types.json",
|
||||
name: "locking",
|
||||
}),
|
||||
dml: getOptions({
|
||||
entryPointPath: [
|
||||
"packages/core/utils/src/dml/entity-builder.ts",
|
||||
|
||||
+15
-10
@@ -6,14 +6,14 @@ const authProviderOptions: FormattingOptionsType = {
|
||||
reflectionGroups: {
|
||||
Constructors: false,
|
||||
},
|
||||
reflectionDescription: `In this document, you’ll learn how to create an auth provider module and the methods you must implement in its main service.`,
|
||||
reflectionDescription: `In this document, you’ll learn how to create an Auth Module Provider and the methods you must implement in its main service.`,
|
||||
frontmatterData: {
|
||||
slug: "/references/auth/provider",
|
||||
tags: ["auth", "server", "how to"],
|
||||
sidebar_label: "Create Auth Provider",
|
||||
},
|
||||
reflectionTitle: {
|
||||
fullReplacement: "How to Create an Auth Provider Module",
|
||||
fullReplacement: "How to Create an Auth Module Provider",
|
||||
},
|
||||
shouldIncrementAfterStartSections: true,
|
||||
expandMembers: true,
|
||||
@@ -24,6 +24,11 @@ const authProviderOptions: FormattingOptionsType = {
|
||||
reflection_typeParameters: false,
|
||||
},
|
||||
startSections: [
|
||||
`## Implementation Example
|
||||
|
||||
As you implement your Auth Module Provider, it can be useful to refer to an existing provider and how it's implemeted.
|
||||
|
||||
If you need to refer to an existing implementation as an example, check the [Google Auth Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/auth-google).`,
|
||||
`## 1. Create Module Provider Directory
|
||||
|
||||
Start by creating a new directory for your module provider.
|
||||
@@ -37,9 +42,9 @@ If you're creating the module provider in a plugin, create it under the \`src/pr
|
||||
The rest of this guide always uses the \`src/modules/my-auth\` directory as an example.
|
||||
|
||||
</Note>`,
|
||||
`## 2. Create the Auth Provider Service
|
||||
`## 2. Create the Auth Module Provider's Service
|
||||
|
||||
Create the file \`src/modules/my-auth/service.ts\` that holds the module's main service. It must extend the \`AbstractAuthModuleProvider\` class imported from \`@medusajs/framework/utils\`:
|
||||
Create the file \`src/modules/my-auth/service.ts\` that holds the module provider's main service. It must extend the \`AbstractAuthModuleProvider\` class imported from \`@medusajs/framework/utils\`:
|
||||
|
||||
\`\`\`ts title="src/modules/my-auth/service.ts"
|
||||
import { AbstractAuthModuleProvider } from "@medusajs/framework/utils"
|
||||
@@ -52,7 +57,7 @@ export default MyAuthProviderService
|
||||
\`\`\``,
|
||||
],
|
||||
endSections: [
|
||||
`## 3. Create Module Definition File
|
||||
`## 3. Create Module Provider Definition File
|
||||
|
||||
Create the file \`src/modules/my-auth/index.ts\` with the following content:
|
||||
|
||||
@@ -68,8 +73,8 @@ export default ModuleProvider(Modules.AUTH, {
|
||||
})
|
||||
\`\`\`
|
||||
|
||||
This exports the module's definition, indicating that the \`MyAuthProviderService\` is the module's service.`,
|
||||
`## 4. Use Module
|
||||
This exports the module provider's definition, indicating that the \`MyAuthProviderService\` is the module provider's service.`,
|
||||
`## 4. Use Module Provider
|
||||
|
||||
To use your Auth Module Provider, add it to the \`providers\` array of the Auth Module in \`medusa-config.ts\`:
|
||||
|
||||
@@ -107,9 +112,9 @@ module.exports = defineConfig({
|
||||
`,
|
||||
`## 5. Test it Out
|
||||
|
||||
To test out your authentication provider, use any of the [Authentication Routes](https://docs.medusajs.com/v2/resources/commerce-modules/auth/authentication-route), using your provider's ID as a path parameter.
|
||||
To test out your Authentication Module Provider, use any of the [Authentication Routes](https://docs.medusajs.com/v2/resources/commerce-modules/auth/authentication-route), using your provider's ID as a path parameter.
|
||||
|
||||
For example, to get a registration token for an admin user, send a \`POST\` request to \`/auth/user/my-auth/register\` replacing \`my-auth\` with your authentication provider's ID:
|
||||
For example, to get a registration token for an admin user, send a \`POST\` request to \`/auth/user/my-auth/register\` replacing \`my-auth\` with your Authentication Module Provider's ID:
|
||||
|
||||
\`\`\`bash
|
||||
curl -X POST http://localhost:9000/auth/user/my-auth/register
|
||||
@@ -120,7 +125,7 @@ curl -X POST http://localhost:9000/auth/user/my-auth/register
|
||||
}'
|
||||
\`\`\`
|
||||
|
||||
Change the request body to pass the data required for your authentication provider to register the user.
|
||||
Change the request body to pass the data required for your Authentication Module Provider to register the user.
|
||||
|
||||
If registration is successful, the response will have a \`token\` property.
|
||||
`,
|
||||
|
||||
+13
-8
@@ -6,12 +6,12 @@ const fileOptions: FormattingOptionsType = {
|
||||
reflectionGroups: {
|
||||
Constructors: false,
|
||||
},
|
||||
reflectionDescription: `In this document, you’ll learn how to create a file provider module and the methods you must implement in its main service.`,
|
||||
reflectionDescription: `In this document, you’ll learn how to create a File Module Provider and the methods you must implement in its main service.`,
|
||||
frontmatterData: {
|
||||
slug: "/references/file-provider-module",
|
||||
},
|
||||
reflectionTitle: {
|
||||
fullReplacement: "How to Create a File Provider Module",
|
||||
fullReplacement: "How to Create a File Module Provider",
|
||||
},
|
||||
shouldIncrementAfterStartSections: true,
|
||||
expandMembers: true,
|
||||
@@ -22,6 +22,11 @@ const fileOptions: FormattingOptionsType = {
|
||||
reflection_typeParameters: false,
|
||||
},
|
||||
startSections: [
|
||||
`## Implementation Example
|
||||
|
||||
As you implement your File Module Provider, it can be useful to refer to an existing provider and how it's implemeted.
|
||||
|
||||
If you need to refer to an existing implementation as an example, check the [S3 File Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/file-s3).`,
|
||||
`## Create Module Provider Directory
|
||||
|
||||
Start by creating a new directory for your module provider.
|
||||
@@ -35,9 +40,9 @@ If you're creating the module provider in a plugin, create it under the \`src/pr
|
||||
The rest of this guide always uses the \`src/modules/my-file\` directory as an example.
|
||||
|
||||
</Note>`,
|
||||
`## 2. Create the File Provider Service
|
||||
`## 2. Create the File Module Provider's Service
|
||||
|
||||
Create the file \`src/modules/my-file/service.ts\` that holds the implementation of the module's main service. It must extend the \`AbstractFileProviderService\` class imported from \`@medusajs/framework/utils\`:
|
||||
Create the file \`src/modules/my-file/service.ts\` that holds the implementation of the module provider's main service. It must extend the \`AbstractFileProviderService\` class imported from \`@medusajs/framework/utils\`:
|
||||
|
||||
\`\`\`ts title="src/modules/my-file/service.ts"
|
||||
import { AbstractFileProviderService } from "@medusajs/framework/utils"
|
||||
@@ -50,7 +55,7 @@ export default MyFileProviderService
|
||||
\`\`\``,
|
||||
],
|
||||
endSections: [
|
||||
`## 3. Create Module Definition File
|
||||
`## 3. Create Module Provider Definition File
|
||||
|
||||
Create the file \`src/modules/my-file/index.ts\` with the following content:
|
||||
|
||||
@@ -66,8 +71,8 @@ export default ModuleProvider(Modules.FILE, {
|
||||
})
|
||||
\`\`\`
|
||||
|
||||
This exports the module's definition, indicating that the \`MyFileProviderService\` is the module's service.`,
|
||||
`## 4. Use Module
|
||||
This exports the module provider's definition, indicating that the \`MyFileProviderService\` is the module provider's service.`,
|
||||
`## 4. Use Module Provider
|
||||
|
||||
To use your File Module Provider, add it to the \`providers\` array of the File Module in \`medusa-config.ts\`:
|
||||
|
||||
@@ -107,7 +112,7 @@ module.exports = defineConfig({
|
||||
`,
|
||||
`## 5. Test it Out
|
||||
|
||||
To test out your file provider, use the Medusa Admin or the [Upload API route](https://docs.medusajs.com/v2/api/admin#uploads_postuploads) to upload a file.
|
||||
To test out your File Module Provider, use the Medusa Admin or the [Upload API route](https://docs.medusajs.com/v2/api/admin#uploads_postuploads) to upload a file.
|
||||
`,
|
||||
],
|
||||
},
|
||||
|
||||
+12
-12
@@ -6,14 +6,14 @@ const fulfillmentProviderOptions: FormattingOptionsType = {
|
||||
reflectionGroups: {
|
||||
Constructors: false,
|
||||
},
|
||||
reflectionDescription: `In this document, you’ll learn how to create a fulfillment provider module and the methods you must implement in its main service.`,
|
||||
reflectionDescription: `In this document, you’ll learn how to create a Fulfillment Module Provider and the methods you must implement in its main service.`,
|
||||
frontmatterData: {
|
||||
slug: "/references/fulfillment/provider",
|
||||
tags: ["fulfillment", "server", "how to"],
|
||||
sidebar_label: "Create Fulfillment Provider",
|
||||
},
|
||||
reflectionTitle: {
|
||||
fullReplacement: "How to Create a Fulfillment Provider Module",
|
||||
fullReplacement: "How to Create a Fulfillment Module Provider",
|
||||
},
|
||||
shouldIncrementAfterStartSections: true,
|
||||
expandMembers: true,
|
||||
@@ -24,11 +24,11 @@ const fulfillmentProviderOptions: FormattingOptionsType = {
|
||||
reflection_typeParameters: false,
|
||||
},
|
||||
startSections: [
|
||||
`## Understanding Fulfillment Provider Implementation
|
||||
`## Understanding Fulfillment Module Provider Implementation
|
||||
|
||||
The Fulfillment Module Provider handles processing fulfillments and shipments with a third-party provirder. However, it's not responsible for managing fulfillment concepts within Medusa, such as creating a fulfillment or its shipments. The Fulfillment Module uses your fulfillment provider within core operations.
|
||||
The Fulfillment Module Provider handles processing fulfillments and shipments with a third-party provirder. However, it's not responsible for managing fulfillment concepts within Medusa, such as creating a fulfillment or its shipments. The Fulfillment Module uses your Fulfillment Module Provider within core operations.
|
||||
|
||||
For example, when the merchant creates a fulfillment for an order, the Fulfillment Module uses your fulfillment provider to create the fulfillment in the third-party system, then creates the fulfillment in Medusa. So, you only have to implement the third-party fulfillment processing logic in your fulfillment provider.
|
||||
For example, when the merchant creates a fulfillment for an order, the Fulfillment Module uses your Fulfillment Module Provider to create the fulfillment in the third-party system, then creates the fulfillment in Medusa. So, you only have to implement the third-party fulfillment processing logic in your Fulfillment Module Provider.
|
||||
`,
|
||||
`## 1. Create Module Provider Directory
|
||||
|
||||
@@ -43,9 +43,9 @@ If you're creating the module provider in a plugin, create it under the \`src/pr
|
||||
The rest of this guide always uses the \`src/modules/my-fulfillment\` directory as an example.
|
||||
|
||||
</Note>`,
|
||||
`## 2. Create the Fulfillment Provider Service
|
||||
`## 2. Create the Fulfillment Module Provider Service
|
||||
|
||||
Create the file \`src/modules/my-fulfillment/service.ts\` that holds the module's main service. It must extend the \`AbstractFulfillmentProviderService\` class imported from \`@medusajs/framework/utils\`:
|
||||
Create the file \`src/modules/my-fulfillment/service.ts\` that holds the module provider's main service. It must extend the \`AbstractFulfillmentProviderService\` class imported from \`@medusajs/framework/utils\`:
|
||||
|
||||
\`\`\`ts title="src/modules/my-fulfillment/service.ts"
|
||||
import { AbstractFulfillmentProviderService } from "@medusajs/framework/utils"
|
||||
@@ -58,7 +58,7 @@ export default MyFulfillmentProviderService
|
||||
\`\`\``,
|
||||
],
|
||||
endSections: [
|
||||
`## 3. Create Module Definition File
|
||||
`## 3. Create Module Provider Definition File
|
||||
|
||||
Create the file \`src/modules/my-fulfillment/index.ts\` with the following content:
|
||||
|
||||
@@ -74,8 +74,8 @@ export default ModuleProvider(Modules.FULFILLMENT, {
|
||||
})
|
||||
\`\`\`
|
||||
|
||||
This exports the module's definition, indicating that the \`MyFulfillmentProviderService\` is the module's service.`,
|
||||
`## 4. Use Module
|
||||
This exports the module provider's definition, indicating that the \`MyFulfillmentProviderService\` is the module provider's service.`,
|
||||
`## 4. Use Module Provider
|
||||
|
||||
To use your Fulfillment Module Provider, add it to the \`providers\` array of the Fulfillment Module in \`medusa-config.ts\`:
|
||||
|
||||
@@ -109,9 +109,9 @@ module.exports = defineConfig({
|
||||
`,
|
||||
`## 5. Test it Out
|
||||
|
||||
Before you use your fulfillment provider, in the Medusa Admin:
|
||||
Before you use your Fulfillment Module Provider, in the Medusa Admin:
|
||||
|
||||
1. Add the fulfillment provider to a location.
|
||||
1. Add the Fulfillment Module Provider to a location.
|
||||
2. Add in the location a delivery shipping option that uses the provider.
|
||||
|
||||
Then, place an order, choosing the shipping option you created during checkout, and create a fulfillment in the Medusa Admin. The fulfillment is created using your provider.
|
||||
|
||||
+2
@@ -13,6 +13,7 @@ import workflowsOptions from "./workflows.js"
|
||||
import dmlOptions from "./dml.js"
|
||||
import coreFlowsOptions from "./core-flows.js"
|
||||
import jsSdkOptions from "./js-sdk.js"
|
||||
import lockingOptions from "./locking.js"
|
||||
|
||||
const mergerCustomOptions: FormattingOptionsType = {
|
||||
...authProviderOptions,
|
||||
@@ -22,6 +23,7 @@ const mergerCustomOptions: FormattingOptionsType = {
|
||||
...fulfillmentProviderOptions,
|
||||
...helperStepsOptions,
|
||||
...jsSdkOptions,
|
||||
...lockingOptions,
|
||||
...medusaConfigOptions,
|
||||
...medusaOptions,
|
||||
...notificationOptions,
|
||||
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
import { FormattingOptionsType } from "types"
|
||||
import baseSectionsOptions from "../base-section-options.js"
|
||||
|
||||
const lockingOptions: FormattingOptionsType = {
|
||||
"^locking/.*ILockingProvider": {
|
||||
reflectionGroups: {
|
||||
Constructors: false,
|
||||
},
|
||||
reflectionDescription: `In this document, you’ll learn how to create a Locking Module Provider and the methods you must implement in its main service.`,
|
||||
frontmatterData: {
|
||||
slug: "/references/locking-module-provider",
|
||||
tags: ["locking", "server", "how to"],
|
||||
sidebar_label: "Create Locking Provider",
|
||||
},
|
||||
reflectionTitle: {
|
||||
fullReplacement: "How to Create a Locking Module Provider",
|
||||
},
|
||||
shouldIncrementAfterStartSections: true,
|
||||
expandMembers: true,
|
||||
expandProperties: true,
|
||||
sections: {
|
||||
...baseSectionsOptions,
|
||||
member_declaration_title: false,
|
||||
reflection_typeParameters: false,
|
||||
},
|
||||
startSections: [
|
||||
`## Implementation Example
|
||||
|
||||
As you implement your Locking Module Provider, it can be useful to refer to an existing provider and how it's implemeted.
|
||||
|
||||
If you need to refer to an existing implementation as an example, check the [Redis Locking Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/locking-redis).`,
|
||||
`## 1. Create Module Provider Directory
|
||||
|
||||
Start by creating a new directory for your module provider.
|
||||
|
||||
If you're creating the module provider in a Medusa application, create it under the \`src/modules\` directory. For example, \`src/modules/my-locking\`.
|
||||
|
||||
If you're creating the module provider in a plugin, create it under the \`src/providers\` directory. For example, \`src/providers/my-locking\`.
|
||||
|
||||
<Note>
|
||||
|
||||
The rest of this guide always uses the \`src/modules/my-locking\` directory as an example.
|
||||
|
||||
</Note>`,
|
||||
`## 2. Create the Locking Module Provider Service
|
||||
|
||||
Create the file \`src/modules/my-locking/service.ts\` that holds the module provider's main service. It must implement the \`ILockingProvider\` interface imported from \`@medusajs/framework/types\`:
|
||||
|
||||
\`\`\`ts title="src/modules/my-locking/service.ts"
|
||||
import { ILockingProvider } from "@medusajs/framework/types"
|
||||
|
||||
type Options = {
|
||||
url: string
|
||||
}
|
||||
|
||||
class MyLockingProviderService implements ILockingProvider {
|
||||
// TODO implement methods
|
||||
}
|
||||
|
||||
export default MyLockingProviderService
|
||||
\`\`\``,
|
||||
],
|
||||
endSections: [
|
||||
`## 3. Create Module Definition File
|
||||
|
||||
Create the file \`src/modules/my-locking/index.ts\` with the following content:
|
||||
|
||||
\`\`\`ts title="src/modules/my-locking/index.ts"
|
||||
import { ModuleProvider, Modules } from "@medusajs/framework/utils"
|
||||
import MyLockingProviderService from "./service"
|
||||
|
||||
export default ModuleProvider(Modules.LOCKING, {
|
||||
services: [MyLockingProviderService],
|
||||
})
|
||||
\`\`\`
|
||||
|
||||
This exports the module provider's definition, indicating that the \`MyLockingProviderService\` is the module provider's service.`,
|
||||
`## 4. Use Module Provider
|
||||
|
||||
To use your Locking Module Provider, add it to the \`providers\` array of the Locking Module in \`medusa-config.ts\`:
|
||||
|
||||
\`\`\`ts title="medusa-config.ts"
|
||||
module.exports = defineConfig({
|
||||
// ...
|
||||
modules: [
|
||||
{
|
||||
resolve: "@medusajs/medusa/payment",
|
||||
options: {
|
||||
providers: [
|
||||
{
|
||||
// if module provider is in a plugin, use \`plugin-name/providers/my-locking\`
|
||||
resolve: "./src/modules/my-locking",
|
||||
id: "my-lock",
|
||||
// set this if you want this provider to be used by default
|
||||
// and you have other Locking Module Providers registered.
|
||||
is_default: true,
|
||||
options: {
|
||||
url: "http://example.com",
|
||||
// provider options...
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
\`\`\`
|
||||
`,
|
||||
`## 5. Test it Out
|
||||
|
||||
When you start the Medusa application, if your Locking Module Provider is the only registered provider without enabling \`is_default\`, you'll see the following message:
|
||||
|
||||
\`\`\`bash
|
||||
info: Locking module: Using "my-lock" as default.
|
||||
\`\`\`
|
||||
|
||||
This indicates that your Locking Module Provider is being used as the default provider.
|
||||
|
||||
The Locking Module will now use your provider to handle all locking operations.
|
||||
`,
|
||||
`## Useful Guides
|
||||
|
||||
- [How to Use Locking Module](/references/locking-service)
|
||||
`,
|
||||
],
|
||||
},
|
||||
"^locking/.*ILockingModule": {
|
||||
reflectionGroups: {
|
||||
Constructors: false,
|
||||
},
|
||||
reflectionDescription: `In this document, you’ll learn about the different methods in the Locking Module's service and how to use them.`,
|
||||
frontmatterData: {
|
||||
slug: "/references/locking-service",
|
||||
tags: ["locking", "server", "how to"],
|
||||
sidebar_label: "Use Locking Module",
|
||||
},
|
||||
reflectionTitle: {
|
||||
fullReplacement: "How to Use Locking Module",
|
||||
},
|
||||
expandMembers: true,
|
||||
startSections: [
|
||||
`## Resolve Locking Module's Service
|
||||
|
||||
In your workflow's step, you can resolve the Locking Module's service from the Medusa container:
|
||||
|
||||
\`\`\`ts
|
||||
import { Modules } from "@medusajs/framework/utils"
|
||||
import { createStep } from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
const step1 = createStep(
|
||||
"step-1",
|
||||
async ({}, { container }) => {
|
||||
const lockingModuleService = container.resolve(
|
||||
Modules.LOCKING
|
||||
)
|
||||
|
||||
// TODO use lockingModuleService
|
||||
}
|
||||
)
|
||||
\`\`\`
|
||||
|
||||
You can then use the Locking Module's service's methods in the step. The rest of this guide details these methods.
|
||||
|
||||
---
|
||||
`,
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export default lockingOptions
|
||||
+12
-7
@@ -6,14 +6,14 @@ const notificationOptions: FormattingOptionsType = {
|
||||
reflectionGroups: {
|
||||
Constructors: false,
|
||||
},
|
||||
reflectionDescription: `In this document, you’ll learn how to create a notification provider module and the methods you must implement in it.`,
|
||||
reflectionDescription: `In this document, you’ll learn how to create a Notification Module Provider and the methods you must implement in it.`,
|
||||
frontmatterData: {
|
||||
slug: "/references/notification-provider-module",
|
||||
tags: ["notification", "server", "how to"],
|
||||
sidebar_label: "Create Notification Provider",
|
||||
},
|
||||
reflectionTitle: {
|
||||
fullReplacement: "How to Create a Notification Provider Module",
|
||||
fullReplacement: "How to Create a Notification Module Provider",
|
||||
},
|
||||
shouldIncrementAfterStartSections: true,
|
||||
expandMembers: true,
|
||||
@@ -24,6 +24,11 @@ const notificationOptions: FormattingOptionsType = {
|
||||
reflection_typeParameters: false,
|
||||
},
|
||||
startSections: [
|
||||
`## Implementation Example
|
||||
|
||||
As you implement your Notification Module Provider, it can be useful to refer to an existing provider and how it's implemeted.
|
||||
|
||||
If you need to refer to an existing implementation as an example, check the [SendGrid Notification Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/notification-sendgrid).`,
|
||||
`## 1. Create Module Provider Directory
|
||||
|
||||
Start by creating a new directory for your module provider.
|
||||
@@ -37,11 +42,11 @@ If you're creating the module provider in a plugin, create it under the \`src/pr
|
||||
The rest of this guide always uses the \`src/modules/my-notification\` directory as an example.
|
||||
|
||||
</Note>`,
|
||||
`## 2. Create the Notification Provider Service
|
||||
`## 2. Create the Notification Module Provider's Service
|
||||
|
||||
Create the file \`src/modules/my-notification/service.ts\` that holds the implementation of the notification service.
|
||||
|
||||
The Notification Provider Module's main service must extend the \`AbstractNotificationProviderService\` class imported from \`@medusajs/framework/utils\`:
|
||||
The Notification Module Provider's main service must extend the \`AbstractNotificationProviderService\` class imported from \`@medusajs/framework/utils\`:
|
||||
|
||||
\`\`\`ts title="src/modules/my-notification/service.ts"
|
||||
import {
|
||||
@@ -56,7 +61,7 @@ export default MyNotificationProviderService
|
||||
\`\`\``,
|
||||
],
|
||||
endSections: [
|
||||
`## 3. Create Module Definition File
|
||||
`## 3. Create Module Provider Definition File
|
||||
|
||||
Create the file \`src/modules/my-notification/index.ts\` with the following content:
|
||||
|
||||
@@ -72,8 +77,8 @@ export default ModuleProvider(Modules.NOTIFICATION, {
|
||||
})
|
||||
\`\`\`
|
||||
|
||||
This exports the module's definition, indicating that the \`MyNotificationProviderService\` is the module's service.`,
|
||||
`## 4. Use Module
|
||||
This exports the module provider's definition, indicating that the \`MyNotificationProviderService\` is the module provider's service.`,
|
||||
`## 4. Use Module Provider
|
||||
|
||||
To use your Notification Module Provider, add it to the \`providers\` array of the Notification Module in \`medusa-config.ts\`:
|
||||
|
||||
|
||||
+18
-13
@@ -6,14 +6,14 @@ const paymentProviderOptions: FormattingOptionsType = {
|
||||
maxLevel: 2,
|
||||
},
|
||||
"^payment_provider/.*AbstractPaymentProvider": {
|
||||
reflectionDescription: `In this document, you’ll learn how to create a Payment Provider to be used with the Payment Module.`,
|
||||
reflectionDescription: `In this document, you’ll learn how to create a Payment Module Provider to be used with the Payment Module.`,
|
||||
frontmatterData: {
|
||||
slug: "/references/payment/provider",
|
||||
tags: ["payment", "server", "how to"],
|
||||
sidebar_label: "Create Payment Provider",
|
||||
},
|
||||
reflectionTitle: {
|
||||
fullReplacement: "How to Create a Payment Provider",
|
||||
fullReplacement: "How to Create a Payment Module Provider",
|
||||
},
|
||||
shouldIncrementAfterStartSections: true,
|
||||
expandMembers: true,
|
||||
@@ -24,11 +24,16 @@ const paymentProviderOptions: FormattingOptionsType = {
|
||||
reflection_typeParameters: false,
|
||||
},
|
||||
startSections: [
|
||||
`## Understanding Payment Provider Implementation
|
||||
`## Implementation Example
|
||||
|
||||
As you implement your Payment Module Provider, it can be useful to refer to an existing provider and how it's implemeted.
|
||||
|
||||
The Payment Module Provider handles processing payment with a third-party provirder. However, it's not responsible for managing payment concepts within Medusa, such as payment sessions or collections. These concepts are handled by the Payment Module which uses your payment provider within core operations.
|
||||
If you need to refer to an existing implementation as an example, check the [Stripe Payment Module Provider in the Medusa repository](https://github.com/medusajs/medusa/tree/develop/packages/modules/providers/payment-stripe).`,
|
||||
`## Understanding Payment Module Provider Implementation
|
||||
|
||||
For example, when the merchant captures an order's payment, the Payment Module uses the payment provider to capture the payment, the makes updates to the \`Payment\` record associated with the order. So, you only have to implement the third-party payment processing logic in your payment provider.
|
||||
The Payment Module Provider handles processing payment with a third-party provirder. However, it's not responsible for managing payment concepts within Medusa, such as payment sessions or collections. These concepts are handled by the Payment Module which uses your Payment Module Provider within core operations.
|
||||
|
||||
For example, when the merchant captures an order's payment, the Payment Module uses the Payment Module Provider to capture the payment, the makes updates to the \`Payment\` record associated with the order. So, you only have to implement the third-party payment processing logic in your Payment Module Provider.
|
||||
`,
|
||||
`## 1. Create Module Provider Directory
|
||||
|
||||
@@ -43,9 +48,9 @@ If you're creating the module provider in a plugin, create it under the \`src/pr
|
||||
The rest of this guide always uses the \`src/modules/my-payment\` directory as an example.
|
||||
|
||||
</Note>`,
|
||||
`## 2. Create the Payment Provider Service
|
||||
`## 2. Create the Payment Module Provider's Service
|
||||
|
||||
Create the file \`src/modules/my-payment/service.ts\` that holds the module's main service. It must extend the \`AbstractPaymentProvider\` class imported from \`@medusajs/framework/utils\`:
|
||||
Create the file \`src/modules/my-payment/service.ts\` that holds the module provider's main service. It must extend the \`AbstractPaymentProvider\` class imported from \`@medusajs/framework/utils\`:
|
||||
|
||||
\`\`\`ts title="src/modules/my-payment/service.ts"
|
||||
import { AbstractPaymentProvider } from "@medusajs/framework/utils"
|
||||
@@ -64,7 +69,7 @@ export default MyPaymentProviderService
|
||||
\`\`\``,
|
||||
],
|
||||
endSections: [
|
||||
`## 3. Create Module Definition File
|
||||
`## 3. Create Module Provider Definition File
|
||||
|
||||
Create the file \`src/modules/my-payment/index.ts\` with the following content:
|
||||
|
||||
@@ -80,8 +85,8 @@ export default ModuleProvider(Modules.PAYMENT, {
|
||||
})
|
||||
\`\`\`
|
||||
|
||||
This exports the module's definition, indicating that the \`MyPaymentProviderService\` is the module's service.`,
|
||||
`## 4. Use Module
|
||||
This exports the module provider's definition, indicating that the \`MyPaymentProviderService\` is the module provider's service.`,
|
||||
`## 4. Use Module Provider
|
||||
|
||||
To use your Payment Module Provider, add it to the \`providers\` array of the Payment Module in \`medusa-config.ts\`:
|
||||
|
||||
@@ -111,13 +116,13 @@ module.exports = defineConfig({
|
||||
`,
|
||||
`## 5. Test it Out
|
||||
|
||||
Before you use your payment provider, enable it in a region using the Medusa Admin.
|
||||
Before you use your Payment Module Provider, enable it in a region using the Medusa Admin.
|
||||
|
||||
Then, go through checkout to place an order. Your payment provider is used to authorize the payment.
|
||||
Then, go through checkout to place an order. Your Payment Module Provider is used to authorize the payment.
|
||||
`,
|
||||
`## Useful Guides
|
||||
|
||||
- [Storefront Guide: how to implement UI for your payment provider during checkout](https://docs.medusajs.com/resources/storefront-development/checkout/payment)
|
||||
- [Storefront Guide: how to implement UI for your Payment Module Provider during checkout](https://docs.medusajs.com/resources/storefront-development/checkout/payment)
|
||||
`,
|
||||
],
|
||||
},
|
||||
|
||||
+11
-11
@@ -5,14 +5,14 @@ const taxProviderOptions: FormattingOptionsType = {
|
||||
reflectionGroups: {
|
||||
Properties: false,
|
||||
},
|
||||
reflectionDescription: `In this document, you’ll learn how to create a tax provider to use with the Tax Module, and the methods to implement.`,
|
||||
reflectionDescription: `In this document, you’ll learn how to create a Tax Module Provider to use with the Tax Module, and the methods to implement.`,
|
||||
frontmatterData: {
|
||||
slug: "/references/tax/provider",
|
||||
tags: ["tax", "server", "how to"],
|
||||
sidebar_label: "Create Tax Provider",
|
||||
},
|
||||
reflectionTitle: {
|
||||
fullReplacement: "How to Create a Tax Provider",
|
||||
fullReplacement: "How to Create a Tax Module Provider",
|
||||
},
|
||||
shouldIncrementAfterStartSections: true,
|
||||
expandMembers: true,
|
||||
@@ -20,12 +20,12 @@ const taxProviderOptions: FormattingOptionsType = {
|
||||
startSections: [
|
||||
`## Overview
|
||||
|
||||
A tax provider is used to retrieve the tax lines in a provided context. The Tax Module provides a default \`system\` provider. You can create your own tax provider, either in a plugin, in a module provider, or directly in your Medusa application's codebase, then use it in any tax region.`,
|
||||
`## Understanding Tax Provider Implementation
|
||||
A Tax Module Provider is used to retrieve the tax lines in a provided context. The Tax Module provides a default \`system\` provider. You can create your own Tax Module Provider, either in a plugin, in a module provider, or directly in your Medusa application's codebase, then use it in any tax region.`,
|
||||
`## Understanding Tax Module Provider Implementation
|
||||
|
||||
The Tax Module Provider handles calculating taxes with a third-party provirder. However, it's not responsible for managing tax concepts within Medusa, such as creating a tax region. The Tax Module uses your tax provider within core operations.
|
||||
The Tax Module Provider handles calculating taxes with a third-party provirder. However, it's not responsible for managing tax concepts within Medusa, such as creating a tax region. The Tax Module uses your Tax Module Provider within core operations.
|
||||
|
||||
For example, during checkout, the tax provider of the tax region that the customer is in is used to calculate the tax for the cart and order. So, you only have to implement the third-party tax calculation logic in your tax provider.`,
|
||||
For example, during checkout, the Tax Module Provider of the tax region that the customer is in is used to calculate the tax for the cart and order. So, you only have to implement the third-party tax calculation logic in your Tax Module Provider.`,
|
||||
`## 1. Create Module Provider Directory
|
||||
|
||||
Start by creating a new directory for your module provider.
|
||||
@@ -39,9 +39,9 @@ If you're creating the module provider in a plugin, create it under the \`src/pr
|
||||
The rest of this guide always uses the \`src/modules/my-tax\` directory as an example.
|
||||
|
||||
</Note>`,
|
||||
`## 2. Create the Tax Provider Service
|
||||
`## 2. Create the Tax Module Provider's Service
|
||||
|
||||
Create the file \`src/modules/my-tax/service.ts\` that holds the module's main service. It must extend the \`ITaxProvider\` class imported from \`@medusajs/framework/types\`:
|
||||
Create the file \`src/modules/my-tax/service.ts\` that holds the module provider's main service. It must extend the \`ITaxProvider\` class imported from \`@medusajs/framework/types\`:
|
||||
|
||||
\`\`\`ts title="src/modules/my-tax/service.ts"
|
||||
import { ITaxProvider } from "@medusajs/framework/types"
|
||||
@@ -52,7 +52,7 @@ export default class MyTaxProvider implements ITaxProvider {
|
||||
\`\`\``,
|
||||
],
|
||||
endSections: [
|
||||
`## 3. Create Module Definition File
|
||||
`## 3. Create Module Provider Definition File
|
||||
|
||||
Create the file \`src/modules/my-tax/index.ts\` with the following content:
|
||||
|
||||
@@ -68,8 +68,8 @@ export default ModuleProvider(Modules.TAX, {
|
||||
})
|
||||
\`\`\`
|
||||
|
||||
This exports the module's definition, indicating that the \`MyTaxProvider\` is the module's service.`,
|
||||
`## 4. Use Module
|
||||
This exports the module provider's definition, indicating that the \`MyTaxProvider\` is the module provider's service.`,
|
||||
`## 4. Use Module Provider
|
||||
|
||||
To use your Tax Module Provider, add it to the \`providers\` array of the Tax Module in \`medusa-config.ts\`:
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ const allReferences = [
|
||||
"types",
|
||||
"utils",
|
||||
"workflows",
|
||||
"locking",
|
||||
]
|
||||
|
||||
export default allReferences
|
||||
|
||||
Reference in New Issue
Block a user