diff --git a/www/apps/resources/app/commerce-modules/auth/create-actor-type/page.mdx b/www/apps/resources/app/commerce-modules/auth/create-actor-type/page.mdx index 770338bd91..084a6c8c00 100644 --- a/www/apps/resources/app/commerce-modules/auth/create-actor-type/page.mdx +++ b/www/apps/resources/app/commerce-modules/auth/create-actor-type/page.mdx @@ -295,3 +295,124 @@ curl 'http://localhost:9000/manager/me' \ ``` Whenever you want to log in as a manager, use the `/auth/manager/emailpass` API route, as explained in step 3. + +--- + +## Delete User of Actor Type + +When you delete a user of the actor type, you must update its auth identity to remove the association to the user. + +For example, create the following workflow that deletes a manager and updates its auth identity, create the file `src/workflows/delete-manager.ts` with the following content: + +```ts title="src/workflows/delete-manager.ts" collapsibleLines="1-6" expandButtonLabel="Show Imports" +import { + createStep, + StepResponse, +} from "@medusajs/workflows-sdk" +import ManagerModuleService from "../modules/manager/service" + +export type DeleteManagerWorkflow = { + id: string +} + +const deleteManagerStep = createStep( + "delete-manager-step", + async ( + { id }: DeleteManagerWorkflow, + { container }) => { + const managerModuleService: ManagerModuleService = + container.resolve("managerModuleService") + + const manager = await managerModuleService.retrieve(id) + + await managerModuleService.deleteManagers(id) + + return new StepResponse(undefined, { manager }) + }, + async ({ manager }, { container }) => { + const managerModuleService: ManagerModuleService = + container.resolve("managerModuleService") + + await managerModuleService.createManagers(manager) + } + ) +``` + +You add a step that deletes the manager using the `deleteManagers` method of the module's main service. In the compensation function, you create the manager again. + +Next, in the same file, add the workflow that deletes a manager: + +export const deleteHighlights = [ + ["30", "manager_id", "If your actor type has a different name, such as vendor, change it to be `{actor_type}_id`."] +] + +```ts title="src/workflows/delete-manager.ts" collapsibleLines="1-15" expandButtonLabel="Show Imports" highlights={deleteHighlights} +// other imports +import { MedusaError } from "@medusajs/utils" +import { + WorkflowData, + WorkflowResponse, + createWorkflow, + transform, +} from "@medusajs/workflows-sdk" +import { + setAuthAppMetadataStep, + useRemoteQueryStep, +} from "@medusajs/core-flows" + +// ... + +export const deleteManagerWorkflow = createWorkflow( + "delete-manager", + ( + input: WorkflowData + ): WorkflowResponse => { + deleteManagerStep(input) + + const authIdentities = useRemoteQueryStep({ + entry_point: "auth_identity", + fields: ["id"], + variables: { + filters: { + app_metadata: { + // the ID is of the format `{actor_type}_id`. + manager_id: input.id, + }, + }, + }, + }) + + const authIdentity = transform( + { authIdentities }, + ({ authIdentities }) => { + const authIdentity = authIdentities[0] + + if (!authIdentity) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + "Auth identity not found" + ) + } + + return authIdentity + } + ) + + setAuthAppMetadataStep({ + authIdentityId: authIdentity.id, + actorType: "manager", + value: null, + }) + + return new WorkflowResponse(input.id) + } +) +``` + +In the workflow, you: + +1. Use the `deleteManagerStep` defined earlier to delete the manager. +2. Retrieve the auth identity of the manager using Query. To do that, you filter the `app_metadata` property of an auth identity, which holds the user's ID under `{actor_type_name}_id`. So, in this case, it's `manager_id`. +3. Check that the auth identity exist, then, update the auth identity to remove the ID of the manager from it. + +You can use this workflow when deleting a manager, such as in an API route. diff --git a/www/apps/resources/app/recipes/marketplace/examples/restaurant-delivery/page.mdx b/www/apps/resources/app/recipes/marketplace/examples/restaurant-delivery/page.mdx index b28cfde8de..df9b714b08 100644 --- a/www/apps/resources/app/recipes/marketplace/examples/restaurant-delivery/page.mdx +++ b/www/apps/resources/app/recipes/marketplace/examples/restaurant-delivery/page.mdx @@ -858,11 +858,7 @@ const user = createUserStep(input.user) const authUserInput = transform({ input, user }, (data) => ({ authIdentityId: data.input.auth_identity_id, actorType: data.input.user.actor_type, - key: - data.input.user.actor_type === "restaurant" - ? "restaurant_id" - : "driver_id", - value: user.id, + value: data.user.id, })) setAuthAppMetadataStep(authUserInput) @@ -1034,7 +1030,201 @@ This returns the created driver user. --- -## Step 8: Create Restaurant Product API Route +## Step 8: Delete Restaurant Admin API Route + +In this step, you'll create a workflow that deletes the restaurant admin and its association to its auth identity, then use it in an API route. + + + +The same logic can be applied to delete a driver. + + + +### Create deleteRestaurantAdminStep + +First, create the step that deletes the restaurant admin at `restaurant-marketplace/src/workflows/restaurant/steps/delete-restaurant-admin.ts`: + +```ts title="restaurant-marketplace/src/workflows/restaurant/steps/delete-restaurant-admin.ts" +import { + createStep, + StepResponse, +} from "@medusajs/workflows-sdk" +import { RESTAURANT_MODULE } from "../../../modules/restaurant" +import { DeleteRestaurantAdminWorkflow } from "../workflows/delete-restaurant-admin" + +export const deleteRestaurantAdminStep = createStep( + "delete-restaurant-admin", + async ({ id }: DeleteRestaurantAdminWorkflow, { container }) => { + const restaurantModuleService = container.resolve( + RESTAURANT_MODULE + ) + + const admin = await restaurantModuleService.retrieveRestaurantAdmin(id) + + await restaurantModuleService.deleteRestaurantAdmins(id) + + return new StepResponse(undefined, { admin }) + }, + async ({ admin }, { container }) => { + const restaurantModuleService = container.resolve( + RESTAURANT_MODULE + ) + + await restaurantModuleService.createRestaurantAdmins(admin) + } +) +``` + +In this step, you resolve the Restaurant Module's service and delete the admin. In the compensation function, you create the admin again. + +### Create deleteRestaurantAdminWorkflow + +Then, create the workflow that deletes the restaurant admin at `restaurant-marketplace/src/workflows/restaurant/workflows/delete-restaurant-admin.ts`: + +```ts title="restaurant-marketplace/src/workflows/restaurant/workflows/delete-restaurant-admin.ts" collapsibleLines="1-13" expandButtonLabel="Show Imports" +import { MedusaError } from "@medusajs/utils" +import { + WorkflowData, + WorkflowResponse, + createWorkflow, + transform, +} from "@medusajs/workflows-sdk" +import { + setAuthAppMetadataStep, + useRemoteQueryStep, +} from "@medusajs/core-flows" +import { deleteRestaurantAdminStep } from "../steps/delete-restaurant-admin" + +export type DeleteRestaurantAdminWorkflow = { + id: string +} + +export const deleteRestaurantAdminWorkflow = createWorkflow( + "delete-restaurant-admin", + ( + input: WorkflowData + ): WorkflowResponse => { + deleteRestaurantAdminStep(input) + + // TODO update auth identity + } +) +``` + +So far, you only use the `deleteRestaurantAdminStep` in the workflow, which deletes the restaurant admin. + +Replace the `TODO` with the following: + +```ts title="restaurant-marketplace/src/workflows/restaurant/workflows/delete-restaurant-admin.ts" +const authIdentities = useRemoteQueryStep({ + entry_point: "auth_identity", + fields: ["id"], + variables: { + filters: { + app_metadata: { + restaurant_id: input.id, + }, + }, + }, +}) + +const authIdentity = transform( + { authIdentities }, + ({ authIdentities }) => { + const authIdentity = authIdentities[0] + + if (!authIdentity) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + "Auth identity not found" + ) + } + + return authIdentity + } +) + +setAuthAppMetadataStep({ + authIdentityId: authIdentity.id, + actorType: "restaurant", + value: null, +}) + +return new WorkflowResponse(input.id) +``` + +After deleting the restaurant admin, you: + +1. Retrieve its auth identity using Query. To do that, you filter its `app_metadata` property by checking that its `restaurant_id` property's value is the admin's ID. For drivers, you replace `restaurant_id` with `driver_id`. +2. Check that the auth identity exists using the `transform` utility. Otherwise, throw an error. +3. Unset the association between the auth identity and the restaurant admin using the `setAuthAppMetadataStep` imported from `@medusajs/core-flows`. + +### Create API Route + +Finally, add the API route that uses the workflow at `src/api/restaurants/[id]/admins/[admin_id]/route.ts`: + +```ts title="src/api/restaurants/[id]/admins/[admin_id]/route.ts" +import { + AuthenticatedMedusaRequest, + MedusaResponse, +} from "@medusajs/medusa" +import { + deleteRestaurantAdminWorkflow, +} from "../../../../../workflows/restaurant/workflows/delete-restaurant-admin" + +export const DELETE = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + await deleteRestaurantAdminWorkflow(req.scope).run({ + input: { + id: req.params.admin_id, + }, + }) + + res.json({ message: "success" }) +} +``` + +You add a `DELETE` API route at `/restaurants/[id]/admins/[admin_id]`. In the route, you execute the workflow to delete the restaurant admin. + +### Add Authentication Middleware + +This API route should only be accessible by restaurant admins. + +So, in the file `src/api/middlewares.ts`, add a new middleware: + +```ts title="src/api/middlewares.ts" +export default defineMiddlewares({ + routes: [ + // ... + { + method: ["POST", "DELETE"], + matcher: "/restaurants/:id/**", + middlewares: [ + authenticate(["restaurant", "user"], "bearer"), + ], + }, + ], +}) +``` + +This allows only restaurant admins and Medusa Admin users to access routes under the `/restaurants/[id]` prefix if the request method is `POST` or `DELETE`. + +### Test API Route + +To test it out, create another restaurant admin user, then send a `DELETE` request to `/restaurants/[id]/admins/[admin_id]`, authenticated as the first admin user you created: + +```bash +curl -X DELETE 'http://localhost:9000/restaurants/01J7GHGQTCAVY5C1AH1H733Q4G/admins/01J7GJKHWXF1YDMXH09EXEDCD6' \ +-H 'Authorization: Bearer {token}' +``` + +Make sure to replace the first ID with the restaurant's ID, and the second ID with the ID of the admin to delete. + +--- + +## Step 9: Create Restaurant Product API Route In this step, you’ll create the API route that creates a product for a restaurant. @@ -1144,29 +1334,6 @@ export async function POST(req: MedusaRequest, res: MedusaResponse) { The creates a `POST` API route at `/restaurants/[id]/products`. It accepts the products’ details in the request body, executes the `createRestaurantProductsWorkflow` to create the products, and returns the created products in the response. -### Add Authentication Middleware - -This API route should only be accessible by restaurant admins. - -So, in the file `src/api/middlewares.ts`, add a new middleware: - -```ts title="src/api/middlewares.ts" -export default defineMiddlewares({ - routes: [ - // ... - { - method: ["POST", "DELETE"], - matcher: "/restaurants/:id/**", - middlewares: [ - authenticate(["restaurant", "user"], "bearer"), - ], - }, - ], -}) -``` - -This allows only restaurant admins and Medusa Admin users to access routes under the `/restaurants/[id]` prefix if the request method is `POST` or `DELETE`. - ### Test it Out To create a product using the above API route, send a `POST` request to `/restaurants/[id]/products`, replacing `[id]` with the restaurant’s ID: @@ -1209,7 +1376,7 @@ The request returns the created product in the response. --- -## Step 9: Create Order Delivery Workflow +## Step 10: Create Order Delivery Workflow In this step, you’ll create the workflow that creates a delivery. You’ll use it at a later step once a customer places their order. @@ -1362,7 +1529,7 @@ In the workflow, you: --- -## Step 10: Handle Delivery Workflow +## Step 11: Handle Delivery Workflow In this step, you’ll create the workflow that handles the different stages of the delivery. This workflow needs to run in the background to update the delivery when an action occurs. @@ -1898,7 +2065,7 @@ In the next steps, you’ll execute the workflow and see it in action as you add --- -## Step 11: Create Order Delivery API Route +## Step 12: Create Order Delivery API Route In this step, you’ll create the API route that executes the workflows created by the previous two steps. This API route is used when a customer places their order. @@ -2074,7 +2241,7 @@ In the upcoming steps, you’ll add functionalities to update the delivery’s s --- -## Step 12: Accept Delivery API Route +## Step 13: Accept Delivery API Route In this step, you’ll create an API route that a restaurant admin uses to accept a delivery. This moves the `handleDeliveryWorkflow` execution from `notifyRestaurantStep` to the next step. @@ -2526,7 +2693,7 @@ Meaning that the `handleDeliveryWorkflow`'s execution has moved to the `awaitDri --- -## Step 13: Claim Delivery API Route +## Step 14: Claim Delivery API Route In this step, you’ll add the API route that allows a driver to claim a delivery. @@ -2671,7 +2838,7 @@ This indicates that the `handleDeliveryWorkflow`'s execution continued past the --- -## Step 14: Prepare API Route +## Step 15: Prepare API Route In this step, you’ll add the API route that restaurants use to indicate they’re preparing the order. @@ -2761,7 +2928,7 @@ This message indicates that the `handleDeliveryWorkflow`'s execution has moved t --- -## Step 15: Ready API Route +## Step 16: Ready API Route In this step, you’ll create the API route that restaurants use to indicate that a delivery is ready for pick up. @@ -2853,7 +3020,7 @@ This message indicates that the `handleDeliveryWorkflow`'s execution has moved t --- -## Step 18: Pick Up Delivery API Route +## Step 17: Pick Up Delivery API Route In this step, you’ll add the API route that the driver uses to indicate they’ve picked up the delivery. @@ -2993,7 +3160,7 @@ This message indicates that the `handleDeliveryWorkflow`'s execution has moved t --- -## Step 19: Complete Delivery API Route +## Step 18: Complete Delivery API Route In this step, you’ll create the API route that the driver uses to indicate that they completed the delivery. @@ -3083,7 +3250,7 @@ As the route sets the status of the `awaitDeliveryStep` to successful in the `ha --- -## Step 20: Real-Time Tracking in the Storefront +## Step 19: Real-Time Tracking in the Storefront In this step, you’ll learn how to implement real-time tracking of a delivery in a Next.js-based storefront. diff --git a/www/apps/resources/generated/edit-dates.mjs b/www/apps/resources/generated/edit-dates.mjs index a02960f65c..4d5d8d9f6c 100644 --- a/www/apps/resources/generated/edit-dates.mjs +++ b/www/apps/resources/generated/edit-dates.mjs @@ -225,12 +225,9 @@ export const generatedEditDates = { "app/commerce-modules/auth/_events/_events-table/page.mdx": "2024-07-03T19:27:13+03:00", "app/commerce-modules/auth/auth-flows/page.mdx": "2024-09-05T08:50:11.671Z", "app/commerce-modules/auth/_events/page.mdx": "2024-07-03T19:27:13+03:00", - "app/commerce-modules/auth/auth-identity-and-actor-types/page.mdx": "2024-07-31T17:01:33+03:00", - "app/commerce-modules/api-key/page.mdx": "2024-09-05T14:59:37.604Z", - "app/commerce-modules/auth/create-actor-type/page.mdx": "2024-07-31T17:01:33+03:00", "app/commerce-modules/auth/auth-identity-and-actor-types/page.mdx": "2024-09-05T08:11:28.936Z", "app/commerce-modules/api-key/page.mdx": "2024-08-05T07:24:27+00:00", - "app/commerce-modules/auth/create-actor-type/page.mdx": "2024-09-05T09:24:48.099Z", + "app/commerce-modules/auth/create-actor-type/page.mdx": "2024-09-11T13:15:34.040Z", "app/architectural-modules/page.mdx": "2024-05-28T13:25:03+03:00", "app/commerce-modules/api-key/relations-to-other-modules/page.mdx": "2024-05-29T11:08:06+00:00", "app/architectural-modules/workflow-engine/redis/page.mdx": "2024-07-18T13:04:29+02:00", @@ -646,7 +643,7 @@ export const generatedEditDates = { "app/medusa-cli/commands/start/page.mdx": "2024-08-28T10:44:19.952Z", "app/medusa-cli/commands/telemtry/page.mdx": "2024-08-28T11:25:08.553Z", "app/medusa-cli/commands/user/page.mdx": "2024-08-28T10:44:52.489Z", - "app/recipes/marketplace/examples/restaurant-delivery/page.mdx": "2024-08-29T09:20:26.842Z", + "app/recipes/marketplace/examples/restaurant-delivery/page.mdx": "2024-09-11T13:29:33.305Z", "references/types/HttpTypes/interfaces/types.HttpTypes.AdminCreateCustomerGroup/page.mdx": "2024-08-30T00:11:02.074Z", "references/types/HttpTypes/interfaces/types.HttpTypes.AdminCreateReservation/page.mdx": "2024-08-30T00:11:02.342Z", "references/types/HttpTypes/interfaces/types.HttpTypes.AdminCustomerGroup/page.mdx": "2024-08-30T00:11:02.070Z", @@ -911,11 +908,6 @@ export const generatedEditDates = { "references/order/interfaces/order.UpdateOrderReturnWithSelectorDTO/page.mdx": "2024-09-06T11:12:03.118Z", "references/promotion/IPromotionModuleService/methods/promotion.IPromotionModuleService.updatePromotions/page.mdx": "2024-09-04T00:11:32.621Z", "references/promotion/interfaces/promotion.UpdatePromotionDTO/page.mdx": "2024-09-04T00:11:32.593Z", - "references/types/HttpTypes/interfaces/types.HttpTypes.AdminClaim/page.mdx": "2024-09-06T11:11:36.322Z", - "references/types/HttpTypes/interfaces/types.HttpTypes.AdminClaimOrderResponse/page.mdx": "2024-09-06T11:11:36.370Z", - "references/types/HttpTypes/interfaces/types.HttpTypes.AdminClaimPreviewResponse/page.mdx": "2024-09-06T11:11:36.390Z", - "references/types/HttpTypes/interfaces/types.HttpTypes.AdminOrderEditPreviewResponse/page.mdx": "2024-09-06T11:11:36.646Z", - "references/types/interfaces/types.BaseClaim/page.mdx": "2024-09-06T11:11:36.238Z", "references/types/HttpTypes/interfaces/types.HttpTypes.AdminClaim/page.mdx": "2024-09-04T00:11:02.573Z", "references/types/HttpTypes/interfaces/types.HttpTypes.AdminClaimOrderResponse/page.mdx": "2024-09-04T00:11:02.625Z", "references/types/HttpTypes/interfaces/types.HttpTypes.AdminClaimPreviewResponse/page.mdx": "2024-09-04T00:11:02.637Z",