feat(core-flows,modules-sdk,types,medusa,link-modules): adds variant <> inventory item link endpoints (#7576)

what:

- adds variant inventory link management endpoints:
```
Link inventory item to variant
POST /products/:id/variants/:vid/inventory-items

Update variant's inventory item link
POST /products/:id/variants/:vid/inventory-items/:iid

Unlink variant's inventory item
DELETE /products/:id/variants/:vid/inventory-items/:iid
```

- a batch endpoint that does the above 3 across variants
```
POST /products/:id/variants/inventory-items
```
This commit is contained in:
Riqwan Thamir authored and GitHub committed 2024-06-03 18:23:29 +00:00
1 parent 122186a78d
commit ecfbfcc707
23 files changed
+1279 -126

No files matched your search

@@ -0,0 +1,66 @@
import { dismissLinksWorkflow, updateLinksWorkflow } from "@medusajs/core-flows"
import { Modules } from "@medusajs/utils"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../../../../types/routing"
import { refetchVariant } from "../../../../../helpers"
import { AdminUpdateVariantInventoryItemType } from "../../../../../validators"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminUpdateVariantInventoryItemType>,
res: MedusaResponse
) => {
const variantId = req.params.variant_id
const inventoryItemId = req.params.inventory_item_id
await updateLinksWorkflow(req.scope).run({
input: [
{
[Modules.PRODUCT]: { variant_id: variantId },
[Modules.INVENTORY]: { inventory_item_id: inventoryItemId },
data: { required_quantity: req.validatedBody.required_quantity },
},
],
})
const variant = await refetchVariant(
variantId,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ variant })
}
export const DELETE = async (
req: AuthenticatedMedusaRequest,
res: MedusaResponse
) => {
const variantId = req.params.variant_id
const inventoryItemId = req.params.inventory_item_id
const {
result: [deleted],
} = await dismissLinksWorkflow(req.scope).run({
input: [
{
[Modules.PRODUCT]: { variant_id: variantId },
[Modules.INVENTORY]: { inventory_item_id: inventoryItemId },
},
],
})
const parent = await refetchVariant(
variantId,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({
id: deleted,
object: "variant-inventory-item-link",
deleted: true,
parent,
})
}
@@ -0,0 +1,35 @@
import { createLinksWorkflow } from "@medusajs/core-flows"
import { Modules } from "@medusajs/utils"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../../../types/routing"
import { refetchVariant } from "../../../../helpers"
import { AdminCreateVariantInventoryItemType } from "../../../../validators"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminCreateVariantInventoryItemType>,
res: MedusaResponse
) => {
const variantId = req.params.variant_id
await createLinksWorkflow(req.scope).run({
input: [
{
[Modules.PRODUCT]: { variant_id: variantId },
[Modules.INVENTORY]: {
inventory_item_id: req.validatedBody.inventory_item_id,
},
data: { required_quantity: req.validatedBody.required_quantity },
},
],
})
const variant = await refetchVariant(
variantId,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ variant })
}
@@ -0,0 +1,28 @@
import { batchLinksWorkflow } from "@medusajs/core-flows"
import {
AuthenticatedMedusaRequest,
MedusaResponse,
} from "../../../../../../../types/routing"
import { buildBatchVariantInventoryData } from "../../../../helpers"
import { AdminBatchVariantInventoryItemsType } from "../../../../validators"
export const POST = async (
req: AuthenticatedMedusaRequest<AdminBatchVariantInventoryItemsType>,
res: MedusaResponse
) => {
const { create = [], update = [], delete: toDelete = [] } = req.validatedBody
const { result } = await batchLinksWorkflow(req.scope).run({
input: {
create: buildBatchVariantInventoryData(create),
update: buildBatchVariantInventoryData(update),
delete: buildBatchVariantInventoryData(toDelete),
},
})
res.status(200).json({
created: result.created,
updated: result.updated,
deleted: result.deleted,
})
}
@@ -1,3 +1,4 @@
import { LinkDefinition } from "@medusajs/modules-sdk"
import {
BatchMethodResponse,
MedusaContainer,
@@ -5,10 +6,12 @@ import {
ProductVariantDTO,
} from "@medusajs/types"
import {
ContainerRegistrationKeys,
Modules,
promiseAll,
remoteQueryObjectFromString,
ContainerRegistrationKeys,
} from "@medusajs/utils"
import { AdminBatchVariantInventoryItemsType } from "./validators"
const isPricing = (fieldName: string) =>
fieldName.startsWith("variants.prices") ||
@@ -89,6 +92,25 @@ export const refetchProduct = async (
return products[0]
}
export const refetchVariant = async (
variantId: string,
scope: MedusaContainer,
fields: string[]
) => {
const remoteQuery = scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
const queryObject = remoteQueryObjectFromString({
entryPoint: "product_variant",
variables: {
filters: { id: variantId },
},
fields: remapKeysForVariant(fields ?? []),
})
const [variant] = await remoteQuery(queryObject)
return remapVariantResponse(variant)
}
export const refetchBatchProducts = async (
batchResult: BatchMethodResponse<ProductDTO>,
scope: MedusaContainer,
@@ -174,3 +196,31 @@ export const refetchBatchVariants = async (
deleted: batchResult.deleted,
}
}
export const buildBatchVariantInventoryData = (
inputs:
| AdminBatchVariantInventoryItemsType["create"]
| AdminBatchVariantInventoryItemsType["update"]
| AdminBatchVariantInventoryItemsType["delete"]
) => {
const results: LinkDefinition[] = []
for (const input of inputs || []) {
const result: LinkDefinition = {
[Modules.PRODUCT]: { variant_id: input.variant_id },
[Modules.INVENTORY]: {
inventory_item_id: input.inventory_item_id,
},
}
if ("required_quantity" in input) {
result.data = {
required_quantity: input.required_quantity,
}
}
results.push(result)
}
return results
}
@@ -7,20 +7,25 @@ import { createBatchBody } from "../../utils/validators"
import * as QueryConfig from "./query-config"
import { maybeApplyPriceListsFilter } from "./utils"
import {
AdminGetProductsParams,
AdminBatchCreateVariantInventoryItem,
AdminBatchDeleteVariantInventoryItem,
AdminBatchUpdateProduct,
AdminBatchUpdateProductVariant,
AdminBatchUpdateVariantInventoryItem,
AdminCreateProduct,
AdminCreateProductOption,
AdminCreateProductVariant,
AdminCreateVariantInventoryItem,
AdminGetProductOptionParams,
AdminGetProductOptionsParams,
AdminGetProductParams,
AdminGetProductsParams,
AdminGetProductVariantParams,
AdminGetProductVariantsParams,
AdminUpdateProduct,
AdminUpdateProductOption,
AdminGetProductParams,
AdminGetProductVariantsParams,
AdminGetProductVariantParams,
AdminUpdateProductVariant,
AdminGetProductOptionsParams,
AdminGetProductOptionParams,
AdminBatchUpdateProduct,
AdminBatchUpdateProductVariant,
AdminUpdateVariantInventoryItem,
} from "./validators"
export const adminProductRoutesMiddlewares: MiddlewareRoute[] = [
@@ -243,4 +248,57 @@ export const adminProductRoutesMiddlewares: MiddlewareRoute[] = [
),
],
},
// Variant inventory item endpoints
{
method: ["POST"],
matcher: "/admin/products/:id/variants/inventory-items/batch",
middlewares: [
validateAndTransformBody(
createBatchBody(
AdminBatchCreateVariantInventoryItem,
AdminBatchUpdateVariantInventoryItem,
AdminBatchDeleteVariantInventoryItem
)
),
validateAndTransformQuery(
AdminGetProductVariantParams,
QueryConfig.retrieveVariantConfig
),
],
},
{
method: ["POST"],
matcher: "/admin/products/:id/variants/:variant_id/inventory-items",
middlewares: [
validateAndTransformBody(AdminCreateVariantInventoryItem),
validateAndTransformQuery(
AdminGetProductVariantParams,
QueryConfig.retrieveVariantConfig
),
],
},
{
method: ["POST"],
matcher:
"/admin/products/:id/variants/:variant_id/inventory-items/:inventory_item_id",
middlewares: [
validateAndTransformBody(AdminUpdateVariantInventoryItem),
validateAndTransformQuery(
AdminGetProductVariantParams,
QueryConfig.retrieveVariantConfig
),
],
},
{
method: ["DELETE"],
matcher:
"/admin/products/:id/variants/:variant_id/inventory-items/:inventory_item_id",
middlewares: [
validateAndTransformQuery(
AdminGetProductVariantParams,
QueryConfig.retrieveVariantConfig
),
],
},
]
@@ -1,3 +1,4 @@
import { BatchMethodRequest } from "@medusajs/types"
import { ProductStatus } from "@medusajs/utils"
import { z } from "zod"
import { GetProductsParams } from "../../utils/common-validators"
@@ -235,3 +236,56 @@ export const AdminBatchUpdateProduct = AdminUpdateProduct.extend({
// @ValidateNested({ each: true })
// @IsArray()
// categories?: ProductProductCategoryReq[]
export const AdminCreateVariantInventoryItem = z.object({
required_quantity: z.number(),
inventory_item_id: z.string(),
})
export type AdminCreateVariantInventoryItemType = z.infer<
typeof AdminCreateVariantInventoryItem
>
export const AdminUpdateVariantInventoryItem = z.object({
required_quantity: z.number(),
})
export type AdminUpdateVariantInventoryItemType = z.infer<
typeof AdminUpdateVariantInventoryItem
>
export const AdminBatchCreateVariantInventoryItem = z
.object({
required_quantity: z.number(),
inventory_item_id: z.string(),
variant_id: z.string(),
})
.strict()
export type AdminBatchCreateVariantInventoryItemType = z.infer<
typeof AdminBatchCreateVariantInventoryItem
>
export const AdminBatchUpdateVariantInventoryItem = z
.object({
required_quantity: z.number(),
inventory_item_id: z.string(),
variant_id: z.string(),
})
.strict()
export type AdminBatchUpdateVariantInventoryItemType = z.infer<
typeof AdminBatchUpdateVariantInventoryItem
>
export const AdminBatchDeleteVariantInventoryItem = z
.object({
inventory_item_id: z.string(),
variant_id: z.string(),
})
.strict()
export type AdminBatchDeleteVariantInventoryItemType = z.infer<
typeof AdminBatchDeleteVariantInventoryItem
>
export type AdminBatchVariantInventoryItemsType = BatchMethodRequest<
AdminBatchCreateVariantInventoryItemType,
AdminBatchUpdateVariantInventoryItemType,
AdminBatchDeleteVariantInventoryItemType
>
+3 -2
View File
@@ -2,12 +2,13 @@ import { z } from "zod"
export const createBatchBody = (
createValidator: z.ZodType,
updateValidator: z.ZodType
updateValidator: z.ZodType,
deleteValidator: z.ZodType = z.string()
) => {
return z.object({
create: z.array(createValidator).optional(),
update: z.array(updateValidator).optional(),
delete: z.array(z.string()).optional(),
delete: z.array(deleteValidator).optional(),
})
}