feat(medusa): ordem items endpoint (#9646)

This commit is contained in:
Carlos R. L. Rodrigues
2024-10-18 05:59:24 -03:00
committed by GitHub
parent 0a37675f0e
commit 2a98be6b65
16 changed files with 202 additions and 19 deletions

View File

@@ -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,
},
},
}),
],
})
)
})
})
},

View File

@@ -22,6 +22,7 @@ export const getOrderDetailWorkflow = createWorkflow(
input: WorkflowData<{
fields: string[]
order_id: string
version?: number
}>
): WorkflowResponse<OrderDetailDTO> => {
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,
})

View File

@@ -116,7 +116,7 @@ export interface MedusaRequest<Body = unknown>
*/
remoteQueryConfig: {
fields: string[]
pagination: { order?: Record<string, string>; skip?: number; take?: number }
pagination: { order?: Record<string, string>; skip: number; take?: number }
}
/**
* An object containing the fields that are filterable e.g `{ id: Any<String> }`

View File

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

View File

@@ -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<TEntity extends BaseEntity>(
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

View File

@@ -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<BaseOrderLineItem, "variant" | "product"> {
variant?: AdminProductVariant

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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<HttpTypes.AdminOrderItemsFilters>,
res: MedusaResponse<HttpTypes.AdminOrderLineItemsListResponse>
) => {
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),
})
}

View File

@@ -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<AdminGetOrdersOrderParamsType>,
res: MedusaResponse<HttpTypes.AdminOrderResponse>
) => {
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,
},
})

View File

@@ -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",

View File

@@ -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,
}

View File

@@ -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.
*/

View File

@@ -32,7 +32,7 @@ declare global {
fields: string[]
pagination: {
order?: Record<string, string>
skip?: number
skip: number
take?: number
}
}

View File

@@ -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<IOrderModuleService>({
moduleName: Modules.ORDER,
@@ -18,6 +18,7 @@ moduleIntegrationTestRunner<IOrderModuleService>({
"orderChange",
"orderClaim",
"orderExchange",
"orderItem",
"orderLineItem",
"orderShippingMethod",
"orderTransaction",
@@ -89,6 +90,15 @@ moduleIntegrationTestRunner<IOrderModuleService>({
field: "orderExchange",
},
},
orderItem: {
id: {
linkable: "order_item_id",
entity: "OrderItem",
primaryKey: "id",
serviceName: "order",
field: "orderItem",
},
},
orderLineItem: {
id: {
linkable: "order_line_item_id",

View File

@@ -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,