docs: general updates after 2.5.0 update (#11402)

This commit is contained in:
Shahed Nasser
2025-02-11 18:26:21 +02:00
committed by GitHub
parent ca486aa7ed
commit 5cb44d364d
17 changed files with 11576 additions and 11565 deletions
@@ -227,11 +227,11 @@ export default class MetalPricesModuleService {
return fetch(`https://www.goldapi.io/api/${upperCaseSymbol}/${upperCaseCurrency}`, {
headers: {
'x-access-token': this.options_.accessToken,
"Content-Type": "application/json"
"x-access-token": this.options_.accessToken,
"Content-Type": "application/json",
},
redirect: "follow"
}).then(response => response.json())
redirect: "follow",
}).then((response) => response.json())
.then((response) => {
if (response.error) {
throw new MedusaError(
@@ -286,13 +286,13 @@ So, create the file `src/modules/metal-prices/index.ts` with the following conte
![The directory structure of the Metal Prices Module after adding the definition file.](https://res.cloudinary.com/dza7lstvk/image/upload/v1738248049/Medusa%20Resources/custom-item-price-3_imtbuw.jpg)
```ts title="src/modules/metal-prices/index.ts"
import { Module } from "@medusajs/framework/utils";
import MetalPricesModuleService from "./service";
import { Module } from "@medusajs/framework/utils"
import MetalPricesModuleService from "./service"
export const METAL_PRICES_MODULE = "metal-prices"
export default Module(METAL_PRICES_MODULE, {
service: MetalPricesModuleService
service: MetalPricesModuleService,
})
```
@@ -315,8 +315,8 @@ module.exports = defineConfig({
resolve: "./src/modules/metal-prices",
options: {
accessToken: process.env.GOLD_API_TOKEN,
sandbox: process.env.GOLD_API_SANDBOX === "true"
}
sandbox: process.env.GOLD_API_SANDBOX === "true",
},
},
],
})
@@ -409,10 +409,10 @@ To create the step, create the file `src/workflows/steps/get-variant-metal-price
![The directory structure after adding the step file.](https://res.cloudinary.com/dza7lstvk/image/upload/v1738249036/Medusa%20Resources/custom-item-price-4_kumzdc.jpg)
```ts title="src/workflows/steps/get-variant-metal-prices.ts"
import { createStep } from "@medusajs/framework/workflows-sdk";
import { createStep } from "@medusajs/framework/workflows-sdk"
import { ProductVariantDTO } from "@medusajs/framework/types"
import { METAL_PRICES_MODULE } from "../../modules/metal-prices";
import MetalPricesModuleService from "../../modules/metal-prices/service";
import { METAL_PRICES_MODULE } from "../../modules/metal-prices"
import MetalPricesModuleService from "../../modules/metal-prices/service"
export type GetVariantMetalPricesStepInput = {
variant: ProductVariantDTO & {
@@ -429,7 +429,7 @@ export const getVariantMetalPricesStep = createStep(
async ({
variant,
currencyCode,
quantity = 1
quantity = 1,
}: GetVariantMetalPricesStepInput, { container }) => {
const metalPricesModuleService: MetalPricesModuleService =
container.resolve(METAL_PRICES_MODULE)
@@ -546,19 +546,19 @@ export const addCustomToCartWorkflow = createWorkflow(
"*",
"options.*",
"options.option.*",
"calculated_price.*"
"calculated_price.*",
],
filters: {
id: item.variant_id
id: item.variant_id,
},
options: {
throwIfKeyNotFound: true
throwIfKeyNotFound: true,
},
context: {
calculated_price: QueryContext({
currency_code: carts[0].currency_code
})
}
currency_code: carts[0].currency_code,
}),
},
}).config({ name: "retrieve-variant" })
// TODO add more steps
@@ -580,7 +580,7 @@ Next, you'll retrieve the variant's real-time price using the `getVariantMetalPr
```ts title="src/workflows/add-custom-to-cart.ts"
import {
getVariantMetalPricesStep,
GetVariantMetalPricesStepInput
GetVariantMetalPricesStepInput,
} from "./steps/get-variant-metal-prices"
```
@@ -590,7 +590,7 @@ Then, replace the `TODO` in the workflow with the following:
const price = getVariantMetalPricesStep({
variant: variants[0],
currencyCode: carts[0].currency_code,
quantity: item.quantity
quantity: item.quantity,
} as unknown as GetVariantMetalPricesStepInput)
// TODO add item with custom price to cart
@@ -610,19 +610,19 @@ Then, replace the `TODO` in the workflow with the following:
```ts title="src/workflows/add-custom-to-cart.ts"
const itemToAdd = transform({
item,
price
price,
}, (data) => {
return [{
...data.item,
unit_price: data.price
unit_price: data.price,
}]
})
addToCartWorkflow.runAsStep({
input: {
items: itemToAdd,
cart_id
}
cart_id,
},
})
// TODO retrieve and return cart
@@ -653,7 +653,7 @@ const { data: updatedCarts } = useQueryGraphStep({
}).config({ name: "refetch-cart" })
return new WorkflowResponse({
cart: updatedCarts[0]
cart: updatedCarts[0],
})
```
@@ -699,8 +699,8 @@ export const POST = async (
.run({
input: {
cart_id: id,
item
}
item,
},
})
res.status(200).json({ cart: result.cart })
@@ -740,7 +740,7 @@ import {
validateAndTransformBody,
} from "@medusajs/framework/http"
import {
StoreAddCartLineItem
StoreAddCartLineItem,
} from "@medusajs/medusa/api/store/carts/validators"
export default defineMiddlewares({
@@ -750,10 +750,10 @@ export default defineMiddlewares({
method: "POST",
middlewares: [
validateAndTransformBody(
StoreAddCartLineItem,
)
]
}
StoreAddCartLineItem
),
],
},
],
})
```
@@ -624,6 +624,9 @@ export const createWishlistStep = createStep(
return new StepResponse(wishlist, wishlist.id)
},
async (id, { container }) => {
if (!id) {
return
}
const wishlistModuleService: WishlistModuleService =
container.resolve(WISHLIST_MODULE)
@@ -1108,6 +1111,9 @@ export const createWishlistItemStep = createStep(
return new StepResponse(item, item.id)
},
async (id, { container }) => {
if (!id) {
return
}
const wishlistModuleService: WishlistModuleService =
container.resolve(WISHLIST_MODULE)
@@ -524,6 +524,9 @@ Since the `createRestockSubscriptionStep` creates a restock subscription, you'll
export const createOrGetRestockSubscriptionsStep = createStep(
// ...
async (restockSubscription, { container }) => {
if (!restockSubscription) {
return
}
const restockModuleService: RestockModuleService = container.resolve(
RESTOCK_MODULE
)
@@ -576,6 +579,9 @@ export const updateRestockSubscriptionStep = createStep(
return new StepResponse(restockSubscription, oldData)
},
async (restockSubscription, { container }) => {
if (!restockSubscription) {
return
}
const restockModuleService: RestockModuleService = container.resolve(
RESTOCK_MODULE
)
@@ -745,20 +745,16 @@ To implement and expose a feature that manipulates data, you create a workflow t
So, you'll start by implementing the functionality to create a user in a workflow. The workflow has two steps:
1. Create the user in the database.
1. Create the user in the database. This user is either a restaurant admin or a driver. So, you'll create two separate steps for each user type.
2. Set the actor type of the users authentication identity (created by the `/auth/{actor_type}/{provider}/register` API route). For this step, youll use `setAuthAppMetadataStep` from Medusa's core workflows.
To implement the first step, create the file `src/workflows/user/steps/create-user.ts` with the following content:
You'll start by implementing the steps to create a restaurant admin. Create the file `
```ts title="src/workflows/user/steps/create-user.ts" collapsibleLines="1-9" expandButtonLabel="Show Imports"
import { MedusaError } from "@medusajs/framework/utils"
To implement the first step, create the file `src/workflows/user/steps/create-restaurant-admin.ts` with the following content:
```ts title="src/workflows/user/steps/create-restaurant-admin.ts"
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
import {
CreateDriverInput,
CreateRestaurantAdminInput,
} from "../workflows/create-user"
import { RESTAURANT_MODULE } from "../../../modules/restaurant"
import { DELIVERY_MODULE } from "../../../modules/delivery"
export type CreateRestaurantAdminInput = {
restaurant_id: string;
@@ -767,6 +763,43 @@ export type CreateRestaurantAdminInput = {
last_name: string;
};
export const createRestaurantAdminStep = createStep(
"create-restaurant-admin-step",
async (
data: CreateRestaurantAdminInput,
{ container }
) => {
const restaurantModuleService = container.resolve(RESTAURANT_MODULE)
const restaurantAdmin = await restaurantModuleService.createRestaurantAdmins(
data
)
return new StepResponse(restaurantAdmin, restaurantAdmin.id)
},
async (id, { container }) => {
if (!id) {
return
}
const service = container.resolve(RESTAURANT_MODULE)
await service.deleteRestaurantAdmins(id)
}
)
```
This creates a step that accepts as input the data of the restaurant to create and its type. The step creates a restaurant admin and returns it.
In the compensation function, you delete the created restaurant admin if an error occurs.
Then, to implement the step that creates a driver, create the file `src/workflows/user/steps/create-driver.ts` with the following content:
```ts title="src/workflows/user/steps/create-driver.ts"
import { createStep, StepResponse } from "@medusajs/framework/workflows-sdk"
import { RESTAURANT_MODULE } from "../../../modules/restaurant"
import { DELIVERY_MODULE } from "../../../modules/delivery"
export type CreateDriverInput = {
email: string;
first_name: string;
@@ -775,101 +808,49 @@ export type CreateDriverInput = {
avatar_url?: string;
};
type CreateUserStepInput = (CreateRestaurantAdminInput | CreateDriverInput) & {
actor_type: "restaurant" | "driver";
};
export const createUserStep = createStep(
"create-user-step",
export const createDriverStep = createStep(
"create-driver-step",
async (
{ actor_type, ...data }: CreateUserStepInput,
data: CreateDriverInput,
{ container }
) => {
if (actor_type === "restaurant") {
// TODO create restaurant admin
} else if (actor_type === "driver") {
// TODO create driver
const deliveryModuleService = container.resolve(DELIVERY_MODULE)
const driver = await deliveryModuleService.createDrivers(data)
return new StepResponse(driver, driver.id)
},
async (id, { container }) => {
if (!id) {
return
}
throw MedusaError.Types.INVALID_DATA
},
function ({ id, actor_type }, { container }) {
// TODO add compensation actions
const service = container.resolve(RESTAURANT_MODULE)
await service.deleteRestaurantAdmins(id)
}
)
```
This creates a step that accepts as input the data of the user to create and its type. If the type is `restaurant`, then a restaurant admin is created. If the type is a `driver`, then a driver is created. Otherwise, an error is thrown.
This creates a step that accepts as input the data of the driver to create. The step creates a driver and returns it.
Replace the first `TODO` with the following to create a restaurant admin:
```ts title="src/workflows/user/steps/create-user.ts"
const service = container.resolve(RESTAURANT_MODULE)
const restaurantAdmin = await service.createRestaurantAdmins(
data
)
return new StepResponse(restaurantAdmin, {
id: restaurantAdmin.id,
actor_type: actor_type as string,
})
```
This resolves the Restaurant Modules main service, creates the restaurant admin, and returns it.
Then, replace the second `TODO` with the following to create a driver:
```ts title="src/workflows/user/steps/create-user.ts"
const service = container.resolve(DELIVERY_MODULE)
const driver = await service.createDrivers(data)
return new StepResponse(driver, {
id: driver.id,
actor_type: actor_type as string,
})
```
This resolves the Driver Modules main service, creates the driver, and returns it.
Finally, replace the remaining `TODO` with the following compensation action:
```ts title="src/workflows/user/steps/create-user.ts"
if (actor_type === "restaurant") {
const service = container.resolve(RESTAURANT_MODULE)
return service.deleteRestaurantAdmins(id)
} else {
const service = container.resolve(DELIVERY_MODULE)
return service.deleteDrivers(id)
}
```
In the compensation function, if the `actor_type` is a restaurant, you delete the created restaurant admin. Otherwise, you delete the created driver.
In the compensation function, you delete the created driver if an error occurs.
Next, create the workflow in the file `src/workflows/user/workflows/create-user.ts`:
```ts title="src/workflows/user/workflows/create-user.ts" collapsibleLines="1-12" expandButtonLabel="Show Imports"
```ts title="src/workflows/user/workflows/create-user.ts" collapsibleLines="1-13" expandButtonLabel="Show Imports"
import { setAuthAppMetadataStep } from "@medusajs/medusa/core-flows"
import {
createWorkflow,
transform,
when,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import { CreateDriverInput, createDriverStep } from "../steps/create-driver"
import {
CreateDriverInput,
CreateRestaurantAdminInput,
createUserStep,
} from "../steps/create-user"
type WorkflowInput = {
user: (CreateRestaurantAdminInput | CreateDriverInput) & {
actor_type: "restaurant" | "driver";
};
auth_identity_id: string;
};
createRestaurantAdminStep,
} from "../steps/create-restaurant-admin"
export type CreateUserWorkflowInput = {
user: (CreateRestaurantAdminInput | CreateDriverInput) & {
@@ -884,7 +865,6 @@ export const createUserWorkflow = createWorkflow(
// TODO create user
}
)
```
In this file, you create the necessary types and the workflow with a `TODO`.
@@ -892,20 +872,38 @@ In this file, you create the necessary types and the workflow with a `TODO`.
Replace the `TODO` with the following:
export const createUserHighlights = [
["1", "createUserStep", "Create the user."],
["3", "transform", "Define the input for the next step."],
["6", "key", "Set the key based on whether the user is a restaurant admin or a driver."],
["13", "setAuthAppMetadataStep", "Update the authentication identity with to associate it with the new user."]
["1", "restaurantUser", "Create a restaurant user if the actor type is `restaurant`."],
["8", "driverUser", "Create a driver if the actor type is `driver`."],
["15", "transform", "Define the input for the next step."],
["27", "setAuthAppMetadataStep", "Update the authentication identity to associate it with the new user."]
]
```ts title="src/workflows/user/workflows/create-user.ts" highlights={createUserHighlights}
const user = createUserStep(input.user)
const restaurantUser = when(input, (input) => input.user.actor_type === "restaurant")
.then(() => {
return createRestaurantAdminStep(
input.user as CreateRestaurantAdminInput
)
})
const authUserInput = transform({ input, user }, (data) => ({
authIdentityId: data.input.auth_identity_id,
actorType: data.input.user.actor_type,
value: data.user.id,
}))
const driverUser = when(input, (input) => input.user.actor_type === "driver")
.then(() => {
return createDriverStep(
input.user as CreateDriverInput
)
})
const { user, authUserInput } = transform({ input, restaurantUser, driverUser }, (data) => {
const user = data.restaurantUser || data.driverUser
return {
user,
authUserInput: {
authIdentityId: data.input.auth_identity_id,
actorType: data.input.user.actor_type,
value: user.id,
},
}
})
setAuthAppMetadataStep(authUserInput)
@@ -914,10 +912,11 @@ return new WorkflowResponse(user)
In the workflow, you:
1. Use the `createUserStep` to create the user.
2. Use `transform` to create the input to be passed to the next step.
3. Use `setAuthAppMetadataStep` from Medusa's core workflows to update the authentication identity and associate it with the new user.
4. Return the created user.
1. Create a restaurant admin if the actor type is `restaurant`.
2. Create a driver if the actor type is `driver`.
3. Use `transform` to create the input to be passed to the next step and return the created user, which is either a restaurant admin or a driver.
4. Use `setAuthAppMetadataStep` from Medusa's core workflows to update the authentication identity and associate it with the new user.
5. Return the created user.
### Create API Route
@@ -1294,7 +1293,7 @@ import {
createProductsWorkflow,
createRemoteLinkStep,
} from "@medusajs/medusa/core-flows"
import { CreateProductDTO } from "@medusajs/framework/types"
import { CreateProductWorkflowInputDTO } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"
import {
WorkflowResponse,
@@ -1304,7 +1303,7 @@ import {
import { RESTAURANT_MODULE } from "../../../modules/restaurant"
type WorkflowInput = {
products: CreateProductDTO[];
products: CreateProductWorkflowInputDTO[];
restaurant_id: string;
};
@@ -468,7 +468,7 @@ export const POST = async (
.resolve("marketplaceModuleService")
// create vendor
let vendor = await marketplaceModuleService.createVendors([vendorData])
let vendor = await marketplaceModuleService.createVendors(vendorData)
// create vendor admin
await createVendorAdminWorkflow(req.scope)
@@ -1705,7 +1705,7 @@ class SubscriptionModuleService extends MedusaService({
Subscription,
}) {
// ...
async recordNewSubscriptionOrder(id: string): Promise<SubscriptionData[]> {
async recordNewSubscriptionOrder(id: string) {
const subscription = await this.retrieveSubscription(id)
const orderDate = new Date()
@@ -272,7 +272,7 @@ const updateCartAddress = (customerAddress: Record<string, unknown>) => {
city: customerAddress.city || "",
country_code: customerAddress.country_code || cart.region?.countries?.[0].iso_2,
province: customerAddress.province || "",
phone: customerAddress.phone || ""
phone: customerAddress.phone || "",
}
fetch(`http://localhost:9000/store/carts/${cart.id}`, {
@@ -284,8 +284,8 @@ const updateCartAddress = (customerAddress: Record<string, unknown>) => {
},
body: JSON.stringify({
shipping_address: address,
billing_address: address
})
billing_address: address,
}),
})
.then((res) => res.json())
.then(({ cart: updatedCart }) => {
@@ -312,11 +312,11 @@ export const react2Highlights = [
```tsx highlights={react2Highlights}
"use client" // include with Next.js 13+
import { useEffect, useState } from "react";
import { useCart } from "../../../providers/cart";
import { useCustomer } from "../../../providers/customer";
import { useEffect, useState } from "react"
import { useCart } from "../../../providers/cart"
import { useCustomer } from "../../../providers/customer"
export default function CheckoutAddressStep () {
export default function CheckoutAddressStep() {
const { cart, setCart } = useCart()
const { customer } = useCustomer()
const [loading, setLoading] = useState(false)
@@ -349,7 +349,7 @@ export default function CheckoutAddressStep () {
city: customerAddress.city || "",
country_code: customerAddress.country_code || cart.region?.countries?.[0].iso_2,
province: customerAddress.province || "",
phone: customerAddress.phone || ""
phone: customerAddress.phone || "",
}
fetch(`http://localhost:9000/store/carts/${cart.id}`, {
@@ -361,8 +361,8 @@ export default function CheckoutAddressStep () {
},
body: JSON.stringify({
shipping_address: address,
billing_address: address
})
billing_address: address,
}),
})
.then((res) => res.json())
.then(({ cart: updatedCart }) => {
@@ -69,7 +69,7 @@ module.exports = defineConfig({
optimizeDeps: {
include: ["qs"],
},
};
}
},
},
// ...
+5 -5
View File
@@ -116,14 +116,14 @@ export const generatedEditDates = {
"app/recipes/digital-products/page.mdx": "2025-01-06T11:19:35.623Z",
"app/recipes/ecommerce/page.mdx": "2024-10-22T11:01:01.218Z",
"app/recipes/integrate-ecommerce-stack/page.mdx": "2024-12-09T13:03:35.846Z",
"app/recipes/marketplace/examples/vendors/page.mdx": "2025-01-06T11:19:35.639Z",
"app/recipes/marketplace/examples/vendors/page.mdx": "2025-02-11T12:59:00.539Z",
"app/recipes/marketplace/page.mdx": "2024-10-03T13:07:44.153Z",
"app/recipes/multi-region-store/page.mdx": "2024-10-03T13:07:13.813Z",
"app/recipes/omnichannel/page.mdx": "2024-10-03T13:07:14.384Z",
"app/recipes/oms/page.mdx": "2024-07-01T10:21:19+03:00",
"app/recipes/personalized-products/page.mdx": "2024-10-03T13:07:44.153Z",
"app/recipes/pos/page.mdx": "2024-10-03T13:07:13.964Z",
"app/recipes/subscriptions/examples/standard/page.mdx": "2025-02-04T07:37:14.832Z",
"app/recipes/subscriptions/examples/standard/page.mdx": "2025-02-11T14:21:09.533Z",
"app/recipes/subscriptions/page.mdx": "2024-10-03T13:07:44.155Z",
"app/recipes/page.mdx": "2024-07-11T15:56:41+00:00",
"app/service-factory-reference/methods/create/page.mdx": "2024-07-31T17:01:33+03:00",
@@ -577,7 +577,7 @@ export const generatedEditDates = {
"app/medusa-cli/commands/start/page.mdx": "2024-08-28T10:44:19.952Z",
"app/medusa-cli/commands/telemtry/page.mdx": "2025-01-16T09:51:24.323Z",
"app/medusa-cli/commands/user/page.mdx": "2024-08-28T10:44:52.489Z",
"app/recipes/marketplace/examples/restaurant-delivery/page.mdx": "2024-12-11T10:05:52.851Z",
"app/recipes/marketplace/examples/restaurant-delivery/page.mdx": "2025-02-11T15:51:17.490Z",
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminCreateCustomerGroup/page.mdx": "2024-12-09T13:21:33.569Z",
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminCreateReservation/page.mdx": "2024-12-09T13:21:34.505Z",
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminCustomerGroup/page.mdx": "2024-12-23T13:57:05.262Z",
@@ -5583,7 +5583,7 @@ export const generatedEditDates = {
"references/modules/sales_channel_models/page.mdx": "2024-12-10T14:55:13.205Z",
"references/types/DmlTypes/types/types.DmlTypes.KnownDataTypes/page.mdx": "2024-12-17T16:57:19.922Z",
"references/types/DmlTypes/types/types.DmlTypes.RelationshipTypes/page.mdx": "2024-12-10T14:54:55.435Z",
"app/recipes/commerce-automation/restock-notification/page.mdx": "2025-01-23T10:18:28.126Z",
"app/recipes/commerce-automation/restock-notification/page.mdx": "2025-02-11T13:29:56.235Z",
"app/troubleshooting/workflow-errors/page.mdx": "2024-12-11T08:44:36.598Z",
"app/integrations/guides/shipstation/page.mdx": "2025-01-07T14:40:42.561Z",
"app/nextjs-starter/guides/customize-stripe/page.mdx": "2024-12-25T14:48:55.877Z",
@@ -5860,7 +5860,7 @@ export const generatedEditDates = {
"references/core_flows/types/core_flows.ThrowUnlessPaymentCollectionNotePaidInput/page.mdx": "2025-01-17T16:43:25.819Z",
"references/core_flows/types/core_flows.ValidatePaymentsRefundStepInput/page.mdx": "2025-01-17T16:43:26.128Z",
"references/core_flows/types/core_flows.ValidateRefundStepInput/page.mdx": "2025-01-17T16:43:26.124Z",
"app/plugins/guides/wishlist/page.mdx": "2025-02-05T09:12:22.965Z",
"app/plugins/guides/wishlist/page.mdx": "2025-02-11T14:25:46.734Z",
"app/plugins/page.mdx": "2025-01-22T09:36:37.745Z",
"app/admin-components/components/data-table/page.mdx": "2025-01-22T16:01:01.279Z",
"references/order_models/variables/order_models.Order/page.mdx": "2025-01-27T11:43:58.788Z",