docs: general updates after 2.5.0 update (#11402)
This commit is contained in:
@@ -288,7 +288,7 @@ const columns = [
|
||||
}),
|
||||
columnHelper.accessor("name", {
|
||||
header: "Name",
|
||||
})
|
||||
}),
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
+20
-20
@@ -337,10 +337,10 @@ medusaIntegrationTestRunner({
|
||||
{
|
||||
type: "publishable",
|
||||
title: "Test Key",
|
||||
created_by: ""
|
||||
}
|
||||
]
|
||||
}
|
||||
created_by: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
})).result[0]
|
||||
})
|
||||
describe("GET /custom", () => {
|
||||
@@ -349,8 +349,8 @@ medusaIntegrationTestRunner({
|
||||
`/store/custom`,
|
||||
{
|
||||
headers: {
|
||||
"x-publishable-api-key": pak.token
|
||||
}
|
||||
"x-publishable-api-key": pak.token,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@@ -421,13 +421,13 @@ medusaIntegrationTestRunner({
|
||||
provider: "emailpass",
|
||||
entity_id: "admin@medusa.js",
|
||||
provider_metadata: {
|
||||
password: "supersecret"
|
||||
}
|
||||
}
|
||||
password: "supersecret",
|
||||
},
|
||||
},
|
||||
],
|
||||
app_metadata: {
|
||||
user_id: user.id
|
||||
}
|
||||
user_id: user.id,
|
||||
},
|
||||
})
|
||||
|
||||
const token = jwt.sign(
|
||||
@@ -503,13 +503,13 @@ medusaIntegrationTestRunner({
|
||||
provider: "emailpass",
|
||||
entity_id: "customer@medusa.js",
|
||||
provider_metadata: {
|
||||
password: "supersecret"
|
||||
}
|
||||
}
|
||||
password: "supersecret",
|
||||
},
|
||||
},
|
||||
],
|
||||
app_metadata: {
|
||||
user_id: customer.id
|
||||
}
|
||||
user_id: customer.id,
|
||||
},
|
||||
})
|
||||
|
||||
const token = jwt.sign(
|
||||
@@ -533,10 +533,10 @@ medusaIntegrationTestRunner({
|
||||
{
|
||||
type: "publishable",
|
||||
title: "Test Key",
|
||||
created_by: ""
|
||||
}
|
||||
]
|
||||
}
|
||||
created_by: "",
|
||||
},
|
||||
],
|
||||
},
|
||||
})).result[0]
|
||||
|
||||
headers["x-publishable-api-key"] = pak.token
|
||||
|
||||
+1
-1
@@ -126,7 +126,7 @@ medusaIntegrationTestRunner({
|
||||
it("returns message", async () => {
|
||||
const { errors } = await helloWorldWorkflow(getContainer())
|
||||
.run({
|
||||
throwOnError: false
|
||||
throwOnError: false,
|
||||
})
|
||||
|
||||
expect(errors.length).toBeGreaterThan(0)
|
||||
|
||||
@@ -10,59 +10,59 @@ In this chapter, you'll learn how to manage relationships between data models wh
|
||||
|
||||
### BelongsTo Side of One-to-One
|
||||
|
||||
When you create a record of a data model that belongs to another through a one-to-one relation, pass the ID of the other data model's record in the relation property.
|
||||
When you create a record of a data model that belongs to another through a one-to-one relation, pass the ID of the other data model's record in the `{relation}_id` property, where `{relation}` is the name of the relation property.
|
||||
|
||||
For example, assuming you have the [User and Email data models from the previous chapter](../relationships/page.mdx#one-to-one-relationship), set an email's user ID as follows:
|
||||
|
||||
export const belongsHighlights = [
|
||||
["4", "user", "The ID of the user the email belongs to."],
|
||||
["11", "user", "The ID of the user the email belongs to."]
|
||||
["4", "user_id", "The ID of the user the email belongs to."],
|
||||
["11", "user_id", "The ID of the user the email belongs to."]
|
||||
]
|
||||
|
||||
```ts highlights={belongsHighlights}
|
||||
// when creating an email
|
||||
const email = await helloModuleService.createEmails({
|
||||
// other properties...
|
||||
user: "123",
|
||||
user_id: "123",
|
||||
})
|
||||
|
||||
// when updating an email
|
||||
const email = await helloModuleService.updateEmails({
|
||||
id: "321",
|
||||
// other properties...
|
||||
user: "123",
|
||||
user_id: "123",
|
||||
})
|
||||
```
|
||||
|
||||
In the example above, you pass the `user` property when creating or updating an email to specify the user it belongs to.
|
||||
In the example above, you pass the `user_id` property when creating or updating an email to specify the user it belongs to.
|
||||
|
||||
### HasOne Side
|
||||
|
||||
When you create a record of a data model that has one of another, pass the ID of the other data model's record in the relation property.
|
||||
When you create a record of a data model that has one of another, pass the ID of the other data model's record in the `{relation}_id` property, where `{relation}` is the name of the relation property.
|
||||
|
||||
For example, assuming you have the [User and Email data models from the previous chapter](../relationships/page.mdx#one-to-one-relationship), set a user's email ID as follows:
|
||||
|
||||
export const hasOneHighlights = [
|
||||
["4", "email", "The ID of the email that the user has."],
|
||||
["11", "email", "The ID of the email that the user has."]
|
||||
["4", "email_id", "The ID of the email that the user has."],
|
||||
["11", "email_id", "The ID of the email that the user has."]
|
||||
]
|
||||
|
||||
```ts highlights={hasOneHighlights}
|
||||
// when creating a user
|
||||
const user = await helloModuleService.createUsers({
|
||||
// other properties...
|
||||
email: "123",
|
||||
email_id: "123",
|
||||
})
|
||||
|
||||
// when updating a user
|
||||
const user = await helloModuleService.updateUsers({
|
||||
id: "321",
|
||||
// other properties...
|
||||
email: "123",
|
||||
email_id: "123",
|
||||
})
|
||||
```
|
||||
|
||||
In the example above, you pass the `email` property when creating or updating a user to specify the email it has.
|
||||
In the example above, you pass the `email_id` property when creating or updating a user to specify the email it has.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ const { data } = await query.graph({
|
||||
fields: ["*"],
|
||||
context: QueryContext({
|
||||
lang: "es",
|
||||
})
|
||||
}),
|
||||
})
|
||||
```
|
||||
|
||||
@@ -58,7 +58,7 @@ import Author from "./models/author"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
Post,
|
||||
Author
|
||||
Author,
|
||||
}){
|
||||
// @ts-ignore
|
||||
async listPosts(
|
||||
@@ -125,8 +125,8 @@ const { data } = await query.graph({
|
||||
lang: "es",
|
||||
author: QueryContext({
|
||||
lang: "es",
|
||||
})
|
||||
})
|
||||
}),
|
||||
}),
|
||||
})
|
||||
```
|
||||
|
||||
@@ -144,7 +144,7 @@ import Author from "./models/author"
|
||||
|
||||
class BlogModuleService extends MedusaService({
|
||||
Post,
|
||||
Author
|
||||
Author,
|
||||
}){
|
||||
// @ts-ignore
|
||||
async listPosts(
|
||||
@@ -168,7 +168,7 @@ class BlogModuleService extends MedusaService({
|
||||
author: {
|
||||
...post.author,
|
||||
name: isAuthorLangEs ? post.author.name + " en español" : post.author.name,
|
||||
}
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -201,8 +201,8 @@ const { data } = await query.graph({
|
||||
context: {
|
||||
post: QueryContext({
|
||||
lang: "es",
|
||||
})
|
||||
}
|
||||
}),
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ import { createStep, createWorkflow } from "@medusajs/framework/workflows-sdk"
|
||||
|
||||
const step1 = createStep(
|
||||
{
|
||||
name: "step-1"
|
||||
name: "step-1",
|
||||
},
|
||||
async () => {
|
||||
console.log("Hello from step 1")
|
||||
@@ -61,7 +61,7 @@ export const helloWorkflow = createWorkflow(
|
||||
{
|
||||
name: "hello-workflow",
|
||||
retentionTime: 99999,
|
||||
store: true
|
||||
store: true,
|
||||
},
|
||||
() => {
|
||||
step1()
|
||||
@@ -98,8 +98,8 @@ export const retrieveHighlights = [
|
||||
]
|
||||
|
||||
```ts title="src/workflows/[id]/route.ts" highlights={retrieveHighlights}
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework";
|
||||
import { Modules } from "@medusajs/framework/utils";
|
||||
import { MedusaRequest, MedusaResponse } from "@medusajs/framework"
|
||||
import { Modules } from "@medusajs/framework/utils"
|
||||
|
||||
export async function GET(
|
||||
req: MedusaRequest,
|
||||
@@ -112,11 +112,11 @@ export async function GET(
|
||||
)
|
||||
|
||||
const [workflowExecution] = await workflowEngineService.listWorkflowExecutions({
|
||||
transaction_id: transaction_id
|
||||
transaction_id: transaction_id,
|
||||
})
|
||||
|
||||
res.json({
|
||||
workflowExecution
|
||||
workflowExecution,
|
||||
})
|
||||
}
|
||||
```
|
||||
@@ -165,7 +165,7 @@ To check if a stored workflow execution failed, you can check its `state` proper
|
||||
```ts
|
||||
if (workflowExecution.state === "failed") {
|
||||
return res.status(500).json({
|
||||
error: "Workflow failed"
|
||||
error: "Workflow failed",
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
@@ -38,7 +38,7 @@ export const generatedEditDates = {
|
||||
"app/learn/fundamentals/workflows/long-running-workflow/page.mdx": "2025-01-27T08:45:19.028Z",
|
||||
"app/learn/fundamentals/workflows/constructor-constraints/page.mdx": "2024-12-12T15:09:41.179Z",
|
||||
"app/learn/fundamentals/data-models/write-migration/page.mdx": "2024-11-11T15:27:59.794Z",
|
||||
"app/learn/fundamentals/data-models/manage-relationships/page.mdx": "2024-12-12T14:23:26.254Z",
|
||||
"app/learn/fundamentals/data-models/manage-relationships/page.mdx": "2025-02-11T15:53:12.541Z",
|
||||
"app/learn/fundamentals/modules/remote-query/page.mdx": "2024-07-21T21:20:24+02:00",
|
||||
"app/learn/fundamentals/modules/options/page.mdx": "2025-01-16T09:21:38.244Z",
|
||||
"app/learn/fundamentals/data-models/relationships/page.mdx": "2025-02-03T08:01:18.094Z",
|
||||
@@ -59,9 +59,9 @@ export const generatedEditDates = {
|
||||
"app/learn/fundamentals/data-models/index/page.mdx": "2024-10-21T13:30:21.368Z",
|
||||
"app/learn/fundamentals/custom-cli-scripts/page.mdx": "2024-10-23T07:08:55.898Z",
|
||||
"app/learn/fundamentals/data-models/property-types/page.mdx": "2024-12-12T10:41:32.999Z",
|
||||
"app/learn/debugging-and-testing/testing-tools/integration-tests/api-routes/page.mdx": "2025-02-07T12:16:51.237Z",
|
||||
"app/learn/debugging-and-testing/testing-tools/integration-tests/api-routes/page.mdx": "2025-02-11T15:56:03.835Z",
|
||||
"app/learn/debugging-and-testing/testing-tools/integration-tests/page.mdx": "2024-12-09T15:52:01.019Z",
|
||||
"app/learn/debugging-and-testing/testing-tools/integration-tests/workflows/page.mdx": "2025-01-31T13:19:02.586Z",
|
||||
"app/learn/debugging-and-testing/testing-tools/integration-tests/workflows/page.mdx": "2025-02-11T15:56:03.835Z",
|
||||
"app/learn/debugging-and-testing/testing-tools/page.mdx": "2025-01-31T13:19:02.587Z",
|
||||
"app/learn/debugging-and-testing/testing-tools/unit-tests/module-example/page.mdx": "2024-09-02T11:04:27.232Z",
|
||||
"app/learn/debugging-and-testing/testing-tools/unit-tests/page.mdx": "2024-09-02T11:03:26.997Z",
|
||||
@@ -88,7 +88,7 @@ export const generatedEditDates = {
|
||||
"app/learn/customization/extend-features/extend-create-product/page.mdx": "2025-01-06T11:18:58.250Z",
|
||||
"app/learn/customization/custom-features/page.mdx": "2024-12-09T10:46:28.593Z",
|
||||
"app/learn/customization/customize-admin/page.mdx": "2024-12-09T11:02:38.801Z",
|
||||
"app/learn/customization/customize-admin/route/page.mdx": "2025-02-05T09:09:11.472Z",
|
||||
"app/learn/customization/customize-admin/route/page.mdx": "2025-02-11T15:56:03.835Z",
|
||||
"app/learn/customization/customize-admin/widget/page.mdx": "2025-02-05T09:10:18.163Z",
|
||||
"app/learn/customization/extend-features/define-link/page.mdx": "2024-12-09T11:02:39.346Z",
|
||||
"app/learn/customization/extend-features/page.mdx": "2024-12-09T11:02:39.244Z",
|
||||
@@ -110,11 +110,11 @@ export const generatedEditDates = {
|
||||
"app/learn/fundamentals/data-models/check-constraints/page.mdx": "2024-12-06T14:34:50.384Z",
|
||||
"app/learn/fundamentals/module-links/link/page.mdx": "2025-01-06T09:27:25.604Z",
|
||||
"app/learn/conventions/ts-aliases/page.mdx": "2025-01-23T15:01:15.403Z",
|
||||
"app/learn/fundamentals/workflows/store-executions/page.mdx": "2025-01-27T08:45:19.028Z",
|
||||
"app/learn/fundamentals/workflows/store-executions/page.mdx": "2025-02-11T15:56:03.835Z",
|
||||
"app/learn/fundamentals/plugins/create/page.mdx": "2025-02-07T10:26:53.059Z",
|
||||
"app/learn/fundamentals/plugins/page.mdx": "2025-01-22T10:14:10.433Z",
|
||||
"app/learn/customization/reuse-customizations/page.mdx": "2025-01-22T10:01:57.665Z",
|
||||
"app/learn/update/page.mdx": "2025-01-27T08:45:19.030Z",
|
||||
"app/learn/fundamentals/module-links/query-context/page.mdx": "2025-02-03T17:04:24.479Z",
|
||||
"app/learn/fundamentals/module-links/query-context/page.mdx": "2025-02-11T15:56:03.835Z",
|
||||
"app/learn/fundamentals/admin/environment-variables/page.mdx": "2025-02-06T13:29:46.800Z"
|
||||
}
|
||||
+11359
-11359
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||

|
||||
|
||||
```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
|
||||

|
||||
|
||||
```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 user’s authentication identity (created by the `/auth/{actor_type}/{provider}/register` API route). For this step, you’ll 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 Module’s 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 Module’s 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"],
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
},
|
||||
// ...
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user