From 2a98be6b658897755018bcd29d437485490f58a2 Mon Sep 17 00:00:00 2001 From: "Carlos R. L. Rodrigues" <37986729+carlos-r-l-rodrigues@users.noreply.github.com> Date: Fri, 18 Oct 2024 05:59:24 -0300 Subject: [PATCH] feat(medusa): ordem items endpoint (#9646) --- .../http/__tests__/claims/claims.spec.ts | 29 +++++++- .../src/order/workflows/get-order-detail.ts | 6 +- packages/core/framework/src/http/types.ts | 2 +- .../src/http/utils/refetch-entities.ts | 4 +- .../src/http/utils/validate-query.ts | 10 +-- .../types/src/http/order/admin/entities.ts | 13 ++++ .../types/src/http/order/admin/queries.ts | 7 ++ .../types/src/http/order/admin/responses.ts | 11 ++- .../api/admin/orders/[id]/line-items/route.ts | 71 +++++++++++++++++++ .../medusa/src/api/admin/orders/[id]/route.ts | 12 ++-- .../src/api/admin/orders/middlewares.ts | 13 +++- .../src/api/admin/orders/query-config.ts | 14 ++++ .../medusa/src/api/admin/orders/validators.ts | 13 ++++ packages/medusa/src/types/global.ts | 2 +- .../integration-tests/__tests__/index.spec.ts | 12 +++- packages/modules/order/src/joiner-config.ts | 2 + 16 files changed, 202 insertions(+), 19 deletions(-) create mode 100644 packages/medusa/src/api/admin/orders/[id]/line-items/route.ts diff --git a/integration-tests/http/__tests__/claims/claims.spec.ts b/integration-tests/http/__tests__/claims/claims.spec.ts index 2c798f488a..eaf4cb1c8d 100644 --- a/integration-tests/http/__tests__/claims/claims.spec.ts +++ b/integration-tests/http/__tests__/claims/claims.spec.ts @@ -1,3 +1,4 @@ +import { medusaIntegrationTestRunner } from "@medusajs/test-utils" import { ClaimReason, ClaimType, @@ -5,7 +6,6 @@ import { Modules, RuleOperator, } from "@medusajs/utils" -import { medusaIntegrationTestRunner } from "@medusajs/test-utils" import { adminHeaders, createAdminUser, @@ -1372,6 +1372,33 @@ medusaIntegrationTestRunner({ ) // additional items is one of the props that should be undefined expect(res.data.claim.additional_items).toBeUndefined() + + const resLineItems = await api.get( + `/admin/orders/${order.id}/line-items`, + adminHeaders + ) + + expect(resLineItems.data).toEqual( + expect.objectContaining({ + order_items: [ + expect.objectContaining({ + order_id: order.id, + item_id: item.id, + version: 3, + item: expect.objectContaining({ + title: "Custom Item 2", + unit_price: 25, + }), + history: { + version: { + from: 1, + to: 3, + }, + }, + }), + ], + }) + ) }) }) }, diff --git a/packages/core/core-flows/src/order/workflows/get-order-detail.ts b/packages/core/core-flows/src/order/workflows/get-order-detail.ts index d7ae2ae0af..1c9d4368ca 100644 --- a/packages/core/core-flows/src/order/workflows/get-order-detail.ts +++ b/packages/core/core-flows/src/order/workflows/get-order-detail.ts @@ -22,6 +22,7 @@ export const getOrderDetailWorkflow = createWorkflow( input: WorkflowData<{ fields: string[] order_id: string + version?: number }> ): WorkflowResponse => { const fields = transform(input, ({ fields }) => { @@ -38,7 +39,10 @@ export const getOrderDetailWorkflow = createWorkflow( const order: OrderDTO = useRemoteQueryStep({ entry_point: "orders", fields, - variables: { id: input.order_id }, + variables: { + id: input.order_id, + version: input.version, + }, list: false, throw_if_key_not_found: true, }) diff --git a/packages/core/framework/src/http/types.ts b/packages/core/framework/src/http/types.ts index 9b26d4829b..e1f19434c3 100644 --- a/packages/core/framework/src/http/types.ts +++ b/packages/core/framework/src/http/types.ts @@ -116,7 +116,7 @@ export interface MedusaRequest */ remoteQueryConfig: { fields: string[] - pagination: { order?: Record; skip?: number; take?: number } + pagination: { order?: Record; skip: number; take?: number } } /** * An object containing the fields that are filterable e.g `{ id: Any }` diff --git a/packages/core/framework/src/http/utils/refetch-entities.ts b/packages/core/framework/src/http/utils/refetch-entities.ts index 07f1850df9..e8b7e33e9c 100644 --- a/packages/core/framework/src/http/utils/refetch-entities.ts +++ b/packages/core/framework/src/http/utils/refetch-entities.ts @@ -1,7 +1,7 @@ import { MedusaContainer } from "@medusajs/types" import { - isString, ContainerRegistrationKeys, + isString, remoteQueryObjectFromString, } from "@medusajs/utils" import { MedusaRequest } from "../types" @@ -11,7 +11,7 @@ export const refetchEntities = async ( idOrFilter: string | object, scope: MedusaContainer, fields: string[], - pagination: MedusaRequest["remoteQueryConfig"]["pagination"] = {} + pagination?: MedusaRequest["remoteQueryConfig"]["pagination"] ) => { const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) const filters = isString(idOrFilter) ? { id: idOrFilter } : idOrFilter diff --git a/packages/core/framework/src/http/utils/validate-query.ts b/packages/core/framework/src/http/utils/validate-query.ts index 28b0815b8c..a450f157ac 100644 --- a/packages/core/framework/src/http/utils/validate-query.ts +++ b/packages/core/framework/src/http/utils/validate-query.ts @@ -1,8 +1,8 @@ -import { z } from "zod" -import { omit } from "lodash" -import { NextFunction } from "express" -import { MedusaError, removeUndefinedProperties } from "@medusajs/utils" import { BaseEntity, QueryConfig, RequestQueryFields } from "@medusajs/types" +import { MedusaError, removeUndefinedProperties } from "@medusajs/utils" +import { NextFunction } from "express" +import { omit } from "lodash" +import { z } from "zod" import { zodValidator } from "../../zod/zod-helpers" import { MedusaRequest, MedusaResponse } from "../types" @@ -79,7 +79,7 @@ export function validateAndTransformQuery( req.validatedQuery = validated req.filterableFields = getFilterableFields(req.validatedQuery) - req.remoteQueryConfig = cnf.remoteQueryConfig + req.remoteQueryConfig = cnf.remoteQueryConfig as any req.listConfig = (cnf as any).listConfig req.retrieveConfig = (cnf as any).retrieveConfig diff --git a/packages/core/types/src/http/order/admin/entities.ts b/packages/core/types/src/http/order/admin/entities.ts index 8ded34c297..29215d0e3a 100644 --- a/packages/core/types/src/http/order/admin/entities.ts +++ b/packages/core/types/src/http/order/admin/entities.ts @@ -47,6 +47,19 @@ export interface AdminOrderChangeAction export interface AdminOrderFulfillment extends BaseOrderFulfillment {} +export interface AdminOrderItem { + order_id: string + item_id: string + version: number + history: { + version: { + from: number + to: number + } + } + item: AdminOrderLineItem +} + export interface AdminOrderLineItem extends Omit { variant?: AdminProductVariant diff --git a/packages/core/types/src/http/order/admin/queries.ts b/packages/core/types/src/http/order/admin/queries.ts index 72385df4f4..7d9c1a40c9 100644 --- a/packages/core/types/src/http/order/admin/queries.ts +++ b/packages/core/types/src/http/order/admin/queries.ts @@ -15,3 +15,10 @@ export interface AdminOrderFilters extends FindParams, BaseOrderFilters { } export interface AdminOrderChangesFilters extends BaseOrderChangesFilters {} + +export interface AdminOrderItemsFilters extends FindParams { + id?: string[] | string + item_id?: string[] | string + order_id?: string[] | string + version?: number[] | number +} diff --git a/packages/core/types/src/http/order/admin/responses.ts b/packages/core/types/src/http/order/admin/responses.ts index 75c031ca83..dbcb8a0096 100644 --- a/packages/core/types/src/http/order/admin/responses.ts +++ b/packages/core/types/src/http/order/admin/responses.ts @@ -1,5 +1,10 @@ import { PaginatedResponse } from "../../common" -import { AdminOrder, AdminOrderChange, AdminOrderPreview } from "./entities" +import { + AdminOrder, + AdminOrderChange, + AdminOrderItem, + AdminOrderPreview, +} from "./entities" export interface AdminOrderResponse { order: AdminOrder @@ -13,6 +18,10 @@ export type AdminOrderListResponse = PaginatedResponse<{ orders: AdminOrder[] }> +export type AdminOrderLineItemsListResponse = { + order_items: AdminOrderItem[] +} + export interface AdminOrderPreviewResponse { order: AdminOrderPreview } diff --git a/packages/medusa/src/api/admin/orders/[id]/line-items/route.ts b/packages/medusa/src/api/admin/orders/[id]/line-items/route.ts new file mode 100644 index 0000000000..b50eebd377 --- /dev/null +++ b/packages/medusa/src/api/admin/orders/[id]/line-items/route.ts @@ -0,0 +1,71 @@ +import { + AuthenticatedMedusaRequest, + MedusaResponse, +} from "@medusajs/framework/http" +import { HttpTypes } from "@medusajs/framework/types" +import { + ContainerRegistrationKeys, + deduplicate, +} from "@medusajs/framework/utils" + +export const GET = async ( + req: AuthenticatedMedusaRequest, + res: MedusaResponse +) => { + const { id } = req.params + + const query = req.scope.resolve(ContainerRegistrationKeys.QUERY) + + const result = await query.graph({ + entity: "order_items", + filters: { + ...req.filterableFields, + order_id: id, + }, + fields: deduplicate( + req.remoteQueryConfig.fields.concat(["item_id", "version"]) + ), + }) + + const data = result.data + const deduplicatedItems = {} + + for (const item of data) { + const itemId = item.item_id + const version = item.version + + if (!deduplicatedItems[itemId]) { + deduplicatedItems[itemId] = { + ...item, + history: { + version: { + from: version, + to: version, + }, + }, + } + continue + } + + deduplicatedItems[itemId].history.version.to = Math.max( + version, + deduplicatedItems[itemId].history.version.to + ) + + deduplicatedItems[itemId].history.version.from = Math.min( + version, + deduplicatedItems[itemId].history.version.from + ) + + if (version > deduplicatedItems[itemId].version) { + deduplicatedItems[itemId] = { + ...item, + history: deduplicatedItems[itemId].history, + } + } + } + + res.json({ + order_items: Object.values(deduplicatedItems), + }) +} diff --git a/packages/medusa/src/api/admin/orders/[id]/route.ts b/packages/medusa/src/api/admin/orders/[id]/route.ts index df493a2d57..dd83af5c43 100644 --- a/packages/medusa/src/api/admin/orders/[id]/route.ts +++ b/packages/medusa/src/api/admin/orders/[id]/route.ts @@ -1,16 +1,17 @@ import { getOrderDetailWorkflow } from "@medusajs/core-flows" -import { - ContainerRegistrationKeys, - remoteQueryObjectFromString, -} from "@medusajs/framework/utils" import { AuthenticatedMedusaRequest, MedusaResponse, } from "@medusajs/framework/http" import { HttpTypes } from "@medusajs/framework/types" +import { + ContainerRegistrationKeys, + remoteQueryObjectFromString, +} from "@medusajs/framework/utils" +import { AdminGetOrdersOrderParamsType } from "../validators" export const GET = async ( - req: AuthenticatedMedusaRequest, + req: AuthenticatedMedusaRequest, res: MedusaResponse ) => { const workflow = getOrderDetailWorkflow(req.scope) @@ -18,6 +19,7 @@ export const GET = async ( input: { fields: req.remoteQueryConfig.fields, order_id: req.params.id, + version: req.validatedQuery.version as number, }, }) diff --git a/packages/medusa/src/api/admin/orders/middlewares.ts b/packages/medusa/src/api/admin/orders/middlewares.ts index bcaf9d8ce6..6e3a931b05 100644 --- a/packages/medusa/src/api/admin/orders/middlewares.ts +++ b/packages/medusa/src/api/admin/orders/middlewares.ts @@ -1,11 +1,12 @@ -import { MiddlewareRoute } from "@medusajs/framework/http" import { validateAndTransformBody, validateAndTransformQuery, } from "@medusajs/framework" +import { MiddlewareRoute } from "@medusajs/framework/http" import * as QueryConfig from "./query-config" import { AdminCompleteOrder, + AdminGetOrdersOrderItemsParams, AdminGetOrdersOrderParams, AdminGetOrdersParams, AdminMarkOrderFulfillmentDelivered, @@ -36,6 +37,16 @@ export const adminOrderRoutesMiddlewares: MiddlewareRoute[] = [ ), ], }, + { + method: ["GET"], + matcher: "/admin/orders/:id/line-items", + middlewares: [ + validateAndTransformQuery( + AdminGetOrdersOrderItemsParams, + QueryConfig.listOrderItemsQueryConfig + ), + ], + }, { method: ["GET"], matcher: "/admin/orders/:id/changes", diff --git a/packages/medusa/src/api/admin/orders/query-config.ts b/packages/medusa/src/api/admin/orders/query-config.ts index fd52ac20ad..d05a504951 100644 --- a/packages/medusa/src/api/admin/orders/query-config.ts +++ b/packages/medusa/src/api/admin/orders/query-config.ts @@ -81,6 +81,14 @@ export const defaultAdminRetrieveOrderChangesFields = [ "updated_at", ] +export const defaultAdminOrderItemsFields = [ + "id", + "order_id", + "item_id", + "version", + "*item", +] + export const retrieveTransformQueryConfig = { defaults: defaultAdminRetrieveOrderFields, isList: false, @@ -96,3 +104,9 @@ export const retrieveOrderChangesTransformQueryConfig = { defaults: defaultAdminRetrieveOrderChangesFields, isList: false, } + +export const listOrderItemsQueryConfig = { + defaults: defaultAdminOrderItemsFields, + defaultLimit: 100, + isList: true, +} diff --git a/packages/medusa/src/api/admin/orders/validators.ts b/packages/medusa/src/api/admin/orders/validators.ts index 0f8e3ccfe7..df6661369c 100644 --- a/packages/medusa/src/api/admin/orders/validators.ts +++ b/packages/medusa/src/api/admin/orders/validators.ts @@ -10,6 +10,7 @@ export const AdminGetOrdersOrderParams = createSelectParams().merge( z.object({ id: z.union([z.string(), z.array(z.string())]).optional(), status: z.union([z.string(), z.array(z.string())]).optional(), + version: z.number().optional(), created_at: createOperatorMap().optional(), updated_at: createOperatorMap().optional(), deleted_at: createOperatorMap().optional(), @@ -20,6 +21,18 @@ export type AdminGetOrdersOrderParamsType = z.infer< typeof AdminGetOrdersOrderParams > +export const AdminGetOrdersOrderItemsParams = createSelectParams().merge( + z.object({ + id: z.union([z.string(), z.array(z.string())]).optional(), + item_id: z.union([z.string(), z.array(z.string())]).optional(), + version: z.number().optional(), + }) +) + +export type AdminGetOrdersOrderItemsParamsType = z.infer< + typeof AdminGetOrdersOrderParams +> + /** * Parameters used to filter and configure the pagination of the retrieved order. */ diff --git a/packages/medusa/src/types/global.ts b/packages/medusa/src/types/global.ts index 3d4554b646..942f248797 100644 --- a/packages/medusa/src/types/global.ts +++ b/packages/medusa/src/types/global.ts @@ -32,7 +32,7 @@ declare global { fields: string[] pagination: { order?: Record - skip?: number + skip: number take?: number } } diff --git a/packages/modules/order/integration-tests/__tests__/index.spec.ts b/packages/modules/order/integration-tests/__tests__/index.spec.ts index 06af48f5ef..a01bd0992e 100644 --- a/packages/modules/order/integration-tests/__tests__/index.spec.ts +++ b/packages/modules/order/integration-tests/__tests__/index.spec.ts @@ -1,7 +1,7 @@ import { IOrderModuleService } from "@medusajs/framework/types" import { Module, Modules } from "@medusajs/framework/utils" -import { OrderModuleService } from "@services" import { moduleIntegrationTestRunner } from "@medusajs/test-utils" +import { OrderModuleService } from "@services" moduleIntegrationTestRunner({ moduleName: Modules.ORDER, @@ -18,6 +18,7 @@ moduleIntegrationTestRunner({ "orderChange", "orderClaim", "orderExchange", + "orderItem", "orderLineItem", "orderShippingMethod", "orderTransaction", @@ -89,6 +90,15 @@ moduleIntegrationTestRunner({ field: "orderExchange", }, }, + orderItem: { + id: { + linkable: "order_item_id", + entity: "OrderItem", + primaryKey: "id", + serviceName: "order", + field: "orderItem", + }, + }, orderLineItem: { id: { linkable: "order_line_item_id", diff --git a/packages/modules/order/src/joiner-config.ts b/packages/modules/order/src/joiner-config.ts index edc6873ad1..6968c5cf9e 100644 --- a/packages/modules/order/src/joiner-config.ts +++ b/packages/modules/order/src/joiner-config.ts @@ -5,6 +5,7 @@ import { OrderChange, OrderClaim, OrderExchange, + OrderItem, OrderLineItem, OrderShippingMethod, OrderTransaction, @@ -25,6 +26,7 @@ export const joinerConfig = defineJoinerConfig(Modules.ORDER, { OrderChange, OrderClaim, OrderExchange, + OrderItem, OrderLineItem, OrderShippingMethod, OrderTransaction,