docs: documentation for v1.18 (#5652)

* docs: documentation for v.17.5

* fix links

* updated version number
This commit is contained in:
Shahed Nasser authored and GitHub committed 2023-11-21 08:57:11 +00:00
1 parent adc60e519c
commit 9c7f95c3d5
36 files changed
+1499 -1336

No files matched your search

@@ -106,74 +106,63 @@ Medusa also provides official notification plugins that integrate with third-par
Here’s an example of a subscriber that uses the SendGrid plugin to send an email to the customer when the order has a new note:
```ts title=src/subscribers/new-note.ts
import {
EventBusService,
import {
type SubscriberConfig,
type SubscriberArgs,
ProductVariantService,
NoteService,
OrderService,
} from "@medusajs/medusa"
type InjectedDependencies = {
eventBusService: EventBusService
sendgridService: any
noteService: NoteService
orderService: OrderService
}
export default async function handleNoteCreated({
data, eventName, container, pluginOptions,
}: SubscriberArgs<Record<string, string>>) {
const noteService: NoteService = container.resolve(
"noteService"
)
const orderService: OrderService = container.resolve(
"orderService"
)
const sendgridService = container.resolve("sendgridService")
class NewNoteSubscriber {
protected sendGridService_: any
protected noteService_: NoteService
protected orderService_: OrderService
// retrieve note by id
const note = await noteService.retrieve(data.id, {
relations: ["author"],
})
constructor({
eventBusService,
sendgridService,
noteService,
orderService,
}: InjectedDependencies) {
this.noteService_ = noteService
this.orderService_ = orderService
this.sendGridService_ = sendgridService
eventBusService.subscribe(
"note.created",
this.handleNoteCreated
)
if (!note || note.resource_type !== "order") {
return
}
handleNoteCreated = async (data) => {
// retrieve note by id
const note = await this.noteService_.retrieve(data.id, {
relations: ["author"],
})
// retrieve note's order
const order = await orderService.retrieve(
note.resource_id
)
if (!note || note.resource_type !== "order") {
return
}
// retrieve note's order
const order = await this.orderService_.retrieve(
note.resource_id
)
if (!order) {
return
}
this.sendGridService_.sendEmail({
templateId: "order-update",
from: "hello@medusajs.com",
to: order.email,
dynamic_template_data: {
// any data necessary for your template...
note_text: note.value,
note_author: note.author.first_name,
note_date: note.created_at,
order_id: order.display_id,
},
})
if (!order) {
return
}
sendGridService.sendEmail({
templateId: "order-update",
from: "hello@medusajs.com",
to: order.email,
dynamic_template_data: {
// any data necessary for your template...
note_text: note.value,
note_author: note.author.first_name,
note_date: note.created_at,
order_id: order.display_id,
},
})
}
export default NewNoteSubscriber
export const config: SubscriberConfig = {
event: NoteService.Events.CREATED,
context: {
subscriberId: "note-created-handler",
},
}
```
</Details>
@@ -206,14 +195,14 @@ You can implement automatic synchronization in Medusa using scheduled jobs. A sc
Logger,
ProductService,
StoreService,
MedusaContainer,
} from "@medusajs/medusa"
import {
ProductSelector,
} from "@medusajs/medusa/dist/types/product"
import { AwilixContainer } from "awilix"
export default async (
container: AwilixContainer,
container: MedusaContainer,
config: Record<string, unknown>
): Promise<void> => {
const logger = container.resolve<Logger>("logger")
@@ -422,76 +411,65 @@ For example, if you're grouping customers with over twenty orders, you can use a
```ts title=src/subscribers/add-custom-to-vip.ts
import {
CustomerGroupService,
CustomerService,
EventBusService,
type SubscriberConfig,
type SubscriberArgs,
OrderService,
CustomerService,
CustomerGroupService,
} from "@medusajs/medusa"
type InjectedDependencies = {
orderService: OrderService
customerService: CustomerService
customerGroupService: CustomerGroupService
eventBusService: EventBusService
}
export default async function handleOrderPlaced({
data, eventName, container, pluginOptions,
}: SubscriberArgs<Record<string, string>>) {
const orderService: OrderService = container.resolve(
"orderService"
)
const customerService: CustomerService = container.resolve(
"customerService"
)
const customerGroupService: CustomerGroupService =
container.resolve("customerGroupService")
class AddCustomerToVipSubscriber {
protected orderService_: OrderService
protected customerService_: CustomerService
protected customerGroupService_: CustomerGroupService
// check if VIP group exists
const vipGroup = await customerGroupService.list({
name: "VIP",
}, {
relations: ["customers"],
})
if (!vipGroup.length) {
return
}
constructor({
orderService,
customerService,
customerGroupService,
eventBusService,
}: InjectedDependencies) {
this.orderService_ = orderService
this.customerService_ = customerService
this.customerGroupService_ = customerGroupService
eventBusService.subscribe(
"order.placed",
this.handleOrderPlaced
// retrieve order and its customer
const order = await orderService.retrieve(data.id)
if (!order || !order.customer_id ||
vipGroup[0].customers.find(
(customer) => customer.id === order.customer_id
) !== undefined) {
return
}
// retrieve orders of this customer
const [, count] = await orderService.listAndCount({
customer_id: order.customer_id,
})
if (count >= 20) {
// add customer to VIP group
customerGroupService.addCustomers(
vipGroup[0].id,
order.customer_id
)
}
handleOrderPlaced = async ({ id }) => {
// check if VIP group exists
const vipGroup = await this.customerGroupService_.list({
name: "VIP",
}, {
relations: ["customers"],
})
if (!vipGroup.length) {
return
}
// retrieve order and its customer
const order = await this.orderService_.retrieve(id)
if (!order || !order.customer_id ||
vipGroup[0].customers.find(
(customer) => customer.id === order.customer_id
) !== undefined) {
return
}
// retrieve orders of this customer
const [, count] = await this.orderService_.listAndCount({
customer_id: order.customer_id,
})
if (count >= 20) {
// add customer to VIP group
this.customerGroupService_.addCustomers(
vipGroup[0].id,
order.customer_id
)
}
}
}
export default AddCustomerToVipSubscriber
export const config: SubscriberConfig = {
event: OrderService.Events.PLACED,
context: {
subscriberId: "order-placed-handler",
},
}
```
</Details>
@@ -544,93 +522,78 @@ You can alternatively have a Scheduled Job that checks if the number of new prod
```ts title=src/subscribers/send-products-newsletter.ts
import {
CustomerService,
EventBusService,
NoteService,
OrderService,
ProductService,
type SubscriberConfig,
type SubscriberArgs,
ProductService,
StoreService,
CustomerService,
} from "@medusajs/medusa"
import {
ProductSelector,
} from "@medusajs/medusa/dist/types/product"
type InjectedDependencies = {
eventBusService: EventBusService
sendgridService: any
productService: ProductService
storeService: StoreService
customerService: CustomerService
}
export default async function handleOrderPlaced({
data, eventName, container, pluginOptions,
}: SubscriberArgs<Record<string, string>>) {
const productService: ProductService = container.resolve(
"productService"
)
const storeService: StoreService = container.resolve(
"storeService"
)
const customerService: CustomerService = container.resolve(
"customerService"
)
const sendgridService = container.resolve("sendgridService")
class SendProductsNewsletterSubscriber {
protected sendGridService_: any
protected productService_: ProductService
protected storeService_: StoreService
protected customerService_: CustomerService
constructor({
eventBusService,
sendgridService,
productService,
storeService,
customerService,
}: InjectedDependencies) {
this.productService_ = productService
this.sendGridService_ = sendgridService
this.storeService_ = storeService
this.customerService_ = customerService
eventBusService.subscribe(
"product.created",
this.handleProductCreated
)
// retrieve store to have access to last send date
const store = await storeService.retrieve()
const productFilters: ProductSelector = {}
if (store.metadata.last_send_date) {
productFilters.created_at = {
gt: new Date(store.metadata.last_send_date as string),
}
}
handleProductCreated = async ({ id }) => {
// retrieve store to have access to last send date
const store = await this.storeService_.retrieve()
const productFilters: ProductSelector = {}
if (store.metadata.last_send_date) {
productFilters.created_at = {
gt: new Date(store.metadata.last_send_date as string),
}
}
const products = await productService.list(
productFilters
)
const products = await this.productService_.list(
productFilters
)
if (products.length > 10) {
// get subscribed customers
const customers = await customerService.list({
metadata: {
is_subscribed: true,
},
})
sendgridService.sendEmail({
templateId: "product-newsletter",
from: "hello@medusajs.com",
to: customers.map((customer) => ({
name: customer.first_name,
email: customer.email,
})),
dynamic_template_data: {
// any data necessary for your template...
products,
},
})
if (products.length > 10) {
// get subscribed customers
const customers = await this.customerService_.list({
metadata: {
is_subscribed: true,
},
})
this.sendGridService_.sendEmail({
templateId: "product-newsletter",
from: "hello@medusajs.com",
to: customers.map((customer) => ({
name: customer.first_name,
email: customer.email,
})),
dynamic_template_data: {
// any data necessary for your template...
products,
},
})
await this.storeService_.update({
metadata: {
last_send_date: new Date(),
},
})
}
await storeService.update({
metadata: {
last_send_date: new Date(),
},
})
}
}
export default SendProductsNewsletterSubscriber
export const config: SubscriberConfig = {
event: ProductService.Events.CREATED,
context: {
subscriberId: "product-create-handler",
},
}
```
</Details>
@@ -921,7 +921,7 @@ To add an interface that allows the admin user to upload digital products, you c
When a customer purchases a digital product, they should receive a link to download it.
To implement that, you first need to listen to the `order.placed` event using a Subscriber. Then, in the handler method of the subscriber, you check for the digital products in the order and obtain the download URLs using the file service's `getPresignedDownloadUrl` method.
To implement that, you first need to listen to the `order.placed` event using a Subscriber. Then, in the subscriber handler function, you check for the digital products in the order and obtain the download URLs using the file service's `getPresignedDownloadUrl` method.
:::note
@@ -954,88 +954,74 @@ Finally, you can send a notification, such as an email, to the customer using th
```ts title=src/subscribers/handle-order.ts
import {
AbstractFileService,
EventBusService,
type SubscriberConfig,
type SubscriberArgs,
OrderService,
AbstractFileService,
} from "@medusajs/medusa"
type InjectedDependencies = {
eventBusService: EventBusService
orderService: OrderService
sendgridService: any
fileService: AbstractFileService
}
export default async function handleOrderPlaced({
data, eventName, container, pluginOptions,
}: SubscriberArgs<Record<string, string>>) {
const orderService: OrderService = container.resolve(
"orderService"
)
const fileService: AbstractFileService = container.resolve(
"fileService"
)
const sendgridService = container.resolve("sendgridService")
class HandleOrderSubscribers {
protected readonly orderService_: OrderService
protected readonly sendgridService_: any
protected readonly fileService_: AbstractFileService
const order = await orderService.retrieve(data.id, {
relations: [
"items",
"items.variant",
"items.variant.product_medias",
],
})
constructor({
eventBusService,
orderService,
sendgridService,
fileService,
}: InjectedDependencies) {
this.orderService_ = orderService
this.sendgridService_ = sendgridService
this.fileService_ = fileService
eventBusService.subscribe(
"order.placed",
this.handleOrderPlaced
)
}
handleOrderPlaced = async (
data: Record<string, any>
) => {
const order = await this.orderService_.retrieve(data.id, {
relations: [
"items",
"items.variant",
"items.variant.product_medias",
],
})
// find product medias in the order
const urls = []
for (const item of order.items) {
if (!item.variant.product_medias.length) {
return
}
await Promise.all([
item.variant.product_medias.forEach(
async (productMedia) => {
// get the download URL from the file service
const downloadUrl = await
this.fileService_.getPresignedDownloadUrl({
fileKey: productMedia.file_key,
isPrivate: true,
})
urls.push(downloadUrl)
}),
])
}
if (!urls.length) {
// find product medias in the order
const urls = []
for (const item of order.items) {
if (!item.variant.product_medias.length) {
return
}
this.sendgridService_.sendEmail({
templateId: "digital-download",
from: "hello@medusajs.com",
to: order.email,
dynamic_template_data: {
// any data necessary for your template...
digital_download_urls: urls,
},
})
await Promise.all([
item.variant.product_medias.forEach(
async (productMedia) => {
// get the download URL from the file service
const downloadUrl = await
fileService.getPresignedDownloadUrl({
fileKey: productMedia.file_key,
isPrivate: true,
})
urls.push(downloadUrl)
}),
])
}
if (!urls.length) {
return
}
sendgridService.sendEmail({
templateId: "digital-download",
from: "hello@medusajs.com",
to: order.email,
dynamic_template_data: {
// any data necessary for your template...
digital_download_urls: urls,
},
})
}
export default HandleOrderSubscribers
export const config: SubscriberConfig = {
event: OrderService.Events.PLACED,
context: {
subscriberId: "order-placed-handler",
},
}
```
Notice that regardless of what file service you have installed, you can access it using dependency injection under the name `fileService`.
+16 -9
View File
@@ -256,7 +256,7 @@ You can also extend services if you need to customize a functionality implemente
While implementing your marketplace, you'll typically need to listen to certain events then perform actions asynchronously. For example, you can listen to the `order.placed` event and, when triggered, create child orders of the order, separating ordered items by their associated store.
To listen to events, you need to create Subscribers that subscribe a handler method to an event. In that handler method, you can implement the desired functionality.
To listen to events, you need to create Subscribers that subscribe a handler function to an event. In that handler function, you can implement the desired functionality.
<DocCard item={{
type: 'link',
@@ -274,17 +274,24 @@ To listen to events, you need to create Subscribers that subscribe a handler met
To listen to the `order.placed` event, create the file `src/subscribers/orderNotifier.ts` with the following content:
```ts title=src/subscribers/orderNotifier.ts
class OrderNotifierSubscriber {
constructor({ eventBusService }) {
eventBusService.subscribe("order.placed", this.handleOrder)
}
import {
type SubscriberConfig,
type SubscriberArgs,
OrderService,
} from "@medusajs/medusa"
handleOrder = async (data) => {
// TODO perform functionality
}
export default async function handleOrderPlaced({
data, eventName, container, pluginOptions,
}: SubscriberArgs<Record<string, string>>) {
// TODO perform functionality
}
export default OrderNotifierSubscriber
export const config: SubscriberConfig = {
event: OrderService.Events.PLACED,
context: {
subscriberId: "order-placed-handler",
},
}
```
This subscribes the `handleOrder` method to be executed whenever the `order.placed` event is emitted.
+1 -1
View File
@@ -75,7 +75,7 @@ Medusa uses the fulfillment service whenever a fulfillment action is performed,
![Fulfilling orders with Medusa OMS](https://res.cloudinary.com/dza7lstvk/image/upload/v1695115456/Medusa%20Docs/Diagrams/oms-fulfillment_kfi1iz.jpg)
In addition, Medusa has an event bus that allows you to listen to events and perform a task asynchronously when that event is triggered. So, you can listen to fulfillment-related events, such as the `order.fulfillment_created` event, and handle that event in the subscribed handler method.
In addition, Medusa has an event bus that allows you to listen to events and perform a task asynchronously when that event is triggered. So, you can listen to fulfillment-related events, such as the `order.fulfillment_created` event, and handle that event in the subscribed handler function.
<DocCardList colSize={6} items={[
{
@@ -93,7 +93,7 @@ By implementing the subscription logic within your backend, you can have full co
Implementing the logic depends on your use case, but you'll mainly need to do two things:
1. Perform an action when an order is placed, such as saving subscription details. This can be done using subscribers, which register handler methods to be triggered when an event is emitted. When an order is placed, the `order.placed` event is emitted.
1. Perform an action when an order is placed, such as saving subscription details. This can be done using subscribers, which register handler functions to be triggered when an event is emitted. When an order is placed, the `order.placed` event is emitted.
2. Check daily for subscriptions that need renewal. This can be done using a scheduled job, which is a cron job that can be executed on a defined interval. Within that job, you can define your renewal logic.
<DocCardList colSize={6} items={[