From 44829f296a83632c96df792787b0d7257edfd9f2 Mon Sep 17 00:00:00 2001 From: Stevche Radevski Date: Thu, 18 Apr 2024 08:51:43 +0200 Subject: [PATCH] chore: Move several more endpoints to use zod for validation, unify APIs (#7086) --- .../modules/__tests__/inventory/index.spec.ts | 7 +- packages/inventory-next/src/joiner-config.ts | 2 +- .../admin/fulfillment-sets/[id]/route.ts | 13 +- .../[id]/service-zones/route.ts | 21 +- .../api-v2/admin/fulfillment-sets/helpers.ts | 23 + .../admin/fulfillment-sets/middlewares.ts | 7 +- .../location-levels/[location_id]/route.ts | 39 +- .../[id]/location-levels/batch/combi/route.ts | 55 -- .../[id]/location-levels/op/batch/route.ts | 47 ++ .../[id]/location-levels/route.ts | 36 +- .../admin/inventory-items/[id]/route.ts | 62 +-- .../api-v2/admin/inventory-items/helpers.ts | 28 + .../admin/inventory-items/middlewares.ts | 142 ++--- .../admin/inventory-items/query-config.ts | 10 +- .../src/api-v2/admin/inventory-items/route.ts | 28 +- .../admin/inventory-items/validators.ts | 515 ++++-------------- .../api-v2/admin/invites/[id]/resend/route.ts | 10 +- .../src/api-v2/admin/invites/[id]/route.ts | 25 +- .../src/api-v2/admin/invites/accept/route.ts | 5 +- .../src/api-v2/admin/invites/helpers.ts | 23 + .../src/api-v2/admin/invites/middlewares.ts | 73 ++- .../src/api-v2/admin/invites/query-config.ts | 6 +- .../medusa/src/api-v2/admin/invites/route.ts | 30 +- .../src/api-v2/admin/invites/validators.ts | 181 ++---- .../admin/payments/[id]/capture/route.ts | 24 +- .../admin/payments/[id]/refund/route.ts | 25 +- .../src/api-v2/admin/payments/[id]/route.ts | 25 +- .../src/api-v2/admin/payments/helpers.ts | 23 + .../src/api-v2/admin/payments/middlewares.ts | 44 +- .../admin/payments/payment-providers/route.ts | 37 +- .../src/api-v2/admin/payments/query-config.ts | 16 +- .../medusa/src/api-v2/admin/payments/route.ts | 19 +- .../src/api-v2/admin/payments/validators.ts | 120 ++-- .../src/api-v2/admin/product-types/helpers.ts | 7 +- .../medusa/src/api-v2/utils/unless-path.ts | 15 + 35 files changed, 708 insertions(+), 1035 deletions(-) create mode 100644 packages/medusa/src/api-v2/admin/fulfillment-sets/helpers.ts delete mode 100644 packages/medusa/src/api-v2/admin/inventory-items/[id]/location-levels/batch/combi/route.ts create mode 100644 packages/medusa/src/api-v2/admin/inventory-items/[id]/location-levels/op/batch/route.ts create mode 100644 packages/medusa/src/api-v2/admin/inventory-items/helpers.ts create mode 100644 packages/medusa/src/api-v2/admin/invites/helpers.ts create mode 100644 packages/medusa/src/api-v2/admin/payments/helpers.ts create mode 100644 packages/medusa/src/api-v2/utils/unless-path.ts diff --git a/integration-tests/modules/__tests__/inventory/index.spec.ts b/integration-tests/modules/__tests__/inventory/index.spec.ts index d94e9ae7d0..9f10108765 100644 --- a/integration-tests/modules/__tests__/inventory/index.spec.ts +++ b/integration-tests/modules/__tests__/inventory/index.spec.ts @@ -334,14 +334,14 @@ medusaIntegrationTestRunner({ it("should delete an inventory location level and create a new one", async () => { const result = await api.post( - `/admin/inventory-items/${inventoryItem.id}/location-levels/batch/combi`, + `/admin/inventory-items/${inventoryItem.id}/location-levels/op/batch`, { - creates: [ + create: [ { location_id: "location_2", }, ], - deletes: [locationId], + delete: [locationId], }, adminHeaders ) @@ -386,6 +386,7 @@ medusaIntegrationTestRunner({ id: expect.any(String), object: "inventory-level", deleted: true, + parent: expect.any(Object), }) }) diff --git a/packages/inventory-next/src/joiner-config.ts b/packages/inventory-next/src/joiner-config.ts index 14a0fa53ec..7e007f6cba 100644 --- a/packages/inventory-next/src/joiner-config.ts +++ b/packages/inventory-next/src/joiner-config.ts @@ -32,7 +32,7 @@ export const joinerConfig: ModuleJoinerConfig = { schema: moduleSchema, alias: [ { - name: ["inventory_items", "inventory"], + name: ["inventory_items", "inventory_item", "inventory"], args: { entity: "InventoryItem", }, diff --git a/packages/medusa/src/api-v2/admin/fulfillment-sets/[id]/route.ts b/packages/medusa/src/api-v2/admin/fulfillment-sets/[id]/route.ts index 788a63e809..c722b0f4b0 100644 --- a/packages/medusa/src/api-v2/admin/fulfillment-sets/[id]/route.ts +++ b/packages/medusa/src/api-v2/admin/fulfillment-sets/[id]/route.ts @@ -1,8 +1,4 @@ -import { - AdminFulfillmentSetsDeleteResponse, - IFulfillmentModuleService, -} from "@medusajs/types" -import { ModuleRegistrationName } from "@medusajs/modules-sdk" +import { AdminFulfillmentSetsDeleteResponse } from "@medusajs/types" import { deleteFulfillmentSetsWorkflow } from "@medusajs/core-flows" import { @@ -16,13 +12,6 @@ export const DELETE = async ( ) => { const { id } = req.params - const fulfillmentModuleService = req.scope.resolve( - ModuleRegistrationName.FULFILLMENT - ) - - // Test if exists - await fulfillmentModuleService.retrieve(id) - const { errors } = await deleteFulfillmentSetsWorkflow(req.scope).run({ input: { ids: [id] }, throwOnError: false, diff --git a/packages/medusa/src/api-v2/admin/fulfillment-sets/[id]/service-zones/route.ts b/packages/medusa/src/api-v2/admin/fulfillment-sets/[id]/service-zones/route.ts index a7f21087c8..059e499f23 100644 --- a/packages/medusa/src/api-v2/admin/fulfillment-sets/[id]/service-zones/route.ts +++ b/packages/medusa/src/api-v2/admin/fulfillment-sets/[id]/service-zones/route.ts @@ -1,17 +1,12 @@ import { createServiceZonesWorkflow } from "@medusajs/core-flows" -import { - ContainerRegistrationKeys, - remoteQueryObjectFromString, -} from "@medusajs/utils" import { MedusaRequest, MedusaResponse } from "../../../../../types/routing" import { AdminCreateFulfillmentSetServiceZonesType } from "../../validators" +import { refetchFulfillmentSet } from "../../helpers" export const POST = async ( req: MedusaRequest, res: MedusaResponse ) => { - const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) - const workflowInput = { data: [ { @@ -31,15 +26,11 @@ export const POST = async ( throw errors[0].error } - const [fulfillment_set] = await remoteQuery( - remoteQueryObjectFromString({ - entryPoint: "fulfillment_sets", - variables: { - id: req.params.id, - }, - fields: req.remoteQueryConfig.fields, - }) + const fulfillmentSet = await refetchFulfillmentSet( + req.params.id, + req.scope, + req.remoteQueryConfig.fields ) - res.status(200).json({ fulfillment_set }) + res.status(200).json({ fulfillment_set: fulfillmentSet }) } diff --git a/packages/medusa/src/api-v2/admin/fulfillment-sets/helpers.ts b/packages/medusa/src/api-v2/admin/fulfillment-sets/helpers.ts new file mode 100644 index 0000000000..17494a7a66 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/fulfillment-sets/helpers.ts @@ -0,0 +1,23 @@ +import { MedusaContainer } from "@medusajs/types" +import { + ContainerRegistrationKeys, + remoteQueryObjectFromString, +} from "@medusajs/utils" + +export const refetchFulfillmentSet = async ( + fulfillmentSetId: string, + scope: MedusaContainer, + fields: string[] +) => { + const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + const queryObject = remoteQueryObjectFromString({ + entryPoint: "fulfillment_set", + variables: { + filters: { id: fulfillmentSetId }, + }, + fields: fields, + }) + + const fulfillmentSets = await remoteQuery(queryObject) + return fulfillmentSets[0] +} diff --git a/packages/medusa/src/api-v2/admin/fulfillment-sets/middlewares.ts b/packages/medusa/src/api-v2/admin/fulfillment-sets/middlewares.ts index 8a553ed26a..9a378c6aa0 100644 --- a/packages/medusa/src/api-v2/admin/fulfillment-sets/middlewares.ts +++ b/packages/medusa/src/api-v2/admin/fulfillment-sets/middlewares.ts @@ -30,7 +30,12 @@ export const adminFulfillmentSetsRoutesMiddlewares: MiddlewareRoute[] = [ { method: ["DELETE"], matcher: "/admin/fulfillment-sets/:id/service-zones/:zone_id", - middlewares: [], + middlewares: [ + validateAndTransformQuery( + AdminFulfillmentSetParams, + QueryConfig.retrieveTransformQueryConfig + ), + ], }, { method: ["DELETE"], diff --git a/packages/medusa/src/api-v2/admin/inventory-items/[id]/location-levels/[location_id]/route.ts b/packages/medusa/src/api-v2/admin/inventory-items/[id]/location-levels/[location_id]/route.ts index 91230f65f7..e49b13cb52 100644 --- a/packages/medusa/src/api-v2/admin/inventory-items/[id]/location-levels/[location_id]/route.ts +++ b/packages/medusa/src/api-v2/admin/inventory-items/[id]/location-levels/[location_id]/route.ts @@ -5,15 +5,19 @@ import { } from "@medusajs/utils" import { MedusaRequest, MedusaResponse } from "../../../../../../types/routing" -import { AdminPostInventoryItemsItemLocationLevelsLevelReq } from "../../../validators" -import { deleteInventoryLevelsWorkflow } from "@medusajs/core-flows" -import { updateInventoryLevelsWorkflow } from "@medusajs/core-flows" +import { + deleteInventoryLevelsWorkflow, + updateInventoryLevelsWorkflow, +} from "@medusajs/core-flows" +import { refetchInventoryItem } from "../../../helpers" +import { AdminUpdateInventoryLocationLevelType } from "../../../validators" export const DELETE = async (req: MedusaRequest, res: MedusaResponse) => { const { id, location_id } = req.params const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + // TODO: We probably want to move this logic to the workflow const [{ id: levelId, reserved_quantity: reservedQuantity }] = await remoteQuery( remoteQueryObjectFromString({ @@ -41,23 +45,28 @@ export const DELETE = async (req: MedusaRequest, res: MedusaResponse) => { }, }) + const inventoryItem = await refetchInventoryItem( + id, + req.scope, + req.remoteQueryConfig.fields + ) + res.status(200).json({ id: levelId, object: "inventory-level", deleted: true, + parent: inventoryItem, }) } export const POST = async ( - req: MedusaRequest, + req: MedusaRequest, res: MedusaResponse ) => { - const { id: inventory_item_id, location_id } = req.params - const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) - + const { id, location_id } = req.params const { errors } = await updateInventoryLevelsWorkflow(req.scope).run({ input: { - updates: [{ inventory_item_id, location_id, ...req.validatedBody }], + updates: [{ ...req.validatedBody, inventory_item_id: id, location_id }], }, throwOnError: false, }) @@ -66,17 +75,13 @@ export const POST = async ( throw errors[0].error } - const [inventory_item] = await remoteQuery( - remoteQueryObjectFromString({ - entryPoint: "inventory", - variables: { - id: inventory_item_id, - }, - fields: req.remoteQueryConfig.fields, - }) + const inventoryItem = await refetchInventoryItem( + id, + req.scope, + req.remoteQueryConfig.fields ) res.status(200).json({ - inventory_item, + inventory_item: inventoryItem, }) } diff --git a/packages/medusa/src/api-v2/admin/inventory-items/[id]/location-levels/batch/combi/route.ts b/packages/medusa/src/api-v2/admin/inventory-items/[id]/location-levels/batch/combi/route.ts deleted file mode 100644 index cf87430fa4..0000000000 --- a/packages/medusa/src/api-v2/admin/inventory-items/[id]/location-levels/batch/combi/route.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { - AdminPostInventoryItemsItemLocationLevelsBatchReq, - AdminPostInventoryItemsItemLocationLevelsReq, -} from "../../../../validators" -import { - ContainerRegistrationKeys, - remoteQueryObjectFromString, -} from "@medusajs/utils" -import { - MedusaRequest, - MedusaResponse, -} from "../../../../../../../types/routing" - -import { bulkCreateDeleteLevelsWorkflow } from "@medusajs/core-flows" -import { defaultAdminInventoryItemFields } from "../../../../query-config" - -export const POST = async ( - req: MedusaRequest, - res: MedusaResponse -) => { - const { id } = req.params - - const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) - - const workflow = bulkCreateDeleteLevelsWorkflow(req.scope) - const { errors } = await workflow.run({ - input: { - deletes: req.validatedBody.deletes.map((location_id) => ({ - location_id, - inventory_item_id: id, - })), - creates: req.validatedBody.creates.map((c) => ({ - ...c, - inventory_item_id: id, - })), - }, - throwOnError: false, - }) - - if (Array.isArray(errors) && errors[0]) { - throw errors[0].error - } - - const itemQuery = remoteQueryObjectFromString({ - entryPoint: "inventory_items", - variables: { - id, - }, - fields: defaultAdminInventoryItemFields, - }) - - const [inventory_item] = await remoteQuery(itemQuery) - - res.status(200).json({ inventory_item }) -} diff --git a/packages/medusa/src/api-v2/admin/inventory-items/[id]/location-levels/op/batch/route.ts b/packages/medusa/src/api-v2/admin/inventory-items/[id]/location-levels/op/batch/route.ts new file mode 100644 index 0000000000..810f6132c7 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/inventory-items/[id]/location-levels/op/batch/route.ts @@ -0,0 +1,47 @@ +import { + AdminCreateInventoryLocationLevelType, + AdminUpdateInventoryLocationLevelType, +} from "../../../../validators" +import { + MedusaRequest, + MedusaResponse, +} from "../../../../../../../types/routing" + +import { bulkCreateDeleteLevelsWorkflow } from "@medusajs/core-flows" +import { BatchMethodRequest } from "@medusajs/types" + +export const POST = async ( + req: MedusaRequest< + BatchMethodRequest< + AdminCreateInventoryLocationLevelType, + AdminUpdateInventoryLocationLevelType + > + >, + res: MedusaResponse +) => { + const { id } = req.params + + // TODO: Normalize workflow and response, and add support for updates + const workflow = bulkCreateDeleteLevelsWorkflow(req.scope) + const { errors } = await workflow.run({ + input: { + deletes: + req.validatedBody.delete?.map((location_id) => ({ + location_id, + inventory_item_id: id, + })) ?? [], + creates: + req.validatedBody.create?.map((c) => ({ + ...c, + inventory_item_id: id, + })) ?? [], + }, + throwOnError: false, + }) + + if (Array.isArray(errors) && errors[0]) { + throw errors[0].error + } + + res.status(200).json({ inventory_item: {} }) +} diff --git a/packages/medusa/src/api-v2/admin/inventory-items/[id]/location-levels/route.ts b/packages/medusa/src/api-v2/admin/inventory-items/[id]/location-levels/route.ts index a078f6d8f0..da863f663a 100644 --- a/packages/medusa/src/api-v2/admin/inventory-items/[id]/location-levels/route.ts +++ b/packages/medusa/src/api-v2/admin/inventory-items/[id]/location-levels/route.ts @@ -4,26 +4,26 @@ import { } from "@medusajs/utils" import { MedusaRequest, MedusaResponse } from "../../../../../types/routing" -import { AdminPostInventoryItemsItemLocationLevelsReq } from "../../validators" -import { MedusaError } from "@medusajs/utils" import { createInventoryLevelsWorkflow } from "@medusajs/core-flows" -import { defaultAdminInventoryItemFields } from "../../query-config" +import { + AdminCreateInventoryLocationLevelType, + AdminGetInventoryLocationLevelsParamsType, +} from "../../validators" +import { refetchInventoryItem } from "../../helpers" export const POST = async ( - req: MedusaRequest, + req: MedusaRequest, res: MedusaResponse ) => { const { id } = req.params - const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) - const workflow = createInventoryLevelsWorkflow(req.scope) const { errors } = await workflow.run({ input: { inventory_levels: [ { - inventory_item_id: id, ...req.validatedBody, + inventory_item_id: id, }, ], }, @@ -34,20 +34,18 @@ export const POST = async ( throw errors[0].error } - const itemQuery = remoteQueryObjectFromString({ - entryPoint: "inventory_items", - variables: { - id, - }, - fields: defaultAdminInventoryItemFields, - }) - - const [inventory_item] = await remoteQuery(itemQuery) - - res.status(200).json({ inventory_item }) + const inventoryItem = await refetchInventoryItem( + id, + req.scope, + req.remoteQueryConfig.fields + ) + res.status(200).json({ inventory_item: inventoryItem }) } -export const GET = async (req: MedusaRequest, res: MedusaResponse) => { +export const GET = async ( + req: MedusaRequest, + res: MedusaResponse +) => { const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) const query = remoteQueryObjectFromString({ diff --git a/packages/medusa/src/api-v2/admin/inventory-items/[id]/route.ts b/packages/medusa/src/api-v2/admin/inventory-items/[id]/route.ts index 0f1785d73e..e18ad6aad1 100644 --- a/packages/medusa/src/api-v2/admin/inventory-items/[id]/route.ts +++ b/packages/medusa/src/api-v2/admin/inventory-items/[id]/route.ts @@ -1,36 +1,26 @@ -import { - ContainerRegistrationKeys, - MedusaError, - remoteQueryObjectFromString, -} from "@medusajs/utils" +import { MedusaError } from "@medusajs/utils" import { MedusaRequest, MedusaResponse } from "../../../../types/routing" import { deleteInventoryItemWorkflow, updateInventoryItemsWorkflow, } from "@medusajs/core-flows" +import { + AdminGetInventoryItemParamsType, + AdminUpdateInventoryItemType, +} from "../validators" +import { refetchInventoryItem } from "../helpers" -import { AdminPostInventoryItemsInventoryItemReq } from "../validators" - -export const GET = async (req: MedusaRequest, res: MedusaResponse) => { +export const GET = async ( + req: MedusaRequest, + res: MedusaResponse +) => { const { id } = req.params - const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) - - const query = remoteQueryObjectFromString({ - entryPoint: "inventory", - variables: { - filters: { id }, - skip: 0, - take: 1, - }, - - fields: req.retrieveConfig.select as string[], - }) - - const { rows } = await remoteQuery(query) - - const [inventory_item] = rows - - if (!inventory_item) { + const inventoryItem = await refetchInventoryItem( + id, + req.scope, + req.remoteQueryConfig.fields + ) + if (!inventoryItem) { throw new MedusaError( MedusaError.Types.NOT_FOUND, `Inventory item with id: ${id} was not found` @@ -38,13 +28,13 @@ export const GET = async (req: MedusaRequest, res: MedusaResponse) => { } res.status(200).json({ - inventory_item, + inventory_item: inventoryItem, }) } // Update inventory item export const POST = async ( - req: MedusaRequest, + req: MedusaRequest, res: MedusaResponse ) => { const { id } = req.params @@ -55,20 +45,14 @@ export const POST = async ( }, }) - const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) - - const [inventory_item] = await remoteQuery( - remoteQueryObjectFromString({ - entryPoint: "inventory", - variables: { - id, - }, - fields: req.retrieveConfig.select as string[], - }) + const inventoryItem = await refetchInventoryItem( + id, + req.scope, + req.remoteQueryConfig.fields ) res.status(200).json({ - inventory_item, + inventory_item: inventoryItem, }) } diff --git a/packages/medusa/src/api-v2/admin/inventory-items/helpers.ts b/packages/medusa/src/api-v2/admin/inventory-items/helpers.ts new file mode 100644 index 0000000000..bada406607 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/inventory-items/helpers.ts @@ -0,0 +1,28 @@ +import { MedusaContainer } from "@medusajs/types" +import { + ContainerRegistrationKeys, + remoteQueryObjectFromString, +} from "@medusajs/utils" + +export const refetchInventoryItem = async ( + inventoryItemId: string, + scope: MedusaContainer, + fields: string[] +) => { + const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + const queryObject = remoteQueryObjectFromString({ + entryPoint: "inventory_item", + variables: { + filters: { id: inventoryItemId }, + skip: 0, + take: 1, + }, + fields: fields, + }) + + // TODO: Why does the response type change if you pass skip and take, vs not passing it? + // Also, why does the data change (in this case, not doing skip and take will not return the lazy fields of stockedQuantity and reserved_quantity) + const { rows } = await remoteQuery(queryObject) + + return rows[0] +} diff --git a/packages/medusa/src/api-v2/admin/inventory-items/middlewares.ts b/packages/medusa/src/api-v2/admin/inventory-items/middlewares.ts index dcdebe1961..2c62a6662f 100644 --- a/packages/medusa/src/api-v2/admin/inventory-items/middlewares.ts +++ b/packages/medusa/src/api-v2/admin/inventory-items/middlewares.ts @@ -1,21 +1,19 @@ import * as QueryConfig from "./query-config" - -import { - AdminGetInventoryItemsItemLocationLevelsParams, - AdminGetInventoryItemsItemParams, - AdminGetInventoryItemsParams, - AdminPostInventoryItemsInventoryItemParams, - AdminPostInventoryItemsInventoryItemReq, - AdminPostInventoryItemsItemLocationLevelsBatchReq, - AdminPostInventoryItemsItemLocationLevelsLevelParams, - AdminPostInventoryItemsItemLocationLevelsLevelReq, - AdminPostInventoryItemsItemLocationLevelsReq, - AdminPostInventoryItemsReq, -} from "./validators" -import { transformBody, transformQuery } from "../../../api/middlewares" - import { MiddlewareRoute } from "../../../types/middlewares" import { authenticate } from "../../../utils/authenticate-middleware" +import { validateAndTransformQuery } from "../../utils/validate-query" +import { + AdminCreateInventoryItem, + AdminCreateInventoryLocationLevel, + AdminGetInventoryItemParams, + AdminGetInventoryItemsParams, + AdminGetInventoryLocationLevelParams, + AdminGetInventoryLocationLevelsParams, + AdminUpdateInventoryItem, + AdminUpdateInventoryLocationLevel, +} from "./validators" +import { validateAndTransformBody } from "../../utils/validate-body" +import { createBatchBody } from "../../utils/validators" export const adminInventoryRoutesMiddlewares: MiddlewareRoute[] = [ { @@ -27,7 +25,7 @@ export const adminInventoryRoutesMiddlewares: MiddlewareRoute[] = [ method: ["GET"], matcher: "/admin/inventory-items", middlewares: [ - transformQuery( + validateAndTransformQuery( AdminGetInventoryItemsParams, QueryConfig.listTransformQueryConfig ), @@ -37,8 +35,8 @@ export const adminInventoryRoutesMiddlewares: MiddlewareRoute[] = [ method: ["GET"], matcher: "/admin/inventory-items/:id", middlewares: [ - transformQuery( - AdminGetInventoryItemsItemParams, + validateAndTransformQuery( + AdminGetInventoryItemParams, QueryConfig.retrieveTransformQueryConfig ), ], @@ -47,47 +45,9 @@ export const adminInventoryRoutesMiddlewares: MiddlewareRoute[] = [ method: ["POST"], matcher: "/admin/inventory-items", middlewares: [ - transformBody(AdminPostInventoryItemsReq), - transformQuery( - AdminGetInventoryItemsItemParams, - QueryConfig.retrieveTransformQueryConfig - ), - ], - }, - { - method: ["POST"], - matcher: "/admin/inventory-items/:id/location-levels/batch/combi", - middlewares: [ - transformBody(AdminPostInventoryItemsItemLocationLevelsBatchReq), - ], - }, - { - method: ["GET"], - matcher: "/admin/inventory-items/:id/location-levels", - middlewares: [ - transformQuery( - AdminGetInventoryItemsItemLocationLevelsParams, - QueryConfig.listLocationLevelsTransformQueryConfig - ), - ], - }, - { - method: ["POST"], - matcher: "/admin/inventory-items/:id/location-levels", - middlewares: [transformBody(AdminPostInventoryItemsItemLocationLevelsReq)], - }, - { - method: ["POST"], - matcher: "/admin/inventory-items/:id/location-levels", - middlewares: [transformBody(AdminPostInventoryItemsItemLocationLevelsReq)], - }, - { - method: ["POST"], - matcher: "/admin/inventory-items/:id/location-levels/:location_id", - middlewares: [ - transformBody(AdminPostInventoryItemsItemLocationLevelsLevelReq), - transformQuery( - AdminPostInventoryItemsItemLocationLevelsLevelParams, + validateAndTransformBody(AdminCreateInventoryItem), + validateAndTransformQuery( + AdminGetInventoryItemParams, QueryConfig.retrieveTransformQueryConfig ), ], @@ -96,11 +56,69 @@ export const adminInventoryRoutesMiddlewares: MiddlewareRoute[] = [ method: ["POST"], matcher: "/admin/inventory-items/:id", middlewares: [ - transformBody(AdminPostInventoryItemsInventoryItemReq), - transformQuery( - AdminPostInventoryItemsInventoryItemParams, + validateAndTransformBody(AdminUpdateInventoryItem), + validateAndTransformQuery( + AdminGetInventoryItemParams, QueryConfig.retrieveTransformQueryConfig ), ], }, + { + method: ["GET"], + matcher: "/admin/inventory-items/:id/location-levels", + middlewares: [ + validateAndTransformQuery( + AdminGetInventoryLocationLevelsParams, + QueryConfig.listLocationLevelsTransformQueryConfig + ), + ], + }, + { + method: ["POST"], + matcher: "/admin/inventory-items/:id/location-levels", + middlewares: [ + validateAndTransformBody(AdminCreateInventoryLocationLevel), + validateAndTransformQuery( + AdminGetInventoryItemParams, + QueryConfig.retrieveTransformQueryConfig + ), + ], + }, + { + method: ["DELETE"], + matcher: "/admin/inventory-items/:id/location-levels/:location_id", + middlewares: [ + validateAndTransformQuery( + AdminGetInventoryItemParams, + QueryConfig.retrieveTransformQueryConfig + ), + ], + }, + { + method: ["POST"], + matcher: "/admin/inventory-items/:id/location-levels/:location_id", + middlewares: [ + validateAndTransformBody(AdminUpdateInventoryLocationLevel), + validateAndTransformQuery( + AdminGetInventoryItemParams, + QueryConfig.retrieveTransformQueryConfig + ), + ], + }, + { + method: ["POST"], + matcher: "/admin/inventory-items/:id/location-levels/op/batch", + middlewares: [ + validateAndTransformBody( + createBatchBody( + AdminCreateInventoryLocationLevel, + AdminUpdateInventoryLocationLevel + ) + ), + validateAndTransformQuery( + AdminGetInventoryLocationLevelParams, + QueryConfig.retrieveLocationLevelsTransformQueryConfig + ), + ], + }, ] diff --git a/packages/medusa/src/api-v2/admin/inventory-items/query-config.ts b/packages/medusa/src/api-v2/admin/inventory-items/query-config.ts index f01bc2143d..7363f46607 100644 --- a/packages/medusa/src/api-v2/admin/inventory-items/query-config.ts +++ b/packages/medusa/src/api-v2/admin/inventory-items/query-config.ts @@ -1,6 +1,3 @@ -import { InventoryNext } from "@medusajs/types" -import { defaultAdminProductsVariantFields } from "../products/query-config" - // eslint-disable-next-line max-len export const defaultAdminLocationLevelFields = [ "id", @@ -35,12 +32,7 @@ export const defaultAdminInventoryItemFields = [ "stocked_quantity", "created_at", "updated_at", - ...defaultAdminLocationLevelFields.map( - (field) => `location_levels.${field.toString()}` - ), - ...defaultAdminProductsVariantFields - .filter((field) => !field.startsWith("*")) - .map((field) => `variant.${field}`), + "*location_levels", ] export const retrieveTransformQueryConfig = { diff --git a/packages/medusa/src/api-v2/admin/inventory-items/route.ts b/packages/medusa/src/api-v2/admin/inventory-items/route.ts index ce46d014de..05a55eee33 100644 --- a/packages/medusa/src/api-v2/admin/inventory-items/route.ts +++ b/packages/medusa/src/api-v2/admin/inventory-items/route.ts @@ -7,36 +7,32 @@ import { remoteQueryObjectFromString, } from "@medusajs/utils" -import { AdminPostInventoryItemsReq } from "./validators" import { createInventoryItemsWorkflow } from "@medusajs/core-flows" +import { + AdminCreateInventoryItemType, + AdminGetInventoryItemsParamsType, +} from "./validators" +import { refetchInventoryItem } from "./helpers" -// Create inventory-item export const POST = async ( - req: AuthenticatedMedusaRequest, + req: AuthenticatedMedusaRequest, res: MedusaResponse ) => { - const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) - const { result } = await createInventoryItemsWorkflow(req.scope).run({ input: { items: [req.validatedBody] }, }) - const [inventory_item] = await remoteQuery( - remoteQueryObjectFromString({ - entryPoint: "inventory_items", - variables: { - id: result[0].id, - }, - fields: req.remoteQueryConfig.fields, - }) + const inventoryItem = await refetchInventoryItem( + result[0].id, + req.scope, + req.remoteQueryConfig.fields ) - res.status(200).json({ inventory_item }) + res.status(200).json({ inventory_item: inventoryItem }) } -// List inventory-items export const GET = async ( - req: AuthenticatedMedusaRequest, + req: AuthenticatedMedusaRequest, res: MedusaResponse ) => { const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) diff --git a/packages/medusa/src/api-v2/admin/inventory-items/validators.ts b/packages/medusa/src/api-v2/admin/inventory-items/validators.ts index 776556dc63..92a01029e3 100644 --- a/packages/medusa/src/api-v2/admin/inventory-items/validators.ts +++ b/packages/medusa/src/api-v2/admin/inventory-items/validators.ts @@ -1,408 +1,129 @@ +import { z } from "zod" import { - DateComparisonOperator, - FindParams, - NumericalComparisonOperator, - StringComparisonOperator, - extendedFindParamsMixin, -} from "../../../types/common" -import { - IsBoolean, - IsEmail, - IsNotEmpty, - IsNumber, - IsObject, - IsOptional, - IsString, - Min, - ValidateNested, -} from "class-validator" -import { Transform, Type } from "class-transformer" + createFindParams, + createOperatorMap, + createSelectParams, +} from "../../utils/validators" +import { optionalBooleanMapper } from "../../../utils/validators/is-boolean" -import { IsType } from "../../../utils" +export type AdminGetInventoryItemParamsType = z.infer< + typeof AdminGetInventoryItemParams +> +export const AdminGetInventoryItemParams = createSelectParams() -export class AdminGetInventoryItemsItemParams extends FindParams {} - -/** - * Parameters used to filter and configure the pagination of the retrieved inventory items. - */ -export class AdminGetInventoryItemsParams extends extendedFindParamsMixin({ +export type AdminGetInventoryItemsParamsType = z.infer< + typeof AdminGetInventoryItemsParams +> +export const AdminGetInventoryItemsParams = createFindParams({ limit: 20, offset: 0, -}) { - /** - * IDs to filter inventory items by. - */ - @IsOptional() - @IsType([String, [String]]) - id?: string | string[] +}).merge( + z.object({ + q: z.string().optional(), + id: z.union([z.string(), z.array(z.string())]).optional(), + location_id: z.union([z.string(), z.array(z.string())]).optional(), + sku: z.union([z.string(), z.array(z.string())]).optional(), + origin_country: z.union([z.string(), z.array(z.string())]).optional(), + mid_code: z.union([z.string(), z.array(z.string())]).optional(), + hs_code: z.union([z.string(), z.array(z.string())]).optional(), + material: z.union([z.string(), z.array(z.string())]).optional(), + requires_shipping: z + .preprocess( + (val: any) => optionalBooleanMapper.get(val?.toLowerCase()), + z.boolean().optional() + ) + .optional(), + weight: createOperatorMap(z.number(), parseFloat).optional(), + length: createOperatorMap(z.number(), parseFloat).optional(), + height: createOperatorMap(z.number(), parseFloat).optional(), + width: createOperatorMap(z.number(), parseFloat).optional(), + $and: z.lazy(() => AdminGetInventoryItemsParams.array()).optional(), + $or: z.lazy(() => AdminGetInventoryItemsParams.array()).optional(), + }) +) - /** - * Search terms to search inventory items' sku, title, and description. - */ - @IsOptional() - @IsString() - q?: string +export type AdminGetInventoryLocationLevelParamsType = z.infer< + typeof AdminGetInventoryLocationLevelParams +> +export const AdminGetInventoryLocationLevelParams = createSelectParams() - /** - * Location IDs to filter inventory items by. - */ - @IsOptional() - @IsType([String, [String]]) - location_id?: string | string[] +export type AdminGetInventoryLocationLevelsParamsType = z.infer< + typeof AdminGetInventoryLocationLevelsParams +> +export const AdminGetInventoryLocationLevelsParams = createFindParams({ + limit: 50, + offset: 0, +}).merge( + z.object({ + location_id: z.union([z.string(), z.array(z.string())]).optional(), + $and: z + .lazy(() => AdminGetInventoryLocationLevelsParams.array()) + .optional(), + $or: z.lazy(() => AdminGetInventoryLocationLevelsParams.array()).optional(), + }) +) - /** - * SKUs to filter inventory items by. - */ - @IsOptional() - @IsType([String, [String]]) - sku?: string | string[] +export type AdminCreateInventoryLocationLevelType = z.infer< + typeof AdminCreateInventoryLocationLevel +> +export const AdminCreateInventoryLocationLevel = z + .object({ + location_id: z.string(), + stocked_quantity: z.number().min(0).optional(), + incoming_quantity: z.number().min(0).optional(), + }) + .strict() - /** - * Origin countries to filter inventory items by. - */ - @IsOptional() - @IsType([String, [String]]) - origin_country?: string | string[] +export type AdminUpdateInventoryLocationLevelType = z.infer< + typeof AdminUpdateInventoryLocationLevel +> +export const AdminUpdateInventoryLocationLevel = z + .object({ + stocked_quantity: z.number().min(0).optional(), + incoming_quantity: z.number().min(0).optional(), + }) + .strict() - /** - * MID codes to filter inventory items by. - */ - @IsOptional() - @IsType([String, [String]]) - mid_code?: string | string[] +export type AdminCreateInventoryItemType = z.infer< + typeof AdminCreateInventoryItem +> +export const AdminCreateInventoryItem = z + .object({ + sku: z.string().optional(), + hs_code: z.string().optional(), + weight: z.number().optional(), + length: z.number().optional(), + height: z.number().optional(), + width: z.number().optional(), + origin_country: z.string().optional(), + mid_code: z.string().optional(), + material: z.string().optional(), + title: z.string().optional(), + description: z.string().optional(), + requires_shipping: z.boolean().optional(), + thumbnail: z.string().optional(), + metadata: z.record(z.string(), z.unknown()).optional(), + }) + .strict() - /** - * Materials to filter inventory items by. - */ - @IsOptional() - @IsType([String, [String]]) - material?: string | string[] - - /** - * String filters to apply to inventory items' `hs_code` field. - */ - @IsOptional() - @IsType([String, [String], StringComparisonOperator]) - hs_code?: string | string[] | StringComparisonOperator - - /** - * Number filters to apply to inventory items' `weight` field. - */ - @IsOptional() - @IsType([Number, NumericalComparisonOperator]) - weight?: number | NumericalComparisonOperator - - /** - * Number filters to apply to inventory items' `length` field. - */ - @IsOptional() - @IsType([Number, NumericalComparisonOperator]) - length?: number | NumericalComparisonOperator - - /** - * Number filters to apply to inventory items' `height` field. - */ - @IsOptional() - @IsType([Number, NumericalComparisonOperator]) - height?: number | NumericalComparisonOperator - - /** - * Number filters to apply to inventory items' `width` field. - */ - @IsOptional() - @IsType([Number, NumericalComparisonOperator]) - width?: number | NumericalComparisonOperator - - /** - * Filter inventory items by whether they require shipping. - */ - @IsBoolean() - @IsOptional() - @Transform(({ value }) => value === "true") - requires_shipping?: boolean -} - -export class AdminPostInventoryItemsItemLocationLevelsReq { - @IsString() - location_id: string - - @IsNumber() - @IsOptional() - stocked_quantity?: number - - @IsOptional() - @IsNumber() - incoming_quantity?: number -} - -export class AdminPostInventoryItemsItemLocationLevelsBatchReq { - @ValidateNested({ each: true }) - @Type(() => AdminPostInventoryItemsItemLocationLevelsReq) - creates: AdminPostInventoryItemsItemLocationLevelsReq[] - - @IsString({ each: true }) - deletes: string[] -} - -// eslint-disable-next-line -export class AdminPostInventoryItemsItemLocationLevelsParams extends FindParams {} - -/** - * @schema AdminPostInventoryItemsReq - * type: object - * description: "The details of the inventory item to create." - * properties: - * sku: - * description: The unique SKU of the associated Product Variant. - * type: string - * ean: - * description: The EAN number of the item. - * type: string - * upc: - * description: The UPC number of the item. - * type: string - * barcode: - * description: A generic GTIN field for the Product Variant. - * type: string - * hs_code: - * description: The Harmonized System code of the Inventory Item. May be used by Fulfillment Providers to pass customs information to shipping carriers. - * type: string - * inventory_quantity: - * description: The amount of stock kept of the associated Product Variant. - * type: integer - * default: 0 - * allow_backorder: - * description: Whether the associated Product Variant can be purchased when out of stock. - * type: boolean - * manage_inventory: - * description: Whether Medusa should keep track of the inventory for the associated Product Variant. - * type: boolean - * default: true - * weight: - * description: The weight of the Inventory Item. May be used in shipping rate calculations. - * type: number - * length: - * description: The length of the Inventory Item. May be used in shipping rate calculations. - * type: number - * height: - * description: The height of the Inventory Item. May be used in shipping rate calculations. - * type: number - * width: - * description: The width of the Inventory Item. May be used in shipping rate calculations. - * type: number - * origin_country: - * description: The country in which the Inventory Item was produced. May be used by Fulfillment Providers to pass customs information to shipping carriers. - * type: string - * mid_code: - * description: The Manufacturers Identification code that identifies the manufacturer of the Inventory Item. May be used by Fulfillment Providers to pass customs information to shipping carriers. - * type: string - * material: - * description: The material and composition that the Inventory Item is made of, May be used by Fulfillment Providers to pass customs information to shipping carriers. - * type: string - * title: - * description: The inventory item's title. - * type: string - * description: - * description: The inventory item's description. - * type: string - * thumbnail: - * description: The inventory item's thumbnail. - * type: string - * metadata: - * description: An optional set of key-value pairs with additional information. - * type: object - * externalDocs: - * description: "Learn about the metadata attribute, and how to delete and update it." - * url: "https://docs.medusajs.com/development/entities/overview#metadata-attribute" - */ -export class AdminPostInventoryItemsReq { - @IsString() - @IsOptional() - sku?: string - - @IsString() - @IsOptional() - hs_code?: string - - @IsNumber() - @IsOptional() - weight?: number - - @IsNumber() - @IsOptional() - length?: number - - @IsNumber() - @IsOptional() - height?: number - - @IsNumber() - @IsOptional() - width?: number - - @IsString() - @IsOptional() - origin_country?: string - - @IsString() - @IsOptional() - mid_code?: string - - @IsString() - @IsOptional() - material?: string - - @IsString() - @IsOptional() - title?: string - - @IsString() - @IsOptional() - description?: string - - @IsString() - @IsOptional() - thumbnail?: string - - @IsObject() - @IsOptional() - metadata?: Record -} - -// eslint-disable-next-line max-len -export class AdminGetInventoryItemsItemLocationLevelsParams extends extendedFindParamsMixin( - { - limit: 50, - offset: 0, - } -) { - /** - * Location IDs to filter location levels. - */ - @IsOptional() - @IsString({ each: true }) - location_id?: string[] -} -/** - * @schema AdminPostInventoryItemsItemLocationLevelsLevelReq - * type: object - * properties: - * stocked_quantity: - * description: the total stock quantity of an inventory item at the given location ID - * type: number - * incoming_quantity: - * description: the incoming stock quantity of an inventory item at the given location ID - * type: number - */ -export class AdminPostInventoryItemsItemLocationLevelsLevelReq { - @IsOptional() - @IsNumber() - @Min(0) - incoming_quantity?: number - - @IsOptional() - @IsNumber() - @Min(0) - stocked_quantity?: number -} - -// eslint-disable-next-line -export class AdminPostInventoryItemsItemLocationLevelsLevelParams extends FindParams {} -/** - * @schema AdminPostInventoryItemsInventoryItemReq - * type: object - * description: "The attributes to update in an inventory item." - * properties: - * hs_code: - * description: The Harmonized System code of the Inventory Item. May be used by Fulfillment Providers to pass customs information to shipping carriers. - * type: string - * origin_country: - * description: The country in which the Inventory Item was produced. May be used by Fulfillment Providers to pass customs information to shipping carriers. - * type: string - * mid_code: - * description: The Manufacturers Identification code that identifies the manufacturer of the Inventory Item. May be used by Fulfillment Providers to pass customs information to shipping carriers. - * type: string - * material: - * description: The material and composition that the Inventory Item is made of, May be used by Fulfillment Providers to pass customs information to shipping carriers. - * type: string - * weight: - * description: The weight of the Inventory Item. May be used in shipping rate calculations. - * type: number - * height: - * description: The height of the Inventory Item. May be used in shipping rate calculations. - * type: number - * width: - * description: The width of the Inventory Item. May be used in shipping rate calculations. - * type: number - * length: - * description: The length of the Inventory Item. May be used in shipping rate calculations. - * type: number - * title: - * description: The inventory item's title. - * type: string - * description: - * description: The inventory item's description. - * type: string - * thumbnail: - * description: The inventory item's thumbnail. - * type: string - * requires_shipping: - * description: Whether the item requires shipping. - * type: boolean - */ - -export class AdminPostInventoryItemsInventoryItemReq { - @IsString() - @IsOptional() - sku?: string - - @IsOptional() - @IsString() - origin_country?: string - - @IsOptional() - @IsString() - hs_code?: string - - @IsOptional() - @IsString() - mid_code?: string - - @IsOptional() - @IsString() - material?: string - - @IsOptional() - @IsNumber() - weight?: number - - @IsOptional() - @IsNumber() - height?: number - - @IsOptional() - @IsNumber() - length?: number - - @IsOptional() - @IsNumber() - width?: number - - @IsString() - @IsOptional() - title?: string - - @IsString() - @IsOptional() - description?: string - - @IsString() - @IsOptional() - thumbnail?: string - - @IsBoolean() - @IsOptional() - requires_shipping?: boolean -} - -export class AdminPostInventoryItemsInventoryItemParams extends FindParams {} +export type AdminUpdateInventoryItemType = z.infer< + typeof AdminUpdateInventoryItem +> +export const AdminUpdateInventoryItem = z + .object({ + sku: z.string().optional(), + hs_code: z.string().optional(), + weight: z.number().optional(), + length: z.number().optional(), + height: z.number().optional(), + width: z.number().optional(), + origin_country: z.string().optional(), + mid_code: z.string().optional(), + material: z.string().optional(), + title: z.string().optional(), + description: z.string().optional(), + requires_shipping: z.boolean().optional(), + thumbnail: z.string().optional(), + metadata: z.record(z.string(), z.unknown()).optional(), + }) + .strict() diff --git a/packages/medusa/src/api-v2/admin/invites/[id]/resend/route.ts b/packages/medusa/src/api-v2/admin/invites/[id]/resend/route.ts index 962644173e..c8af510be6 100644 --- a/packages/medusa/src/api-v2/admin/invites/[id]/resend/route.ts +++ b/packages/medusa/src/api-v2/admin/invites/[id]/resend/route.ts @@ -1,6 +1,7 @@ import { MedusaRequest, MedusaResponse } from "../../../../../types/routing" import { refreshInviteTokensWorkflow } from "@medusajs/core-flows" +import { refetchInvite } from "../../helpers" export const POST = async (req: MedusaRequest, res: MedusaResponse) => { const workflow = refreshInviteTokensWorkflow(req.scope) @@ -9,7 +10,12 @@ export const POST = async (req: MedusaRequest, res: MedusaResponse) => { invite_ids: [req.params.id], } - const { result: invites } = await workflow.run({ input }) + const { result } = await workflow.run({ input }) + const invite = await refetchInvite( + result[0].id, + req.scope, + req.remoteQueryConfig.fields + ) - res.status(200).json({ invite: invites[0] }) + res.status(200).json({ invite }) } diff --git a/packages/medusa/src/api-v2/admin/invites/[id]/route.ts b/packages/medusa/src/api-v2/admin/invites/[id]/route.ts index 7f0ec5650e..e21975ca97 100644 --- a/packages/medusa/src/api-v2/admin/invites/[id]/route.ts +++ b/packages/medusa/src/api-v2/admin/invites/[id]/route.ts @@ -2,31 +2,21 @@ import { AuthenticatedMedusaRequest, MedusaResponse, } from "../../../../types/routing" -import { - ContainerRegistrationKeys, - MedusaError, - remoteQueryObjectFromString, -} from "@medusajs/utils" +import { MedusaError } from "@medusajs/utils" import { deleteInvitesWorkflow } from "@medusajs/core-flows" +import { refetchInvite } from "../helpers" -// Get invite export const GET = async ( req: AuthenticatedMedusaRequest, res: MedusaResponse ) => { const { id } = req.params - const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) - - const query = remoteQueryObjectFromString({ - entryPoint: "invite", - variables: { - id, - }, - fields: req.retrieveConfig.select as string[], - }) - - const [invite] = await remoteQuery(query) + const invite = await refetchInvite( + id, + req.scope, + req.remoteQueryConfig.fields + ) if (!invite) { throw new MedusaError( @@ -38,7 +28,6 @@ export const GET = async ( res.status(200).json({ invite }) } -// delete invite export const DELETE = async ( req: AuthenticatedMedusaRequest, res: MedusaResponse diff --git a/packages/medusa/src/api-v2/admin/invites/accept/route.ts b/packages/medusa/src/api-v2/admin/invites/accept/route.ts index cfd72f3323..70f58c55d3 100644 --- a/packages/medusa/src/api-v2/admin/invites/accept/route.ts +++ b/packages/medusa/src/api-v2/admin/invites/accept/route.ts @@ -5,11 +5,10 @@ import { AuthenticatedMedusaRequest, MedusaResponse, } from "../../../../types/routing" - -import { AdminPostInvitesInviteAcceptReq } from "../validators" +import { AdminInviteAcceptType } from "../validators" export const POST = async ( - req: AuthenticatedMedusaRequest, + req: AuthenticatedMedusaRequest, res: MedusaResponse ) => { if (req.auth.actor_id) { diff --git a/packages/medusa/src/api-v2/admin/invites/helpers.ts b/packages/medusa/src/api-v2/admin/invites/helpers.ts new file mode 100644 index 0000000000..6dfcf6f9b0 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/invites/helpers.ts @@ -0,0 +1,23 @@ +import { MedusaContainer } from "@medusajs/types" +import { + ContainerRegistrationKeys, + remoteQueryObjectFromString, +} from "@medusajs/utils" + +export const refetchInvite = async ( + inviteId: string, + scope: MedusaContainer, + fields: string[] +) => { + const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + const queryObject = remoteQueryObjectFromString({ + entryPoint: "invite", + variables: { + filters: { id: inviteId }, + }, + fields: fields, + }) + + const invites = await remoteQuery(queryObject) + return invites[0] +} diff --git a/packages/medusa/src/api-v2/admin/invites/middlewares.ts b/packages/medusa/src/api-v2/admin/invites/middlewares.ts index 9fe69215ff..755b69eddd 100644 --- a/packages/medusa/src/api-v2/admin/invites/middlewares.ts +++ b/packages/medusa/src/api-v2/admin/invites/middlewares.ts @@ -1,42 +1,25 @@ import * as QueryConfig from "./query-config" import { - AdminCreateInviteRequest, - AdminGetInvitesInviteParams, + AdminCreateInvite, + AdminGetInviteAcceptParams, + AdminGetInviteParams, AdminGetInvitesParams, - AdminPostInvitesInviteAcceptParams, - AdminPostInvitesInviteAcceptReq, + AdminInviteAccept, } from "./validators" -import { transformBody, transformQuery } from "../../../api/middlewares" import { MiddlewareRoute } from "../../../types/middlewares" import { authenticate } from "../../../utils/authenticate-middleware" +import { validateAndTransformQuery } from "../../utils/validate-query" +import { validateAndTransformBody } from "../../utils/validate-body" export const adminInviteRoutesMiddlewares: MiddlewareRoute[] = [ - { - method: "ALL", - matcher: "/admin/invites", - middlewares: [authenticate("admin", ["session", "bearer"])], - }, - { - method: "POST", - matcher: "/admin/invites/accept", - middlewares: [ - authenticate("admin", ["session", "bearer"], { - allowUnregistered: true, - }), - ], - }, - { - method: ["GET", "DELETE"], - matcher: "/admin/invites/:id", - middlewares: [authenticate("admin", ["session", "bearer"])], - }, { method: ["GET"], matcher: "/admin/invites", middlewares: [ - transformQuery( + authenticate("admin", ["session", "bearer", "api-key"]), + validateAndTransformQuery( AdminGetInvitesParams, QueryConfig.listTransformQueryConfig ), @@ -45,22 +28,52 @@ export const adminInviteRoutesMiddlewares: MiddlewareRoute[] = [ { method: ["POST"], matcher: "/admin/invites", - middlewares: [transformBody(AdminCreateInviteRequest)], + middlewares: [ + authenticate("admin", ["session", "bearer", "api-key"]), + validateAndTransformBody(AdminCreateInvite), + validateAndTransformQuery( + AdminGetInviteParams, + QueryConfig.retrieveTransformQueryConfig + ), + ], }, { method: "POST", matcher: "/admin/invites/accept", middlewares: [ - transformBody(AdminPostInvitesInviteAcceptReq), - transformQuery(AdminPostInvitesInviteAcceptParams), + authenticate("admin", ["session", "bearer"], { + allowUnregistered: true, + }), + validateAndTransformBody(AdminInviteAccept), + validateAndTransformQuery( + AdminGetInviteAcceptParams, + QueryConfig.retrieveTransformQueryConfig + ), ], }, { method: ["GET"], matcher: "/admin/invites/:id", middlewares: [ - transformQuery( - AdminGetInvitesInviteParams, + authenticate("admin", ["session", "bearer", "api-key"]), + validateAndTransformQuery( + AdminGetInviteParams, + QueryConfig.retrieveTransformQueryConfig + ), + ], + }, + { + method: ["DELETE"], + matcher: "/admin/invites/:id", + middlewares: [authenticate("admin", ["session", "bearer", "api-key"])], + }, + { + method: "POST", + matcher: "/admin/invites/:id/resend", + middlewares: [ + authenticate("admin", ["session", "bearer", "api-key"]), + validateAndTransformQuery( + AdminGetInviteParams, QueryConfig.retrieveTransformQueryConfig ), ], diff --git a/packages/medusa/src/api-v2/admin/invites/query-config.ts b/packages/medusa/src/api-v2/admin/invites/query-config.ts index 10a0b8d548..546e30563a 100644 --- a/packages/medusa/src/api-v2/admin/invites/query-config.ts +++ b/packages/medusa/src/api-v2/admin/invites/query-config.ts @@ -1,5 +1,3 @@ -export const defaultAdminInviteRelations = [] -export const allowedAdminInviteRelations = [] export const defaultAdminInviteFields = [ "id", "email", @@ -13,9 +11,7 @@ export const defaultAdminInviteFields = [ ] export const retrieveTransformQueryConfig = { - defaultFields: defaultAdminInviteFields, - defaultRelations: defaultAdminInviteRelations, - allowedRelations: allowedAdminInviteRelations, + defaults: defaultAdminInviteFields, isList: false, } diff --git a/packages/medusa/src/api-v2/admin/invites/route.ts b/packages/medusa/src/api-v2/admin/invites/route.ts index e7fbbb2de7..21941782ad 100644 --- a/packages/medusa/src/api-v2/admin/invites/route.ts +++ b/packages/medusa/src/api-v2/admin/invites/route.ts @@ -7,32 +7,27 @@ import { remoteQueryObjectFromString, } from "@medusajs/utils" -import { CreateInviteDTO } from "@medusajs/types" import { createInvitesWorkflow } from "@medusajs/core-flows" +import { AdminCreateInviteType, AdminGetInvitesParamsType } from "./validators" +import { refetchInvite } from "./helpers" -// List invites export const GET = async ( - req: AuthenticatedMedusaRequest, + req: AuthenticatedMedusaRequest, res: MedusaResponse ) => { const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) - - const query = remoteQueryObjectFromString({ + const queryObject = remoteQueryObjectFromString({ entryPoint: "invite", variables: { filters: req.filterableFields, - order: req.listConfig.order, - skip: req.listConfig.skip, - take: req.listConfig.take, + ...req.remoteQueryConfig.pagination, }, - fields: req.listConfig.select as string[], + fields: req.remoteQueryConfig.fields, }) - const { rows: invites, metadata } = await remoteQuery({ - ...query, - }) + const { rows: invites, metadata } = await remoteQuery(queryObject) - res.status(200).json({ + res.json({ invites, count: metadata.count, offset: metadata.skip, @@ -40,9 +35,8 @@ export const GET = async ( }) } -// Create invite export const POST = async ( - req: AuthenticatedMedusaRequest, + req: AuthenticatedMedusaRequest, res: MedusaResponse ) => { const workflow = createInvitesWorkflow(req.scope) @@ -54,7 +48,11 @@ export const POST = async ( } const { result } = await workflow.run(input) + const invite = await refetchInvite( + result[0].id, + req.scope, + req.remoteQueryConfig.fields + ) - const [invite] = result res.status(200).json({ invite }) } diff --git a/packages/medusa/src/api-v2/admin/invites/validators.ts b/packages/medusa/src/api-v2/admin/invites/validators.ts index 8b690c14bc..2c490af811 100644 --- a/packages/medusa/src/api-v2/admin/invites/validators.ts +++ b/packages/medusa/src/api-v2/admin/invites/validators.ts @@ -1,146 +1,51 @@ -import { Type } from "class-transformer" +import { z } from "zod" import { - IsEmail, - IsNotEmpty, - IsOptional, - IsString, - ValidateNested, -} from "class-validator" -import { - DateComparisonOperator, - FindParams, - extendedFindParamsMixin, -} from "../../../types/common" -import { IsType } from "../../../utils" + createFindParams, + createOperatorMap, + createSelectParams, +} from "../../utils/validators" -export class AdminGetInvitesInviteParams extends FindParams {} +export type AdminGetInviteParamsType = z.infer +export const AdminGetInviteParams = createSelectParams() -export class AdminGetInvitesParams extends extendedFindParamsMixin({ +export type AdminGetInvitesParamsType = z.infer +export const AdminGetInvitesParams = createFindParams({ limit: 50, offset: 0, -}) { - /** - * IDs to filter invites by. - */ - @IsOptional() - @IsType([String, [String]]) - id?: string | string[] +}).merge( + z.object({ + q: z.string().optional(), + id: z.union([z.string(), z.array(z.string())]).optional(), + email: z.union([z.string(), z.array(z.string())]).optional(), + created_at: createOperatorMap().optional(), + updated_at: createOperatorMap().optional(), + deleted_at: createOperatorMap().optional(), + $and: z.lazy(() => AdminGetInvitesParams.array()).optional(), + $or: z.lazy(() => AdminGetInvitesParams.array()).optional(), + }) +) - /** - * The field to sort the data by. By default, the sort order is ascending. To change the order to descending, prefix the field name with `-`. - */ - @IsString() - @IsOptional() - order?: string +export type AdminGetInviteAcceptParamsType = z.infer< + typeof AdminGetInviteAcceptParams +> +export const AdminGetInviteAcceptParams = createSelectParams().merge( + z.object({ + token: z.string(), + }) +) - /** - * Date filters to apply on the invites' `update_at` date. - */ - @IsOptional() - @ValidateNested() - @Type(() => DateComparisonOperator) - updated_at?: DateComparisonOperator +export type AdminCreateInviteType = z.infer +export const AdminCreateInvite = z + .object({ + email: z.string(), + }) + .strict() - /** - * Date filters to apply on the customer invites' `created_at` date. - */ - @IsOptional() - @ValidateNested() - @Type(() => DateComparisonOperator) - created_at?: DateComparisonOperator - - /** - * Date filters to apply on the invites' `deleted_at` date. - */ - @IsOptional() - @ValidateNested() - @Type(() => DateComparisonOperator) - deleted_at?: DateComparisonOperator - - /** - * Filter to apply on the invites' `email` field. - */ - @IsOptional() - @IsString() - email?: string - - /** - * Comma-separated fields that should be included in the returned invites. - */ - @IsOptional() - @IsString() - fields?: string - - /** - * The term to search invites emails. - */ - @IsString() - @IsOptional() - q?: string -} - -export class AdminCreateInviteRequest { - @IsEmail() - email: string -} - -/** - * @schema AdminPostInvitesInviteAcceptReq - * type: object - * description: "The details of the invite to be accepted." - * required: - * - token - * - user - * properties: - * token: - * description: "The token of the invite to accept. This is a unique token generated when the invite was created or resent." - * type: string - * user: - * description: "The details of the user to create." - * type: object - * required: - * - first_name - * - last_name - * - password - * properties: - * first_name: - * type: string - * description: the first name of the User - * last_name: - * type: string - * description: the last name of the User - * password: - * description: The password for the User - * type: string - * format: password - */ -export class AdminPostInvitesInviteAcceptReq { - /** - * If email is not passed, we default to using the email of the invite. - */ - @IsString() - @IsOptional() - email: string - /** - * The invite's first name. - */ - @IsString() - @IsOptional() - first_name: string - - /** - * The invite's last name. - */ - @IsString() - @IsOptional() - last_name: string -} - -export class AdminPostInvitesInviteAcceptParams { - @IsString() - @IsNotEmpty() - token: string - - @IsOptional() - expand = undefined -} +export type AdminInviteAcceptType = z.infer +export const AdminInviteAccept = z + .object({ + email: z.string().optional(), + first_name: z.string().optional(), + last_name: z.string().optional(), + }) + .strict() diff --git a/packages/medusa/src/api-v2/admin/payments/[id]/capture/route.ts b/packages/medusa/src/api-v2/admin/payments/[id]/capture/route.ts index bac1457625..4be56c70ce 100644 --- a/packages/medusa/src/api-v2/admin/payments/[id]/capture/route.ts +++ b/packages/medusa/src/api-v2/admin/payments/[id]/capture/route.ts @@ -1,21 +1,15 @@ import { capturePaymentWorkflow } from "@medusajs/core-flows" -import { - ContainerRegistrationKeys, - remoteQueryObjectFromString, -} from "@medusajs/utils" import { AuthenticatedMedusaRequest, MedusaResponse, } from "../../../../../types/routing" -import { defaultAdminPaymentFields } from "../../query-config" -import { AdminPostPaymentsCapturesReq } from "../../validators" +import { refetchPayment } from "../../helpers" +import { AdminCreatePaymentCaptureType } from "../../validators" export const POST = async ( - req: AuthenticatedMedusaRequest, + req: AuthenticatedMedusaRequest, res: MedusaResponse ) => { - const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) - const { id } = req.params const { errors } = await capturePaymentWorkflow(req.scope).run({ @@ -31,13 +25,11 @@ export const POST = async ( throw errors[0].error } - const query = remoteQueryObjectFromString({ - entryPoint: "payments", - variables: { id }, - fields: defaultAdminPaymentFields, - }) - - const [payment] = await remoteQuery(query) + const payment = await refetchPayment( + id, + req.scope, + req.remoteQueryConfig.fields + ) res.status(200).json({ payment }) } diff --git a/packages/medusa/src/api-v2/admin/payments/[id]/refund/route.ts b/packages/medusa/src/api-v2/admin/payments/[id]/refund/route.ts index bbff08339f..8b680e61f7 100644 --- a/packages/medusa/src/api-v2/admin/payments/[id]/refund/route.ts +++ b/packages/medusa/src/api-v2/admin/payments/[id]/refund/route.ts @@ -1,23 +1,16 @@ import { refundPaymentWorkflow } from "@medusajs/core-flows" -import { - ContainerRegistrationKeys, - remoteQueryObjectFromString, -} from "@medusajs/utils" import { AuthenticatedMedusaRequest, MedusaResponse, } from "../../../../../types/routing" -import { defaultAdminPaymentFields } from "../../query-config" -import { AdminPostPaymentsRefundsReq } from "../../validators" +import { refetchPayment } from "../../helpers" +import { AdminCreatePaymentRefundType } from "../../validators" export const POST = async ( - req: AuthenticatedMedusaRequest, + req: AuthenticatedMedusaRequest, res: MedusaResponse ) => { - const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) - const { id } = req.params - const { errors } = await refundPaymentWorkflow(req.scope).run({ input: { payment_id: id, @@ -31,13 +24,11 @@ export const POST = async ( throw errors[0].error } - const query = remoteQueryObjectFromString({ - entryPoint: "payments", - variables: { id }, - fields: defaultAdminPaymentFields, - }) - - const [payment] = await remoteQuery(query) + const payment = await refetchPayment( + id, + req.scope, + req.remoteQueryConfig.fields + ) res.status(200).json({ payment }) } diff --git a/packages/medusa/src/api-v2/admin/payments/[id]/route.ts b/packages/medusa/src/api-v2/admin/payments/[id]/route.ts index 340539ec68..fbb065f9c9 100644 --- a/packages/medusa/src/api-v2/admin/payments/[id]/route.ts +++ b/packages/medusa/src/api-v2/admin/payments/[id]/route.ts @@ -1,28 +1,19 @@ -import { Modules } from "@medusajs/modules-sdk" -import { - ContainerRegistrationKeys, - remoteQueryObjectFromString, -} from "@medusajs/utils" import { AuthenticatedMedusaRequest, MedusaResponse, } from "../../../../types/routing" +import { AdminGetPaymentParamsType } from "../validators" +import { refetchPayment } from "../helpers" export const GET = async ( - req: AuthenticatedMedusaRequest, + req: AuthenticatedMedusaRequest, res: MedusaResponse ) => { - const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) - - const { id } = req.params - - const query = remoteQueryObjectFromString({ - entryPoint: Modules.PAYMENT, - variables: { id }, - fields: req.retrieveConfig.select as string[], - }) - - const [payment] = await remoteQuery(query) + const payment = await refetchPayment( + req.params.id, + req.scope, + req.remoteQueryConfig.fields + ) res.status(200).json({ payment }) } diff --git a/packages/medusa/src/api-v2/admin/payments/helpers.ts b/packages/medusa/src/api-v2/admin/payments/helpers.ts new file mode 100644 index 0000000000..36f83ae660 --- /dev/null +++ b/packages/medusa/src/api-v2/admin/payments/helpers.ts @@ -0,0 +1,23 @@ +import { MedusaContainer } from "@medusajs/types" +import { + ContainerRegistrationKeys, + remoteQueryObjectFromString, +} from "@medusajs/utils" + +export const refetchPayment = async ( + paymentId: string, + scope: MedusaContainer, + fields: string[] +) => { + const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + const queryObject = remoteQueryObjectFromString({ + entryPoint: "payment", + variables: { + filters: { id: paymentId }, + }, + fields: fields, + }) + + const payments = await remoteQuery(queryObject) + return payments[0] +} diff --git a/packages/medusa/src/api-v2/admin/payments/middlewares.ts b/packages/medusa/src/api-v2/admin/payments/middlewares.ts index ac72d3004e..3c21cb97b8 100644 --- a/packages/medusa/src/api-v2/admin/payments/middlewares.ts +++ b/packages/medusa/src/api-v2/admin/payments/middlewares.ts @@ -1,25 +1,28 @@ -import { transformBody, transformQuery } from "../../../api/middlewares" import { MiddlewareRoute } from "../../../types/middlewares" import { authenticate } from "../../../utils/authenticate-middleware" +import { unlessPath } from "../../utils/unless-path" +import { validateAndTransformBody } from "../../utils/validate-body" +import { validateAndTransformQuery } from "../../utils/validate-query" import * as queryConfig from "./query-config" import { + AdminCreatePaymentCapture, + AdminCreatePaymentRefund, + AdminGetPaymentParams, + AdminGetPaymentProvidersParams, AdminGetPaymentsParams, - AdminGetPaymentsPaymentProvidersParams, - AdminPostPaymentsCapturesReq, - AdminPostPaymentsRefundsReq, } from "./validators" export const adminPaymentRoutesMiddlewares: MiddlewareRoute[] = [ { method: "ALL", matcher: "/admin/payments", - middlewares: [authenticate("admin", ["session", "bearer"])], + middlewares: [authenticate("admin", ["session", "bearer", "api-key"])], }, { method: ["GET"], matcher: "/admin/payments", middlewares: [ - transformQuery( + validateAndTransformQuery( AdminGetPaymentsParams, queryConfig.listTransformQueryConfig ), @@ -29,8 +32,8 @@ export const adminPaymentRoutesMiddlewares: MiddlewareRoute[] = [ method: ["GET"], matcher: "/admin/payments/payment-providers", middlewares: [ - transformQuery( - AdminGetPaymentsPaymentProvidersParams, + validateAndTransformQuery( + AdminGetPaymentProvidersParams, queryConfig.listTransformPaymentProvidersQueryConfig ), ], @@ -39,20 +42,35 @@ export const adminPaymentRoutesMiddlewares: MiddlewareRoute[] = [ method: ["GET"], matcher: "/admin/payments/:id", middlewares: [ - transformQuery( - AdminGetPaymentsParams, - queryConfig.retrieveTransformQueryConfig + unlessPath( + new RegExp("/admin/payments/payment-providers"), + validateAndTransformQuery( + AdminGetPaymentParams, + queryConfig.retrieveTransformQueryConfig + ) ), ], }, { method: ["POST"], matcher: "/admin/payments/:id/capture", - middlewares: [transformBody(AdminPostPaymentsCapturesReq)], + middlewares: [ + validateAndTransformBody(AdminCreatePaymentCapture), + validateAndTransformQuery( + AdminGetPaymentParams, + queryConfig.retrieveTransformQueryConfig + ), + ], }, { method: ["POST"], matcher: "/admin/payments/:id/refund", - middlewares: [transformBody(AdminPostPaymentsRefundsReq)], + middlewares: [ + validateAndTransformBody(AdminCreatePaymentRefund), + validateAndTransformQuery( + AdminGetPaymentParams, + queryConfig.retrieveTransformQueryConfig + ), + ], }, ] diff --git a/packages/medusa/src/api-v2/admin/payments/payment-providers/route.ts b/packages/medusa/src/api-v2/admin/payments/payment-providers/route.ts index a0b9bef1f6..2c92f5635c 100644 --- a/packages/medusa/src/api-v2/admin/payments/payment-providers/route.ts +++ b/packages/medusa/src/api-v2/admin/payments/payment-providers/route.ts @@ -1,30 +1,33 @@ -import { ModuleRegistrationName } from "@medusajs/modules-sdk" - -import { IPaymentModuleService } from "@medusajs/types" import { AuthenticatedMedusaRequest, MedusaResponse, } from "../../../../types/routing" -import { AdminGetPaymentsPaymentProvidersParams } from "../validators" +import { AdminGetPaymentProvidersParamsType } from "../validators" +import { + ContainerRegistrationKeys, + remoteQueryObjectFromString, +} from "@medusajs/utils" export const GET = async ( - req: AuthenticatedMedusaRequest, + req: AuthenticatedMedusaRequest, res: MedusaResponse ) => { - const paymentModule = req.scope.resolve( - ModuleRegistrationName.PAYMENT - ) + const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) + const queryObject = remoteQueryObjectFromString({ + entryPoint: "payment_provider", + variables: { + filters: req.filterableFields, + ...req.remoteQueryConfig.pagination, + }, + fields: req.remoteQueryConfig.fields, + }) - const [payment_providers, count] = - await paymentModule.listAndCountPaymentProviders(req.filterableFields, { - skip: req.listConfig.skip, - take: req.listConfig.take, - }) + const { rows: payment_providers, metadata } = await remoteQuery(queryObject) - res.status(200).json({ - count, + res.json({ payment_providers, - offset: req.listConfig.skip, - limit: req.listConfig.take, + count: metadata.count, + offset: metadata.skip, + limit: metadata.take, }) } diff --git a/packages/medusa/src/api-v2/admin/payments/query-config.ts b/packages/medusa/src/api-v2/admin/payments/query-config.ts index 7206c43056..a30ae02ac7 100644 --- a/packages/medusa/src/api-v2/admin/payments/query-config.ts +++ b/packages/medusa/src/api-v2/admin/payments/query-config.ts @@ -11,29 +11,19 @@ export const defaultAdminPaymentFields = [ "refunds.amount", ] -export const defaultAdminPaymentRelations = ["captures", "refunds"] - -export const allowedAdminPaymentRelations = ["captures", "refunds"] - export const listTransformQueryConfig = { - defaultFields: defaultAdminPaymentFields, - defaultRelations: defaultAdminPaymentRelations, - allowedRelations: allowedAdminPaymentRelations, + defaults: defaultAdminPaymentFields, isList: true, } export const retrieveTransformQueryConfig = { - defaultFields: defaultAdminPaymentFields, - defaultRelations: defaultAdminPaymentRelations, - allowedRelations: allowedAdminPaymentRelations, + defaults: defaultAdminPaymentFields, isList: false, } export const defaultAdminPaymentPaymentProviderFields = ["id", "is_enabled"] export const listTransformPaymentProvidersQueryConfig = { - defaultFields: defaultAdminPaymentPaymentProviderFields, - defaultRelations: [], - allowedRelations: [], + defaults: defaultAdminPaymentPaymentProviderFields, isList: true, } diff --git a/packages/medusa/src/api-v2/admin/payments/route.ts b/packages/medusa/src/api-v2/admin/payments/route.ts index 963a69f4e3..1566976bee 100644 --- a/packages/medusa/src/api-v2/admin/payments/route.ts +++ b/packages/medusa/src/api-v2/admin/payments/route.ts @@ -1,4 +1,3 @@ -import { Modules } from "@medusajs/modules-sdk" import { ContainerRegistrationKeys, remoteQueryObjectFromString, @@ -7,27 +6,25 @@ import { AuthenticatedMedusaRequest, MedusaResponse, } from "../../../types/routing" +import { AdminGetPaymentsParamsType } from "./validators" export const GET = async ( - req: AuthenticatedMedusaRequest, + req: AuthenticatedMedusaRequest, res: MedusaResponse ) => { const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) - - const query = remoteQueryObjectFromString({ - entryPoint: Modules.PAYMENT, + const queryObject = remoteQueryObjectFromString({ + entryPoint: "payment", variables: { filters: req.filterableFields, - order: req.listConfig.order, - skip: req.listConfig.skip, - take: req.listConfig.take, + ...req.remoteQueryConfig.pagination, }, - fields: req.listConfig.select as string[], + fields: req.remoteQueryConfig.fields, }) - const { rows: payments, metadata } = await remoteQuery(query) + const { rows: payments, metadata } = await remoteQuery(queryObject) - res.status(200).json({ + res.json({ payments, count: metadata.count, offset: metadata.skip, diff --git a/packages/medusa/src/api-v2/admin/payments/validators.ts b/packages/medusa/src/api-v2/admin/payments/validators.ts index 59b1c1d55d..9d2ef4eeec 100644 --- a/packages/medusa/src/api-v2/admin/payments/validators.ts +++ b/packages/medusa/src/api-v2/admin/payments/validators.ts @@ -1,79 +1,57 @@ -import { Type } from "class-transformer" -import { IsBoolean, IsInt, IsOptional, ValidateNested } from "class-validator" +import { z } from "zod" import { - DateComparisonOperator, - FindParams, - extendedFindParamsMixin, -} from "../../../types/common" -import { IsType } from "../../../utils" + createFindParams, + createOperatorMap, + createSelectParams, +} from "../../utils/validators" -export class AdminGetPaymentsPaymentParams extends FindParams {} +export type AdminGetPaymentParamsType = z.infer +export const AdminGetPaymentParams = createSelectParams() -export class AdminGetPaymentsParams extends extendedFindParamsMixin({ +export type AdminGetPaymentsParamsType = z.infer +export const AdminGetPaymentsParams = createFindParams({ limit: 20, offset: 0, -}) { - /** - * IDs to filter users by. - */ - @IsOptional() - @IsType([String, [String]]) - id?: string | string[] +}).merge( + z.object({ + id: z.union([z.string(), z.array(z.string())]).optional(), + created_at: createOperatorMap().optional(), + updated_at: createOperatorMap().optional(), + deleted_at: createOperatorMap().optional(), + $and: z.lazy(() => AdminGetPaymentsParams.array()).optional(), + $or: z.lazy(() => AdminGetPaymentsParams.array()).optional(), + }) +) - /** - * Date filters to apply on the users' `update_at` date. - */ - @IsOptional() - @ValidateNested() - @Type(() => DateComparisonOperator) - updated_at?: DateComparisonOperator +export type AdminGetPaymentProvidersParamsType = z.infer< + typeof AdminGetPaymentProvidersParams +> +export const AdminGetPaymentProvidersParams = createFindParams({ + limit: 20, + offset: 0, +}).merge( + z.object({ + id: z.union([z.string(), z.array(z.string())]).optional(), + is_enabled: z.boolean().optional(), + $and: z.lazy(() => AdminGetPaymentProvidersParams.array()).optional(), + $or: z.lazy(() => AdminGetPaymentProvidersParams.array()).optional(), + }) +) - /** - * Date filters to apply on the customer users' `created_at` date. - */ - @IsOptional() - @ValidateNested() - @Type(() => DateComparisonOperator) - created_at?: DateComparisonOperator +export type AdminCreatePaymentCaptureType = z.infer< + typeof AdminCreatePaymentCapture +> +export const AdminCreatePaymentCapture = z + .object({ + amount: z.number().optional(), + }) + .strict() - /** - * Date filters to apply on the users' `deleted_at` date. - */ - @IsOptional() - @ValidateNested() - @Type(() => DateComparisonOperator) - deleted_at?: DateComparisonOperator -} - -export class AdminPostPaymentsCapturesReq { - @IsInt() - @IsOptional() - amount?: number -} - -export class AdminPostPaymentsRefundsReq { - @IsInt() - @IsOptional() - amount?: number -} - -export class AdminGetPaymentsPaymentProvidersParams extends extendedFindParamsMixin( - { - limit: 20, - offset: 0, - } -) { - /** - * IDs to filter users by. - */ - @IsOptional() - @IsType([String, [String]]) - id?: string | string[] - - /** - * Filter providers by `enabled` flag - */ - @IsBoolean() - @IsOptional() - is_enabled?: boolean -} +export type AdminCreatePaymentRefundType = z.infer< + typeof AdminCreatePaymentRefund +> +export const AdminCreatePaymentRefund = z + .object({ + amount: z.number().optional(), + }) + .strict() diff --git a/packages/medusa/src/api-v2/admin/product-types/helpers.ts b/packages/medusa/src/api-v2/admin/product-types/helpers.ts index 30337bf58f..0ef85a0bc1 100644 --- a/packages/medusa/src/api-v2/admin/product-types/helpers.ts +++ b/packages/medusa/src/api-v2/admin/product-types/helpers.ts @@ -1,12 +1,15 @@ import { MedusaContainer } from "@medusajs/types" -import { remoteQueryObjectFromString } from "@medusajs/utils" +import { + ContainerRegistrationKeys, + remoteQueryObjectFromString, +} from "@medusajs/utils" export const refetchProductType = async ( productTypeId: string, scope: MedusaContainer, fields: string[] ) => { - const remoteQuery = scope.resolve("remoteQuery") + const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) const queryObject = remoteQueryObjectFromString({ entryPoint: "product_type", variables: { diff --git a/packages/medusa/src/api-v2/utils/unless-path.ts b/packages/medusa/src/api-v2/utils/unless-path.ts new file mode 100644 index 0000000000..6bdbea515d --- /dev/null +++ b/packages/medusa/src/api-v2/utils/unless-path.ts @@ -0,0 +1,15 @@ +import { NextFunction } from "express" +import { MedusaRequest, MedusaResponse } from "../../types/routing" +import { MiddlewareFunction } from "../../types/middlewares" + +// Due to how our route loader works, where we load all middlewares before routes, ambiguous routes end up having all middlewares on different routes executed before the route handler is. +// This function allows us to skip middlewares for particular routes, so we can temporarily solve this without completely breaking the route loader for everyone. +export const unlessPath = + (onPath: RegExp, middleware: MiddlewareFunction) => + (req: MedusaRequest, res: MedusaResponse, next: NextFunction) => { + if (onPath.test(req.path)) { + return next() + } else { + return middleware(req, res, next) + } + }