docs: fixes to type errors in guides (#13797)

This commit is contained in:
Shahed Nasser authored and GitHub committed 2025-10-21 16:12:35 +03:00
1 parent d9249be6e6
commit 0d83918348
19 files changed
+922 -682

No files matched your search

@@ -450,7 +450,7 @@ export const validateVariantOutOfStockStep = createStep(
sales_channel_id,
})
if (availability[variant_id].availability > 0) {
if ((availability[variant_id].availability || 0) > 0) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Variant isn't out of stock."
@@ -1212,7 +1212,7 @@ export const getRestockedStep = createStep(
sales_channel_id: restockSubscription.sales_channel_id,
})
if (variantAvailability[restockSubscription.variant_id].availability > 0) {
if ((variantAvailability[restockSubscription.variant_id].availability || 0) > 0) {
restocked.push(restockSubscription)
}
})
@@ -461,7 +461,7 @@ Create the file `src/workflows/create-digital-product/steps/create-digital-produ
export const createDpHighlights = [
["19", "createDigitalProducts", "Create the digital product."],
["24", "digital_product", "Pass the digital product to the compensation function."],
["31", "deleteDigitalProducts", "Delete the digital product if an error occurs in the workflow."]
["34", "deleteDigitalProducts", "Delete the digital product if an error occurs in the workflow."]
]
```ts title="src/workflows/create-digital-product/steps/create-digital-product.ts" highlights={createDpHighlights} collapsibleLines="1-7" expandMoreLabel="Show Imports"
@@ -491,12 +491,15 @@ const createDigitalProductStep = createStep(
digital_product: digitalProduct,
})
},
async ({ digital_product }, { container }) => {
async (data, { container }) => {
if (!data) {
return
}
const digitalProductModuleService: DigitalProductModuleService =
container.resolve(DIGITAL_PRODUCT_MODULE)
await digitalProductModuleService.deleteDigitalProducts(
digital_product.id
data.digital_product.id
)
}
)
@@ -515,7 +518,7 @@ Create the file `src/workflows/create-digital-product/steps/create-digital-produ
export const createDigitalProductMediaHighlights = [
["29", "createDigitalProductMedias", "Create the digital product medias."],
["34", "digital_product_medias", "Pass the digital product medias to the compensation function."],
["41", "deleteDigitalProductMedias", "Delete the digital product medias if an error occurs in the workflow."]
["44", "deleteDigitalProductMedias", "Delete the digital product medias if an error occurs in the workflow."]
]
```ts title="src/workflows/create-digital-product/steps/create-digital-product-medias.ts" highlights={createDigitalProductMediaHighlights} collapsibleLines="1-8" expandMoreLabel="Show Imports"
@@ -555,12 +558,15 @@ const createDigitalProductMediasStep = createStep(
digital_product_medias: digitalProductMedias,
})
},
async ({ digital_product_medias }, { container }) => {
async (data, { container }) => {
if (!data) {
return
}
const digitalProductModuleService: DigitalProductModuleService =
container.resolve(DIGITAL_PRODUCT_MODULE)
await digitalProductModuleService.deleteDigitalProductMedias(
digital_product_medias.map((media) => media.id)
data.digital_product_medias.map((media) => media.id)
)
}
)
@@ -1883,7 +1889,7 @@ export const createDpoHighlights = [
["18", "InferTypeOf", "Infer the type of the `DigitalProduct` data model since it's a variable."],
["33", "createDigitalProductOrders", "Create the digital product order."],
["41", "digital_product_order", "Pass the created digital product order to the compensation function."],
["48", "deleteDigitalProductOrders", "Delete the digital product order if an error occurs in the workflow."]
["51", "deleteDigitalProductOrders", "Delete the digital product order if an error occurs in the workflow."]
]
```ts title="src/workflows/create-digital-product-order/steps/create-digital-product-order.ts" highlights={createDpoHighlights} collapsibleLines="1-14" expandMoreLabel="Show Imports"
@@ -1901,7 +1907,7 @@ import DigitalProductModuleService from "../../../modules/digital-product/servic
import { DIGITAL_PRODUCT_MODULE } from "../../../modules/digital-product"
import DigitalProduct from "../../../modules/digital-product/models/digital-product"
type StepInput = {
export type CreateDigitalProductOrderStepInput = {
items: (OrderLineItemDTO & {
variant: ProductVariantDTO & {
digital_product: InferTypeOf<typeof DigitalProduct>
@@ -1911,7 +1917,7 @@ type StepInput = {
const createDigitalProductOrderStep = createStep(
"create-digital-product-order",
async ({ items }: StepInput, { container }) => {
async ({ items }: CreateDigitalProductOrderStepInput, { container }) => {
const digitalProductModuleService: DigitalProductModuleService =
container.resolve(DIGITAL_PRODUCT_MODULE)
@@ -1929,12 +1935,15 @@ const createDigitalProductOrderStep = createStep(
digital_product_order: digitalProductOrder,
})
},
async ({ digital_product_order }, { container }) => {
async (data, { container }) => {
if (!data) {
return
}
const digitalProductModuleService: DigitalProductModuleService =
container.resolve(DIGITAL_PRODUCT_MODULE)
await digitalProductModuleService.deleteDigitalProductOrders(
digital_product_order.id
data.digital_product_order.id
)
}
)
@@ -1951,14 +1960,14 @@ In the compensation function, you delete the digital product order.
Create the file `src/workflows/create-digital-product-order/index.ts` with the following content:
export const createDpoWorkflowHighlights = [
["27", "completeCartWorkflow", "Create an order for the cart."],
["33", "useQueryGraphStep", "Retrieve the order's items and their associated variants and linked digital products."],
["58", "when", "Check whether the order has any digital products."],
["64", "then", "Perform the callback function if an order has digital products."],
["67", "createDigitalProductOrderStep", "Create the digital product order."],
["71", "createRemoteLinkStep", "Link the digital product order to the Medusa order."],
["80", "createOrderFulfillmentWorkflow", "Create a fulfillment for the digital products in the order."],
["94", "emitEventStep", "Emit the `digital_product_order.created` event."]
["29", "completeCartWorkflow", "Create an order for the cart."],
["35", "useQueryGraphStep", "Retrieve the order's items and their associated variants and linked digital products."],
["60", "when", "Check whether the order has any digital products."],
["66", "then", "Perform the callback function if an order has digital products."],
["69", "createDigitalProductOrderStep", "Create the digital product order."],
["73", "createRemoteLinkStep", "Link the digital product order to the Medusa order."],
["82", "createOrderFulfillmentWorkflow", "Create a fulfillment for the digital products in the order."],
["96", "emitEventStep", "Emit the `digital_product_order.created` event."]
]
```ts title="src/workflows/create-digital-product-order/index.ts" highlights={createDpoWorkflowHighlights} collapsibleLines="1-17" expandMoreLabel="Show Imports"
@@ -1978,7 +1987,9 @@ import {
import {
Modules,
} from "@medusajs/framework/utils"
import createDigitalProductOrderStep from "./steps/create-digital-product-order"
import createDigitalProductOrderStep, {
CreateDigitalProductOrderStepInput,
} from "./steps/create-digital-product-order"
import { DIGITAL_PRODUCT_MODULE } from "../../modules/digital-product"
type WorkflowInput = {
@@ -2015,7 +2026,7 @@ const createDigitalProductOrderWorkflow = createWorkflow(
orders,
},
(data) => {
return data.orders[0].items.filter((item) => item.variant.digital_product !== undefined)
return data.orders[0].items?.filter((item) => item?.variant?.digital_product !== undefined)
}
)
@@ -2023,14 +2034,14 @@ const createDigitalProductOrderWorkflow = createWorkflow(
"create-digital-product-order-condition",
itemsWithDigitalProducts,
(itemsWithDigitalProducts) => {
return itemsWithDigitalProducts.length
return !!itemsWithDigitalProducts?.length
}
).then(() => {
const {
digital_product_order,
} = createDigitalProductOrderStep({
items: orders[0].items,
})
} as unknown as CreateDigitalProductOrderStepInput)
createRemoteLinkStep([{
[DIGITAL_PRODUCT_MODULE]: {
@@ -2047,9 +2058,9 @@ const createDigitalProductOrderWorkflow = createWorkflow(
items: transform({
itemsWithDigitalProducts,
}, (data) => {
return data.itemsWithDigitalProducts.map((item) => ({
id: item.id,
quantity: item.quantity,
return data.itemsWithDigitalProducts!.map((item) => ({
id: item!.id,
quantity: item!.quantity,
}))
}),
},
@@ -2225,10 +2236,14 @@ import {
INotificationModuleService,
IFileModuleService,
} from "@medusajs/framework/types"
import { ModuleRegistrationName } from "@medusajs/framework/utils"
import {
MedusaError,
ModuleRegistrationName,
promiseAll,
} from "@medusajs/framework/utils"
import { DigitalProductOrder, MediaType } from "../../../modules/digital-product/types"
type SendDigitalOrderNotificationStepInput = {
export type SendDigitalOrderNotificationStepInput = {
digital_product_order: DigitalProductOrder
}
@@ -2244,6 +2259,20 @@ export const sendDigitalOrderNotificationStep = createStep(
ModuleRegistrationName.FILE
)
if (!digitalProductOrder.order) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Digital product order is missing associated order."
)
}
if (!digitalProductOrder.order.email) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"Order is missing email."
)
}
// TODO assemble notification
}
)
@@ -2251,16 +2280,16 @@ export const sendDigitalOrderNotificationStep = createStep(
This creates the `sendDigitalOrderNotificationStep` step that receives a digital product order as an input.
In the step, so far you resolve the main services of the Notification and File Modules.
In the step, so far you resolve the main services of the Notification and File Modules. You also check that the digital product order has an associated order and that the order has an email.
Replace the `TODO` with the following:
Next, you'll prepare the data to pass in the notification. Replace the `TODO` with the following:
```ts title="src/workflows/fulfill-digital-order/steps/send-digital-order-notification.ts"
const notificationData = await Promise.all(
const notificationData = await promiseAll(
digitalProductOrder.products.map(async (product) => {
const medias = []
const medias: string[] = []
await Promise.all(
await promiseAll(
product.medias
.filter((media) => media.type === MediaType.MAIN)
.map(async (media) => {
@@ -2304,12 +2333,12 @@ You use the `createNotifications` method of the Notification Module's main servi
Create the workflow in the file `src/workflows/fulfill-digital-order/index.ts`:
export const fulfillWorkflowHighlights = [
["18", "useQueryGraphStep", "Retrieve the digital product order's details."],
["35", "sendDigitalOrderNotificationStep", "Send a notification to the customer."],
["39", "markOrderFulfillmentAsDeliveredWorkflow", "Mark the order's fulfillment as delivered."],
["21", "useQueryGraphStep", "Retrieve the digital product order's details."],
["38", "sendDigitalOrderNotificationStep", "Send a notification to the customer."],
["44", "markOrderFulfillmentAsDeliveredWorkflow", "Mark the order's fulfillment as delivered."],
]
```ts title="src/workflows/fulfill-digital-order/index.ts" highlights={fulfillWorkflowHighlights} collapsibleLines="1-10" expandMoreLabel="Show Imports"
```ts title="src/workflows/fulfill-digital-order/index.ts" highlights={fulfillWorkflowHighlights} collapsibleLines="1-13" expandMoreLabel="Show Imports"
import {
createWorkflow,
WorkflowResponse,
@@ -2318,7 +2347,10 @@ import {
markOrderFulfillmentAsDeliveredWorkflow,
useQueryGraphStep,
} from "@medusajs/medusa/core-flows"
import { sendDigitalOrderNotificationStep } from "./steps/send-digital-order-notification"
import {
sendDigitalOrderNotificationStep,
SendDigitalOrderNotificationStepInput,
} from "./steps/send-digital-order-notification"
type FulfillDigitalOrderWorkflowInput = {
id: string
@@ -2346,14 +2378,17 @@ export const fulfillDigitalOrderWorkflow = createWorkflow(
sendDigitalOrderNotificationStep({
digital_product_order: digitalProductOrders[0],
})
} as unknown as SendDigitalOrderNotificationStepInput)
markOrderFulfillmentAsDeliveredWorkflow.runAsStep({
input: {
orderId: digitalProductOrders[0].order.id,
fulfillmentId: digitalProductOrders[0].order.fulfillments[0].id,
},
})
when({ digitalProductOrders }, (data) => !!data.digitalProductOrders[0].order?.fulfillments?.length)
.then(() => {
markOrderFulfillmentAsDeliveredWorkflow.runAsStep({
input: {
orderId: digitalProductOrders[0].order!.id,
fulfillmentId: digitalProductOrders[0].order!.fulfillments![0]!.id,
},
})
})
return new WorkflowResponse(
digitalProductOrders[0]
@@ -2366,7 +2401,13 @@ In the workflow, you:
1. Retrieve the digital product order's details using `useQueryGraphStep` from Medusa's core workflows.
2. Send a notification to the customer with the digital product download links using the `sendDigitalOrderNotificationStep`.
3. Mark the order's fulfillment as delivered using `markOrderFulfillmentAsDeliveredWorkflow` from Medusa's core workflows.
3. Check whether the Medusa order has fulfillments. If so, mark the order's fulfillment as delivered using `markOrderFulfillmentAsDeliveredWorkflow` from Medusa's core workflows.
<Note title="Tip">
`when` allows you to perform steps based on a condition during execution. Learn more in the [Conditions in Workflows](!docs!/learn/fundamentals/workflows/conditions) documentation.
</Note>
### Configure Notification Module Provider
@@ -2555,8 +2596,11 @@ export const GET = async (
const digitalProducts = {}
customer.orders.forEach((order) => {
order.digital_product_order.products.forEach((product) => {
customer.orders?.forEach((order) => {
order?.digital_product_order?.products.forEach((product) => {
if (!product) {
return
}
digitalProducts[product.id] = product
})
})
@@ -2613,9 +2657,9 @@ export const POST = async (
},
})
const customerDigitalOrderIds = customer.orders
.filter((order) => order.digital_product_order !== undefined)
.map((order) => order.digital_product_order.id)
const customerDigitalOrderIds = customer.orders?.filter(
(order) => order?.digital_product_order !== undefined
).map((order) => order!.digital_product_order!.id)
const { data: dpoResult } = await query.graph({
entity: "digital_product_order",
@@ -2634,11 +2678,11 @@ export const POST = async (
)
}
let foundMedia = undefined
let foundMedia: any | undefined = undefined
dpoResult[0].products.some((product) => {
return product.medias.some((media) => {
foundMedia = media.id === req.params.mediaId ? media : undefined
return product?.medias.some((media) => {
foundMedia = media?.id === req.params.mediaId ? media : undefined
return foundMedia !== undefined
})
@@ -231,15 +231,9 @@ export enum DeliveryStatus {
IN_TRANSIT = "in_transit",
DELIVERED = "delivered",
}
declare module "@medusajs/framework/types" {
export interface ModuleImplementations {
deliveryModuleService: DeliveryModuleService;
}
}
```
This adds an enum that is used by the data models. It also adds a type for `deliveryModuleService` in `ModuleImplementations` so that when you resolve it from the Medusa container, it has the correct typing.
This adds an enum that is used by the data models.
### Create Delivery Data Models
@@ -489,15 +483,9 @@ import { Restaurant } from "../models/restaurant"
export type CreateRestaurant = Omit<
InferTypeOf<typeof Restaurant>, "id" | "admins"
>
declare module "@medusajs/framework/types" {
export interface ModuleImplementations {
restaurantModuleService: RestaurantModuleService;
}
}
```
This adds a type used for inputs in creating a restaurant. It also adds a type for `restaurantModuleService` in `ModuleImplementations` so that when you resolve it from the Medusa container, it has the correct typing.
This adds a type used for inputs in creating a restaurant.
<Note title="Tip">
@@ -920,14 +908,16 @@ const { user, authUserInput } = transform({ input, restaurantUser, driverUser },
authUserInput: {
authIdentityId: data.input.auth_identity_id,
actorType: data.input.user.actor_type,
value: user.id,
value: user?.id || "",
},
}
})
setAuthAppMetadataStep(authUserInput)
return new WorkflowResponse(user)
return new WorkflowResponse({
user,
})
```
In the workflow, you:
@@ -987,7 +977,7 @@ export const POST = async (
} as CreateUserWorkflowInput,
})
res.status(201).json({ user: result })
res.status(201).json({ user: result.user })
}
```
@@ -1133,12 +1123,15 @@ export const deleteRestaurantAdminStep = createStep(
return new StepResponse(undefined, { admin })
},
async ({ admin }, { container }) => {
async (data, { container }) => {
if (!data) {
return
}
const restaurantModuleService: RestaurantModuleService = container.resolve(
RESTAURANT_MODULE
)
const { restaurant: _, ...adminData } = admin
const { restaurant: _, ...adminData } = data.admin
await restaurantModuleService.createRestaurantAdmins(adminData)
}
@@ -1190,6 +1183,7 @@ const { data: authIdentities } = useQueryGraphStep({
entity: "auth_identity",
fields: ["id"],
filters: {
// @ts-ignore
app_metadata: {
restaurant_id: input.id,
},
@@ -1498,7 +1492,7 @@ Next, create the file `src/workflows/delivery/steps/create-delivery.ts` with the
export const createDeliveryStepHighlights = [
["11", "createDeliveries", "Create the delivery."],
["21", "softDeleteDeliveries", "Delete the delivery if an error occurs in the workflow."]
["24", "softDeleteDeliveries", "Delete the delivery if an error occurs in the workflow."]
]
```ts title="src/workflows/delivery/steps/create-delivery.ts" highlights={createDeliveryStepHighlights}
@@ -1518,11 +1512,14 @@ export const createDeliveryStep = createStep(
delivery_id: delivery.id,
})
},
async function ({ delivery_id }, { container }) {
async function (data, { container }) {
if (!data) {
return
}
const deliverModuleService: DeliveryModuleService =
container.resolve(DELIVERY_MODULE)
deliverModuleService.softDeleteDeliveries(delivery_id)
deliverModuleService.softDeleteDeliveries(data.delivery_id)
}
)
@@ -1535,10 +1532,10 @@ This step creates a delivery and returns it. In the compensation function, it de
Finally, create the workflow in `src/workflows/delivery/workflows/create-delivery.ts`:
export const createDeliveryWorkflowHighlights = [
["23", "validateRestaurantStep", "Ensure that the specified restaurant exists before creating the delivery."],
["26", "createDeliveryStep", "Create the delivery."],
["28", "transform", "Define an array of link objects to create."],
["50", "createRemoteLinkStep", "Create the links between the delivery and cart and restaurant."]
["24", "validateRestaurantStep", "Ensure that the specified restaurant exists before creating the delivery."],
["27", "createDeliveryStep", "Create the delivery."],
["29", "transform", "Define an array of link objects to create."],
["51", "createRemoteLinkStep", "Create the links between the delivery and cart and restaurant."]
]
```ts title="src/workflows/delivery/workflows/create-delivery.ts" highlights={createDeliveryWorkflowHighlights} collapsibleLines="1-13" expandButtonLabel="Show Imports"
@@ -1548,6 +1545,7 @@ import {
createWorkflow,
transform,
} from "@medusajs/framework/workflows-sdk"
import { LinkDefinition } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"
import { createRemoteLinkStep } from "@medusajs/medusa/core-flows"
import { DELIVERY_MODULE } from "../../../modules/delivery"
@@ -1589,7 +1587,7 @@ export const createDeliveryWorkflow = createWorkflow(
delivery_id: data.delivery.id,
},
},
]))
] as LinkDefinition[]))
createRemoteLinkStep(links)
@@ -1787,7 +1785,7 @@ export const notifyRestaurantStep = createStep(
await eventBus.emit({
name: "notify.restaurant",
data: {
restaurant_id: delivery.restaurant.id,
restaurant_id: delivery.restaurant?.id,
delivery_id: delivery.id,
},
})
@@ -1837,7 +1835,7 @@ This step is async and its only purpose is to wait until it’s marked as succes
Create the file `src/workflows/delivery/steps/create-order.ts` with the following content:
export const createOrderStepHighlights1 = [
["15", "deliveryQuery", "Retrieve the delivery with its linked cart's details."]
["16", "deliveryQuery", "Retrieve the delivery with its linked cart's details."]
]
```ts title="src/workflows/delivery/steps/create-order.ts" highlights={createOrderStepHighlights1} collapsibleLines="1-9" expandButtonLabel="Show Imports"
@@ -1845,6 +1843,7 @@ import { CreateOrderShippingMethodDTO } from "@medusajs/framework/types"
import {
Modules,
ContainerRegistrationKeys,
MedusaError,
} from "@medusajs/framework/utils"
import { StepResponse, createStep } from "@medusajs/framework/workflows-sdk"
import { DELIVERY_MODULE } from "../../../modules/delivery"
@@ -1871,7 +1870,7 @@ export const createOrderStep = createStep(
// TODO create order
},
async ({ orderId }, { container }) => {
async (data, { container }) => {
// TODO add compensation
}
)
@@ -1883,26 +1882,66 @@ This creates the `createOrderStep`, which so far only retrieves the delivery wit
Replace the `TODO` with the following to create the order:
export const createOrderStepHighlights2 = [
["5", "createOrders", "Create the order."],
["18", "linkDef", "Define an array of links to be created."]
["12", "createOrders", "Create the order."],
["58", "linkDef", "Define an array of links to be created."]
]
```ts title="src/workflows/delivery/steps/create-order.ts" highlights={createOrderStepHighlights2}
const { cart } = delivery
if (!cart) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`Cart for delivery with id: ${deliveryId} was not found`
)
}
const orderModuleService = container.resolve(Modules.ORDER)
const order = await orderModuleService.createOrders({
currency_code: cart.currency_code,
email: cart.email,
shipping_address: cart.shipping_address,
billing_address: cart.billing_address,
items: cart.items,
region_id: cart.region_id,
customer_id: cart.customer_id,
sales_channel_id: cart.sales_channel_id,
shipping_address: {
first_name: cart.shipping_address?.first_name || "",
last_name: cart.shipping_address?.last_name || "",
address_1: cart.shipping_address?.address_1 || "",
address_2: cart.shipping_address?.address_2 || "",
city: cart.shipping_address?.city || "",
province: cart.shipping_address?.province || "",
postal_code: cart.shipping_address?.postal_code || "",
country_code: cart.shipping_address?.country_code || "",
phone: cart.shipping_address?.phone || "",
},
billing_address: {
first_name: cart.billing_address?.first_name || "",
last_name: cart.billing_address?.last_name || "",
address_1: cart.billing_address?.address_1 || "",
address_2: cart.billing_address?.address_2 || "",
city: cart.billing_address?.city || "",
province: cart.billing_address?.province || "",
postal_code: cart.billing_address?.postal_code || "",
country_code: cart.billing_address?.country_code || "",
phone: cart.billing_address?.phone || "",
},
items: cart.items.map((item) => ({
title: item!.title,
quantity: item!.quantity,
variant_id: item!.variant_id || "",
unit_price: item!.unit_price,
metadata: item!.metadata || undefined,
product_id: item!.product_id || "",
})),
region_id: cart.region_id || "",
customer_id: cart.customer_id || "",
sales_channel_id: cart.sales_channel_id || "",
shipping_methods:
cart.shipping_methods as unknown as CreateOrderShippingMethodDTO[],
cart.shipping_methods?.map((sm): CreateOrderShippingMethodDTO => ({
shipping_option_id: sm?.shipping_option_id || "",
data: sm?.data || {},
name: sm?.name || "",
amount: sm?.amount || 0,
order_id: "", // will be set internally
})) || [],
})
const linkDef = [{
@@ -1927,9 +1966,12 @@ You create the order using the Order Module’s main service. Then, you create a
Then, replace the `TODO` in the compensation function with the following:
```ts title="src/workflows/delivery/steps/create-order.ts"
if (!data) {
return
}
const orderService = container.resolve(Modules.ORDER)
await orderService.softDeleteOrders([orderId])
await orderService.softDeleteOrders([data.orderId])
```
You delete the order in the compensation function.
@@ -2374,7 +2416,7 @@ So, start by creating the first step at `src/workflows/delivery/steps/update-del
export const updateDeliveryStepHighlights = [
["16", "prevDeliveryData", "Retrieve the previous data of the delivery for the compensation."],
["19", "updateDeliveries", "Update the delivery."],
["35", "updateDeliveries", "Update the delivery with the old data if an error occurs in the workflow."]
["38", "updateDeliveries", "Update the delivery with the old data if an error occurs in the workflow."]
]
```ts title="src/workflows/delivery/steps/update-delivery.ts" highlights={updateDeliveryStepHighlights}
@@ -2403,14 +2445,17 @@ export const updateDeliveryStep = createStep(
prevDeliveryData,
})
},
async ({ prevDeliveryData }, { container }) => {
async (data, { container }) => {
if (!data) {
return
}
const deliverModuleService: DeliveryModuleService =
container.resolve(DELIVERY_MODULE)
const {
driver,
...prevDeliveryDataWithoutDriver
} = prevDeliveryData
} = data.prevDeliveryData
await deliverModuleService.updateDeliveries(prevDeliveryDataWithoutDriver)
}
@@ -2452,7 +2497,7 @@ export const setStepSuccessStep = createStep(
await engineService.setStepSuccess({
idempotencyKey: {
action: TransactionHandlerType.INVOKE,
transactionId: updatedDelivery.transaction_id,
transactionId: updatedDelivery.transaction_id || "",
stepId,
workflowId: handleDeliveryWorkflowId,
},
@@ -2500,7 +2545,7 @@ export const setStepFailedStep = createStep(
await engineService.setStepFailure({
idempotencyKey: {
action: TransactionHandlerType.INVOKE,
transactionId: updatedDelivery.transaction_id,
transactionId: updatedDelivery.transaction_id || "",
stepId,
workflowId: handleDeliveryWorkflowId,
},
@@ -2551,7 +2596,7 @@ export const updateDeliveryWorkflow = createWorkflow(
when(input, ({ stepIdToSucceed }) => stepIdToSucceed !== undefined)
.then(() => {
setStepSuccessStep({
stepId: input.stepIdToSucceed,
stepId: input.stepIdToSucceed!,
updatedDelivery,
})
})
@@ -2560,7 +2605,7 @@ export const updateDeliveryWorkflow = createWorkflow(
when(input, ({ stepIdToFail }) => stepIdToFail !== undefined)
.then(() => {
setStepFailedStep({
stepId: input.stepIdToFail,
stepId: input.stepIdToFail!,
updatedDelivery,
})
})
@@ -2688,7 +2733,7 @@ export const isDeliveryRestaurant = async (
},
})
if (delivery.restaurant.id !== restaurantAdmin.restaurant.id) {
if (delivery.restaurant?.id !== restaurantAdmin.restaurant.id) {
return res.status(403).json({
message: "unauthorized",
})
@@ -2738,7 +2783,7 @@ import deliveriesMiddlewares from "./deliveries/[id]/middlewares"
export default defineMiddlewares({
routes: [
// ...
...deliveriesMiddlewares.routes,
...deliveriesMiddlewares.routes!,
],
})
```
@@ -3387,7 +3432,7 @@ res.write(
"data: " +
JSON.stringify({
message: "Subscribed to workflow",
transactionId: delivery.transaction_id,
transactionId: delivery.transaction_id || undefined,
}) +
"\n\n"
)
@@ -975,6 +975,7 @@ import {
} from "@medusajs/framework/workflows-sdk"
import {
createProductsWorkflow,
CreateProductsWorkflowInput,
createRemoteLinkStep,
useQueryGraphStep,
} from "@medusajs/medusa/core-flows"
@@ -1014,7 +1015,7 @@ const createVendorProductWorkflow = createWorkflow(
})
const createdProducts = createProductsWorkflow.runAsStep({
input: productData,
input: productData as CreateProductsWorkflowInput,
})
// TODO link vendor and products
@@ -1333,26 +1334,28 @@ import {
createStep,
StepResponse,
} from "@medusajs/framework/workflows-sdk"
import { CartDTO, CartLineItemDTO } from "@medusajs/framework/types"
import { CartLineItemDTO } from "@medusajs/framework/types"
import { ContainerRegistrationKeys, promiseAll } from "@medusajs/framework/utils"
type StepInput = {
cart: CartDTO
export type GroupVendorItemsStepInput = {
cart: {
items?: CartLineItemDTO[]
}
}
const groupVendorItemsStep = createStep(
"group-vendor-items",
async ({ cart }: StepInput, { container }) => {
async ({ cart }: GroupVendorItemsStepInput, { container }) => {
const query = container.resolve(ContainerRegistrationKeys.QUERY)
const vendorsItems: Record<string, CartLineItemDTO[]> = {}
await promiseAll(cart.items?.map(async (item) => {
await promiseAll((cart.items || []).map(async (item) => {
const { data: [product] } = await query.graph({
entity: "product",
fields: ["vendor.*"],
filters: {
id: [item.product_id],
id: item.product_id || "",
},
})
@@ -1451,7 +1454,7 @@ const createVendorOrdersStep = createStep(
created_orders: createdOrders,
})
},
async ({ created_orders }, { container, context }) => {
async (data, { container, context }) => {
// TODO add compensation function
}
)
@@ -1586,19 +1589,19 @@ function prepareOrderData(
// item/vendor. This requires changes in the storefront to commodate that
// and passing the item/vendor ID in the `data` property, for example.
// For simplicity here we just use the same shipping method.
shipping_methods: parentOrder.shipping_methods.map((shippingMethod) => ({
shipping_methods: parentOrder.shipping_methods?.map((shippingMethod) => ({
name: shippingMethod.name,
amount: shippingMethod.amount,
shipping_option_id: shippingMethod.shipping_option_id,
data: shippingMethod.data,
tax_lines: shippingMethod.tax_lines.map((taxLine) => ({
tax_lines: shippingMethod.tax_lines?.map((taxLine) => ({
code: taxLine.code,
rate: taxLine.rate,
provider_id: taxLine.provider_id,
tax_rate_id: taxLine.tax_rate_id,
description: taxLine.description,
})),
adjustments: shippingMethod.adjustments.map((adjustment) => ({
adjustments: shippingMethod.adjustments?.map((adjustment) => ({
code: adjustment.code,
amount: adjustment.amount,
description: adjustment.description,
@@ -1621,7 +1624,10 @@ When creating the child orders, the shipping method of the parent is used as-is
Finally, replace the `TODO` in the compensation function with the following:
```ts title="src/workflows/marketplace/create-vendor-orders/steps/create-vendor-orders.ts"
await Promise.all(created_orders.map((createdOrder) => {
if (!data) {
return
}
await promiseAll(data.created_orders.map((createdOrder) => {
return cancelOrderWorkflow(container).run({
input: {
order_id: createdOrder.id,
@@ -1687,7 +1693,7 @@ const createVendorOrdersWorkflow = createWorkflow(
const { vendorsItems } = groupVendorItemsStep({
cart: carts[0],
})
} as unknown as GroupVendorItemsStepInput)
const order = getOrderDetailWorkflow.runAsStep({
input: {
@@ -1869,7 +1875,7 @@ export const GET = async (
],
variables: {
filters: {
id: vendorAdmin.vendor.orders.map((order) => order.id),
id: vendorAdmin.vendor.orders?.map((order) => order?.id),
},
},
},
@@ -646,7 +646,7 @@ const createSubscriptionStep = createStep(
}, {
subscription: subscription[0],
})
}, async ({ subscription }, { container }) => {
}, async (data, { container }) => {
// TODO implement compensation
}
)
@@ -706,10 +706,13 @@ This adds links between:
The step also has a compensation function to undo the step’s changes if an error occurs. So, replace the second `TODO` with the following:
```ts title="src/workflows/create-subscription/steps/create-subscription.ts"
if (!data) {
return
}
const subscriptionModuleService: SubscriptionModuleService =
container.resolve(SUBSCRIPTION_MODULE)
container.resolve(SUBSCRIPTION_MODULE)
await subscriptionModuleService.cancelSubscriptions(subscription.id)
await subscriptionModuleService.cancelSubscriptions(data.subscription.id)
```
The compensation function receives the subscription as a parameter. It cancels the subscription.
@@ -815,7 +818,7 @@ const createSubscriptionWorkflow = createWorkflow(
const { subscription, linkDefs } = createSubscriptionStep({
cart_id: input.cart_id,
order_id: orders[0].id,
customer_id: orders[0].customer_id,
customer_id: orders[0].customer_id!,
subscription_data: input.subscription_data,
})
@@ -859,9 +862,9 @@ In this step, you’ll create a custom API route similar to the [Complete Cart A
Create the file `src/api/store/carts/[id]/subscribe/route.ts` with the following content:
export const completeCartHighlights = [
["17", "graph", "Retrieve the cart to retrieve the subscription details from the `metadata`."],
["29", "", "If the subscription data isn't set in the cart's `metadata`, throw an error"],
["36", "createSubscriptionWorkflow", "Execute the workflow created in the previous step."]
["18", "graph", "Retrieve the cart to retrieve the subscription details from the `metadata`."],
["30", "", "If the subscription data isn't set in the cart's `metadata`, throw an error"],
["37", "createSubscriptionWorkflow", "Execute the workflow created in the previous step."]
]
```ts title="src/api/store/carts/[id]/subscribe/route.ts" highlights={completeCartHighlights} collapsibleLines="1-10" expandMoreLabel="Show Imports"
@@ -874,6 +877,7 @@ import {
MedusaError,
} from "@medusajs/framework/utils"
import createSubscriptionWorkflow from "../../../../../workflows/create-subscription"
import { SubscriptionInterval } from "../../../../../modules/subscription/types"
export const POST = async (
req: MedusaRequest,
@@ -906,8 +910,8 @@ export const POST = async (
input: {
cart_id: req.params.id,
subscription_data: {
interval: metadata.subscription_interval,
period: metadata.subscription_period,
interval: metadata.subscription_interval as SubscriptionInterval,
period: metadata.subscription_period as number,
},
},
})
@@ -1247,7 +1251,7 @@ export const GET = async (
const {
data: subscriptions,
metadata: { count, take, skip },
metadata: { count, take, skip } = {},
} = await query.graph({
entity: "subscription",
...req.queryConfig,
@@ -1743,7 +1747,7 @@ import { AccountHolderDTO, CustomerDTO, PaymentMethodDTO } from "@medusajs/frame
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
export interface GetPaymentMethodStepInput {
customer: CustomerDTO & {
customer?: CustomerDTO & {
account_holder: AccountHolderDTO
}
}
@@ -1776,7 +1780,7 @@ export const getPaymentMethodStep = createStep(
async ({ customer }: GetPaymentMethodStepInput, { container }) => {
const paymentModuleService = container.resolve(Modules.PAYMENT)
if (!customer.account_holder) {
if (!customer?.account_holder) {
throw new MedusaError(
MedusaError.Types.INVALID_DATA,
"No account holder found for the customer while retrieving payment method"
@@ -1840,7 +1844,7 @@ import { createOrderWorkflow } from "@medusajs/medusa/core-flows"
import { SubscriptionData } from "../../../modules/subscription/types"
import { SUBSCRIPTION_MODULE } from "../../../modules/subscription"
type StepInput = {
export type CreateSubscriptionOrderStepInput = {
subscription: SubscriptionData
cart: CartWorkflowDTO
payment_collection: PaymentCollectionDTO
@@ -1854,7 +1858,7 @@ const createSubscriptionOrderStep = createStep(
"create-subscription-order",
async ({
subscription, cart, payment_collection,
}: StepInput,
}: CreateSubscriptionOrderStepInput,
{ container, context }) => {
const linkDefs: LinkDefinition[] = []
@@ -1873,7 +1877,7 @@ const createSubscriptionOrderStep = createStep(
order,
})
},
async ({ order }, { container }) => {
async (data, { container }) => {
// TODO add compensation function
}
)
@@ -1904,20 +1908,20 @@ function getOrderData(cart: CartWorkflowDTO) {
id: null,
},
items: cart.items,
shipping_methods: cart.shipping_methods.map((method) => ({
shipping_methods: cart.shipping_methods?.map((method) => ({
name: method.name,
amount: method.amount,
is_tax_inclusive: method.is_tax_inclusive,
shipping_option_id: method.shipping_option_id,
data: method.data,
tax_lines: method.tax_lines.map((taxLine) => ({
tax_lines: method.tax_lines?.map((taxLine) => ({
description: taxLine.description,
tax_rate_id: taxLine.tax_rate_id,
code: taxLine.code,
rate: taxLine.rate,
provider_id: taxLine.provider_id,
})),
adjustments: method.adjustments.map((adjustment) => ({
adjustments: method.adjustments?.map((adjustment) => ({
code: adjustment.code,
amount: adjustment.amount,
description: adjustment.description,
@@ -1962,11 +1966,14 @@ This adds links to be created into the `linkDefs` array between the new order an
Finally, replace the `TODO` in the compensation function to cancel the order in case of an error:
```ts title="src/workflows/create-subscription-order/steps/create-subscription-order.ts"
if (!data) {
return
}
const orderModuleService: IOrderModuleService = container.resolve(
Modules.ORDER
)
await orderModuleService.cancel(order.id)
await orderModuleService.cancel(data.order.id)
```
### Create updateSubscriptionStep (Ninth Step)
@@ -2046,7 +2053,7 @@ const updateSubscriptionStep = createStep(
})
},
async ({
prev_data,
data,
}, { container }) => {
// TODO add compensation
}
@@ -2062,15 +2069,18 @@ Before updating the subscription, the step retrieves the old data and passes it
So, replace the `TODO` in the compensation function with the following:
```ts title="src/workflows/create-subscription-order/steps/update-subscription.ts"
if (!data) {
return
}
const subscriptionModuleService: SubscriptionModuleService =
container.resolve(
SUBSCRIPTION_MODULE
)
await subscriptionModuleService.updateSubscriptions({
id: prev_data.id,
last_order_date: prev_data.last_order_date,
next_order_date: prev_data.next_order_date,
id: data.prev_data.id,
last_order_date: data.prev_data.last_order_date,
next_order_date: data.prev_data.next_order_date,
})
```
@@ -2081,19 +2091,19 @@ This updates the subscription’s `last_order_date` and `next_order_date` proper
Finally, create the file `src/workflows/create-subscription-order/index.ts` with the following content:
export const createSubscriptionOrderWorkflowHighlights = [
["26", "useQueryGraphStep", "Retrieve the cart linked to the subscription."],
["63", "createPaymentCollectionsStep", "Create a payment collection using the same information in the cart."],
["67", "getPaymentMethodStep", "Get the customer's saved payment method."],
["80", "data", "Pass data required by Stripe to capture the payment."],
["89", "createPaymentSessionsWorkflow", "Create a payment session in the payment collection from the previous step."],
["93", "authorizePaymentSessionStep", "Authorize the payment session created from the first step."],
["98", "createSubscriptionOrderStep", "Create the new order for the subscription."],
["104", "createRemoteLinkStep", "Create links returned by the previous step."],
["106", "capturePaymentStep", "Capture the order’s payment."],
["111", "updateSubscriptionStep", "Update the subscription’s `last_order_date` and `next_order_date`."]
["31", "useQueryGraphStep", "Retrieve the cart linked to the subscription."],
["68", "createPaymentCollectionsStep", "Create a payment collection using the same information in the cart."],
["72", "getPaymentMethodStep", "Get the customer's saved payment method."],
["85", "data", "Pass data required by Stripe to capture the payment."],
["94", "createPaymentSessionsWorkflow", "Create a payment session in the payment collection from the previous step."],
["98", "authorizePaymentSessionStep", "Authorize the payment session created from the first step."],
["103", "createSubscriptionOrderStep", "Create the new order for the subscription."],
["109", "createRemoteLinkStep", "Create links returned by the previous step."],
["111", "capturePaymentStep", "Capture the order’s payment."],
["116", "updateSubscriptionStep", "Update the subscription’s `last_order_date` and `next_order_date`."]
]
```ts title="src/workflows/create-subscription-order/index.ts" highlights={createSubscriptionOrderWorkflowHighlights} collapsibleLines="1-18" expandMoreLabel="Show Imports"
```ts title="src/workflows/create-subscription-order/index.ts" highlights={createSubscriptionOrderWorkflowHighlights} collapsibleLines="1-23" expandMoreLabel="Show Imports"
import { createWorkflow, transform, WorkflowResponse } from "@medusajs/framework/workflows-sdk"
import {
useQueryGraphStep,
@@ -2108,9 +2118,14 @@ import {
authorizePaymentSessionStep,
createPaymentCollectionsStep,
} from "@medusajs/medusa/core-flows"
import createSubscriptionOrderStep from "./steps/create-subscription-order"
import createSubscriptionOrderStep, {
CreateSubscriptionOrderStepInput,
} from "./steps/create-subscription-order"
import updateSubscriptionStep from "./steps/update-subscription"
import { getPaymentMethodStep } from "./steps/get-payment-method"
import {
getPaymentMethodStep,
GetPaymentMethodStepInput,
} from "./steps/get-payment-method"
type WorkflowInput = {
subscription: SubscriptionData
@@ -2150,9 +2165,9 @@ const createSubscriptionOrderWorkflow = createWorkflow(
}, (data) => {
const cart = data.subscriptions[0].cart
return {
currency_code: cart.currency_code,
amount: cart.payment_collection.amount,
metadata: cart.payment_collection.metadata,
currency_code: cart?.currency_code || "",
amount: cart?.payment_collection?.amount || 0,
metadata: cart?.payment_collection?.metadata || undefined,
}
})
@@ -2172,7 +2187,7 @@ const createSubscriptionOrderWorkflow = createWorkflow(
return {
payment_collection_id: data.payment_collection.id,
provider_id: "pp_stripe_stripe",
customer_id: data.subscriptions[0].cart.customer.id,
customer_id: data.subscriptions[0].cart?.customer?.id,
data: {
payment_method: data.defaultPaymentMethod.id,
off_session: true,
@@ -2195,7 +2210,7 @@ const createSubscriptionOrderWorkflow = createWorkflow(
subscription: input.subscription,
cart: carts[0],
payment_collection,
})
} as unknown as CreateSubscriptionOrderStepInput)
createRemoteLinkStep(linkDefs)