feature: add workflows hooks to the order module (#8496)

This commit is contained in:
Harminder Virk
2024-08-08 16:25:11 +05:30
committed by GitHub
parent 3259b9204c
commit 08764e3965
14 changed files with 163 additions and 71 deletions
@@ -1,4 +1,5 @@
import { import {
AdditionalData,
BigNumberInput, BigNumberInput,
FulfillmentDTO, FulfillmentDTO,
OrderDTO, OrderDTO,
@@ -7,6 +8,8 @@ import {
import { MedusaError, Modules } from "@medusajs/utils" import { MedusaError, Modules } from "@medusajs/utils"
import { import {
WorkflowData, WorkflowData,
WorkflowResponse,
createHook,
createStep, createStep,
createWorkflow, createWorkflow,
parallelize, parallelize,
@@ -109,8 +112,10 @@ export const cancelOrderFulfillmentWorkflowId = "cancel-order-fulfillment"
export const cancelOrderFulfillmentWorkflow = createWorkflow( export const cancelOrderFulfillmentWorkflow = createWorkflow(
cancelOrderFulfillmentWorkflowId, cancelOrderFulfillmentWorkflowId,
( (
input: WorkflowData<OrderWorkflow.CancelOrderFulfillmentWorkflowInput> input: WorkflowData<
): WorkflowData<void> => { OrderWorkflow.CancelOrderFulfillmentWorkflowInput & AdditionalData
>
) => {
const order: OrderDTO & { fulfillments: FulfillmentDTO[] } = const order: OrderDTO & { fulfillments: FulfillmentDTO[] } =
useRemoteQueryStep({ useRemoteQueryStep({
entry_point: "orders", entry_point: "orders",
@@ -153,5 +158,14 @@ export const cancelOrderFulfillmentWorkflow = createWorkflow(
id: input.fulfillment_id, id: input.fulfillment_id,
}, },
}) })
const orderFulfillmentCanceled = createHook("orderFulfillmentCanceled", {
fulfillment,
additional_data: input.additional_data,
})
return new WorkflowResponse(void 0, {
hooks: [orderFulfillmentCanceled],
})
} }
) )
@@ -7,6 +7,8 @@ import {
import { MedusaError, deepFlatMap } from "@medusajs/utils" import { MedusaError, deepFlatMap } from "@medusajs/utils"
import { import {
WorkflowData, WorkflowData,
WorkflowResponse,
createHook,
createStep, createStep,
createWorkflow, createWorkflow,
parallelize, parallelize,
@@ -77,9 +79,7 @@ const validateOrder = createStep(
export const cancelOrderWorkflowId = "cancel-order" export const cancelOrderWorkflowId = "cancel-order"
export const cancelOrderWorkflow = createWorkflow( export const cancelOrderWorkflow = createWorkflow(
cancelOrderWorkflowId, cancelOrderWorkflowId,
( (input: WorkflowData<OrderWorkflow.CancelOrderWorkflowInput>) => {
input: WorkflowData<OrderWorkflow.CancelOrderWorkflowInput>
): WorkflowData<void> => {
const order: OrderDTO & { fulfillments: FulfillmentDTO[] } = const order: OrderDTO & { fulfillments: FulfillmentDTO[] } =
useRemoteQueryStep({ useRemoteQueryStep({
entry_point: "orders", entry_point: "orders",
@@ -118,5 +118,13 @@ export const cancelOrderWorkflow = createWorkflow(
cancelPaymentStep({ paymentIds }), cancelPaymentStep({ paymentIds }),
cancelOrdersStep({ orderIds: [order.id] }) cancelOrdersStep({ orderIds: [order.id] })
) )
const orderCanceled = createHook("orderCanceled", {
order,
})
return new WorkflowResponse(void 0, {
hooks: [orderCanceled],
})
} }
) )
@@ -1,21 +1,28 @@
import { OrderDTO } from "@medusajs/types"
import { import {
WorkflowData, WorkflowData,
WorkflowResponse, WorkflowResponse,
createHook,
createWorkflow, createWorkflow,
} from "@medusajs/workflows-sdk" } from "@medusajs/workflows-sdk"
import { completeOrdersStep } from "../steps" import { completeOrdersStep } from "../steps"
import { AdditionalData } from "@medusajs/types"
type CompleteOrdersStepInput = { type CompleteOrdersStepInput = {
orderIds: string[] orderIds: string[]
} } & AdditionalData
export const completeOrderWorkflowId = "complete-order-workflow" export const completeOrderWorkflowId = "complete-order-workflow"
export const completeOrderWorkflow = createWorkflow( export const completeOrderWorkflow = createWorkflow(
completeOrderWorkflowId, completeOrderWorkflowId,
( (input: WorkflowData<CompleteOrdersStepInput>) => {
input: WorkflowData<CompleteOrdersStepInput> const completedOrders = completeOrdersStep(input)
): WorkflowResponse<OrderDTO[]> => { const ordersCompleted = createHook("ordersCompleted", {
return new WorkflowResponse(completeOrdersStep(input)) orders: completedOrders,
additional_data: input.additional_data,
})
return new WorkflowResponse(completedOrders, {
hooks: [ordersCompleted],
})
} }
) )
@@ -1,6 +1,6 @@
import { import {
AdditionalData,
BigNumberInput, BigNumberInput,
FulfillmentDTO,
FulfillmentWorkflow, FulfillmentWorkflow,
OrderDTO, OrderDTO,
OrderLineItemDTO, OrderLineItemDTO,
@@ -11,6 +11,7 @@ import { MathBN, MedusaError, Modules } from "@medusajs/utils"
import { import {
WorkflowData, WorkflowData,
WorkflowResponse, WorkflowResponse,
createHook,
createStep, createStep,
createWorkflow, createWorkflow,
parallelize, parallelize,
@@ -204,8 +205,10 @@ export const createOrderFulfillmentWorkflowId = "create-order-fulfillment"
export const createOrderFulfillmentWorkflow = createWorkflow( export const createOrderFulfillmentWorkflow = createWorkflow(
createOrderFulfillmentWorkflowId, createOrderFulfillmentWorkflowId,
( (
input: WorkflowData<OrderWorkflow.CreateOrderFulfillmentWorkflowInput> input: WorkflowData<
): WorkflowResponse<FulfillmentDTO> => { OrderWorkflow.CreateOrderFulfillmentWorkflowInput & AdditionalData
>
) => {
const order: OrderDTO = useRemoteQueryStep({ const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders", entry_point: "orders",
fields: [ fields: [
@@ -323,7 +326,14 @@ export const createOrderFulfillmentWorkflow = createWorkflow(
deleteReservationsStep(toDelete) deleteReservationsStep(toDelete)
) )
const fulfillmentCreated = createHook("fulfillmentCreated", {
fulfillment,
additional_data: input.additional_data,
})
// trigger event OrderModuleService.Events.FULFILLMENT_CREATED // trigger event OrderModuleService.Events.FULFILLMENT_CREATED
return new WorkflowResponse(fulfillment) return new WorkflowResponse(fulfillment, {
hooks: [fulfillmentCreated],
})
} }
) )
@@ -1,8 +1,9 @@
import { CreateOrderDTO, OrderDTO } from "@medusajs/types" import { AdditionalData, CreateOrderDTO } from "@medusajs/types"
import { MathBN, MedusaError, isPresent } from "@medusajs/utils" import { MathBN, MedusaError, isPresent } from "@medusajs/utils"
import { import {
WorkflowData, WorkflowData,
WorkflowResponse, WorkflowResponse,
createHook,
createWorkflow, createWorkflow,
parallelize, parallelize,
transform, transform,
@@ -85,7 +86,7 @@ function getOrderInput(data) {
export const createOrdersWorkflowId = "create-orders" export const createOrdersWorkflowId = "create-orders"
export const createOrdersWorkflow = createWorkflow( export const createOrdersWorkflow = createWorkflow(
createOrdersWorkflowId, createOrdersWorkflowId,
(input: WorkflowData<CreateOrderDTO>): WorkflowResponse<OrderDTO> => { (input: WorkflowData<CreateOrderDTO & AdditionalData>) => {
const variantIds = transform({ input }, (data) => { const variantIds = transform({ input }, (data) => {
return (data.input.items ?? []) return (data.input.items ?? [])
.map((item) => item.variant_id) .map((item) => item.variant_id)
@@ -176,7 +177,13 @@ export const createOrdersWorkflow = createWorkflow(
*/ */
updateOrderTaxLinesStep({ order_id: order.id }) updateOrderTaxLinesStep({ order_id: order.id })
const orderCreated = createHook("orderCreated", {
order,
additional_data: input.additional_data,
})
return new WorkflowResponse(order) return new WorkflowResponse(order, {
hooks: [orderCreated],
})
} }
) )
@@ -1,7 +1,14 @@
import { FulfillmentDTO, OrderDTO, OrderWorkflow } from "@medusajs/types" import {
AdditionalData,
FulfillmentDTO,
OrderDTO,
OrderWorkflow,
} from "@medusajs/types"
import { FulfillmentEvents, Modules } from "@medusajs/utils" import { FulfillmentEvents, Modules } from "@medusajs/utils"
import { import {
WorkflowData, WorkflowData,
WorkflowResponse,
createHook,
createStep, createStep,
createWorkflow, createWorkflow,
parallelize, parallelize,
@@ -70,8 +77,10 @@ export const createOrderShipmentWorkflowId = "create-order-shipment"
export const createOrderShipmentWorkflow = createWorkflow( export const createOrderShipmentWorkflow = createWorkflow(
createOrderShipmentWorkflowId, createOrderShipmentWorkflowId,
( (
input: WorkflowData<OrderWorkflow.CreateOrderShipmentWorkflowInput> input: WorkflowData<
): WorkflowData<void> => { OrderWorkflow.CreateOrderShipmentWorkflowInput & AdditionalData
>
) => {
const order: OrderDTO = useRemoteQueryStep({ const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders", entry_point: "orders",
fields: [ fields: [
@@ -112,5 +121,14 @@ export const createOrderShipmentWorkflow = createWorkflow(
eventName: FulfillmentEvents.SHIPMENT_CREATED, eventName: FulfillmentEvents.SHIPMENT_CREATED,
data: { id: shipment.id }, data: { id: shipment.id },
}) })
const shipmentCreated = createHook("shipmentCreated", {
shipment,
additional_data: input.additional_data,
})
return new WorkflowResponse(void 0, {
hooks: [shipmentCreated],
})
} }
) )
@@ -11,7 +11,7 @@ import {
} from "../../../types/routing" } from "../../../types/routing"
import { AdminCreateDraftOrderType } from "./validators" import { AdminCreateDraftOrderType } from "./validators"
import { refetchOrder } from "./helpers" import { refetchOrder } from "./helpers"
import { CreateOrderDTO } from "@medusajs/types" import { AdditionalData, CreateOrderDTO } from "@medusajs/types"
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 remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
@@ -39,7 +39,7 @@ export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
} }
export const POST = async ( export const POST = async (
req: AuthenticatedMedusaRequest<AdminCreateDraftOrderType>, req: AuthenticatedMedusaRequest<AdminCreateDraftOrderType & AdditionalData>,
res: MedusaResponse res: MedusaResponse
) => { ) => {
const input = req.validatedBody const input = req.validatedBody
@@ -48,7 +48,7 @@ export const POST = async (
no_notification: !!input.no_notification_order, no_notification: !!input.no_notification_order,
status: OrderStatus.DRAFT, status: OrderStatus.DRAFT,
is_draft_order: true, is_draft_order: true,
} as CreateOrderDTO } as CreateOrderDTO & AdditionalData
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
@@ -1,6 +1,10 @@
import { z, ZodObject } from "zod" import { z } from "zod"
import { AddressPayload, BigNumberInput } from "../../utils/common-validators" import { AddressPayload, BigNumberInput } from "../../utils/common-validators"
import { createFindParams, createSelectParams } from "../../utils/validators" import {
createFindParams,
createSelectParams,
WithAdditionalData,
} from "../../utils/validators"
export type AdminGetOrderParamsType = z.infer<typeof AdminGetOrderParams> export type AdminGetOrderParamsType = z.infer<typeof AdminGetOrderParams>
export const AdminGetOrderParams = createSelectParams() export const AdminGetOrderParams = createSelectParams()
@@ -49,8 +53,8 @@ const Item = z
return true return true
}) })
export type AdminCreateDraftOrderType = z.infer<typeof _AdminCreateDraftOrder> export type AdminCreateDraftOrderType = z.infer<typeof CreateDraftOrder>
const _AdminCreateDraftOrder = z const CreateDraftOrder = z
.object({ .object({
status: z.nativeEnum(Status).optional(), status: z.nativeEnum(Status).optional(),
sales_channel_id: z.string().nullish(), sales_channel_id: z.string().nullish(),
@@ -68,19 +72,18 @@ const _AdminCreateDraftOrder = z
}) })
.strict() .strict()
export const AdminCreateDraftOrder = (customSchema?: ZodObject<any, any>) => { export const AdminCreateDraftOrder = WithAdditionalData(
const schema = customSchema CreateDraftOrder,
? _AdminCreateDraftOrder.merge(customSchema) (schema) => {
: _AdminCreateDraftOrder return schema.refine(
(data) => {
if (!data.email && !data.customer_id) {
return false
}
return schema.refine( return true
(data) => { },
if (!data.email && !data.customer_id) { { message: "Either email or customer_id must be provided" }
return false )
} }
)
return true
},
{ message: "Either email or customer_id must be provided" }
)
}
@@ -1,3 +1,4 @@
import { AdditionalData } from "@medusajs/types"
import { completeOrderWorkflow } from "@medusajs/core-flows" import { completeOrderWorkflow } from "@medusajs/core-flows"
import { import {
ContainerRegistrationKeys, ContainerRegistrationKeys,
@@ -10,14 +11,17 @@ import {
import { AdminCompleteOrderType } from "../../validators" import { AdminCompleteOrderType } from "../../validators"
export const POST = async ( export const POST = async (
req: AuthenticatedMedusaRequest<AdminCompleteOrderType>, req: AuthenticatedMedusaRequest<AdminCompleteOrderType & AdditionalData>,
res: MedusaResponse res: MedusaResponse
) => { ) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const { id } = req.params const { id } = req.params
await completeOrderWorkflow(req.scope).run({ await completeOrderWorkflow(req.scope).run({
input: { orderIds: [req.validatedBody.order_id] }, input: {
orderIds: [req.validatedBody.order_id],
additional_data: req.validatedBody.additional_data,
},
}) })
const queryObject = remoteQueryObjectFromString({ const queryObject = remoteQueryObjectFromString({
@@ -8,9 +8,12 @@ import {
MedusaResponse, MedusaResponse,
} from "../../../../../../../types/routing" } from "../../../../../../../types/routing"
import { AdminOrderCancelFulfillmentType } from "../../../../validators" import { AdminOrderCancelFulfillmentType } from "../../../../validators"
import { AdditionalData } from "@medusajs/types"
export const POST = async ( export const POST = async (
req: AuthenticatedMedusaRequest<AdminOrderCancelFulfillmentType>, req: AuthenticatedMedusaRequest<
AdminOrderCancelFulfillmentType & AdditionalData
>,
res: MedusaResponse res: MedusaResponse
) => { ) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
@@ -8,9 +8,12 @@ import {
MedusaResponse, MedusaResponse,
} from "../../../../../../../types/routing" } from "../../../../../../../types/routing"
import { AdminOrderCreateShipmentType } from "../../../../validators" import { AdminOrderCreateShipmentType } from "../../../../validators"
import { AdditionalData } from "@medusajs/types"
export const POST = async ( export const POST = async (
req: AuthenticatedMedusaRequest<AdminOrderCreateShipmentType>, req: AuthenticatedMedusaRequest<
AdminOrderCreateShipmentType & AdditionalData
>,
res: MedusaResponse res: MedusaResponse
) => { ) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
@@ -8,9 +8,12 @@ import {
MedusaResponse, MedusaResponse,
} from "../../../../../types/routing" } from "../../../../../types/routing"
import { AdminOrderCreateFulfillmentType } from "../../validators" import { AdminOrderCreateFulfillmentType } from "../../validators"
import { AdditionalData } from "@medusajs/types"
export const POST = async ( export const POST = async (
req: AuthenticatedMedusaRequest<AdminOrderCreateFulfillmentType>, req: AuthenticatedMedusaRequest<
AdminOrderCreateFulfillmentType & AdditionalData
>,
res: MedusaResponse res: MedusaResponse
) => { ) => {
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
@@ -3,6 +3,7 @@ import {
createFindParams, createFindParams,
createOperatorMap, createOperatorMap,
createSelectParams, createSelectParams,
WithAdditionalData,
} from "../../utils/validators" } from "../../utils/validators"
export const AdminGetOrdersOrderParams = createSelectParams().merge( export const AdminGetOrdersOrderParams = createSelectParams().merge(
@@ -42,26 +43,29 @@ export const AdminArchiveOrder = z.object({
}) })
export type AdminArchiveOrderType = z.infer<typeof AdminArchiveOrder> export type AdminArchiveOrderType = z.infer<typeof AdminArchiveOrder>
export const AdminCompleteOrder = z.object({ export type AdminCompleteOrderType = z.infer<typeof CompleteOrder>
export const CompleteOrder = z.object({
order_id: z.string(), order_id: z.string(),
}) })
export type AdminCompleteOrderType = z.infer<typeof AdminArchiveOrder> export const AdminCompleteOrder = WithAdditionalData(CompleteOrder)
const Item = z.object({ const Item = z.object({
id: z.string(), id: z.string(),
quantity: z.number(), quantity: z.number(),
}) })
export const AdminOrderCreateFulfillment = z.object({ export type AdminOrderCreateFulfillmentType = z.infer<
typeof OrderCreateFulfillment
>
export const OrderCreateFulfillment = z.object({
items: z.array(Item), items: z.array(Item),
location_id: z.string().nullish(), location_id: z.string().nullish(),
no_notification: z.boolean().optional(), no_notification: z.boolean().optional(),
metadata: z.record(z.unknown()).nullish(), metadata: z.record(z.unknown()).nullish(),
}) })
export const AdminOrderCreateFulfillment = WithAdditionalData(
export type AdminOrderCreateFulfillmentType = z.infer< OrderCreateFulfillment
typeof AdminOrderCreateFulfillment )
>
const Label = z.object({ const Label = z.object({
tracking_number: z.string(), tracking_number: z.string(),
@@ -69,21 +73,21 @@ const Label = z.object({
label_url: z.string(), label_url: z.string(),
}) })
export const AdminOrderCreateShipment = z.object({ export type AdminOrderCreateShipmentType = z.infer<typeof OrderCreateShipment>
export const OrderCreateShipment = z.object({
items: z.array(Item), items: z.array(Item),
labels: z.array(Label).optional(), labels: z.array(Label).optional(),
no_notification: z.boolean().optional(), no_notification: z.boolean().optional(),
metadata: z.record(z.unknown()).nullish(), metadata: z.record(z.unknown()).nullish(),
}) })
export const AdminOrderCreateShipment = WithAdditionalData(OrderCreateShipment)
export type AdminOrderCreateShipmentType = z.infer<
typeof AdminOrderCreateShipment
>
export const AdminOrderCancelFulfillment = z.object({
no_notification: z.boolean().optional(),
})
export type AdminOrderCancelFulfillmentType = z.infer< export type AdminOrderCancelFulfillmentType = z.infer<
typeof AdminOrderCancelFulfillment typeof OrderCancelFulfillment
> >
export const OrderCancelFulfillment = z.object({
no_notification: z.boolean().optional(),
})
export const AdminOrderCancelFulfillment = WithAdditionalData(
OrderCancelFulfillment
)
+14 -6
View File
@@ -1,19 +1,27 @@
import { z, ZodObject } from "zod" import { z, ZodEffects, ZodObject } from "zod"
/** /**
* Wraps the original schema to a function to accept and merge * Wraps the original schema to a function to accept and merge
* additional_data schema * additional_data schema
*/ */
export const WithAdditionalData = (originalSchema: ZodObject<any, any>) => { export const WithAdditionalData = <T extends ZodObject<any, any>>(
originalSchema: T,
modifyCallback?: (schema: T) => ZodObject<any, any> | ZodEffects<any, any>
) => {
return (additionalDataValidator?: ZodObject<any, any>) => { return (additionalDataValidator?: ZodObject<any, any>) => {
let schema: ZodObject<any, any>
if (!additionalDataValidator) { if (!additionalDataValidator) {
return originalSchema.extend({ schema = originalSchema.extend({
additional_data: z.record(z.unknown()).nullish(), additional_data: z.record(z.unknown()).nullish(),
}) })
} else {
schema = originalSchema.extend({
additional_data: additionalDataValidator,
})
} }
return originalSchema.extend({
additional_data: additionalDataValidator, return modifyCallback ? modifyCallback(schema as T) : schema
})
} }
} }