feat: Align the product module HTTP API to follow our conventions (#6759)

This commit is contained in:
Stevche Radevski
2024-03-20 16:18:13 +01:00
committed by GitHub
parent 4974f5e455
commit 70859397c0
15 changed files with 313 additions and 267 deletions
@@ -7,9 +7,9 @@ import {
updateProductOptionsWorkflow,
} from "@medusajs/core-flows"
import { UpdateProductDTO } from "@medusajs/types"
import { remoteQueryObjectFromString } from "@medusajs/utils"
import { UpdateProductOptionDTO } from "../../../../../../../../types/dist"
import { refetchProduct, remapProduct } from "../../../helpers"
export const GET = async (
req: AuthenticatedMedusaRequest,
@@ -51,7 +51,12 @@ export const POST = async (
throw errors[0].error
}
res.status(200).json({ product_option: result[0] })
const product = await refetchProduct(
productId,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ product: remapProduct(product) })
}
export const DELETE = async (
@@ -71,9 +76,16 @@ export const DELETE = async (
throw errors[0].error
}
const product = await refetchProduct(
productId,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({
id: optionId,
object: "product_option",
deleted: true,
parent: product,
})
}
@@ -6,6 +6,7 @@ import {
import { CreateProductOptionDTO } from "@medusajs/types"
import { createProductOptionsWorkflow } from "@medusajs/core-flows"
import { remoteQueryObjectFromString } from "@medusajs/utils"
import { refetchProduct, remapProduct } from "../../helpers"
export const GET = async (
req: AuthenticatedMedusaRequest,
@@ -56,5 +57,10 @@ export const POST = async (
throw errors[0].error
}
res.status(200).json({ product_option: result[0] })
const product = await refetchProduct(
productId,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ product: remapProduct(product) })
}
@@ -9,7 +9,7 @@ import {
import { UpdateProductDTO } from "@medusajs/types"
import { remoteQueryObjectFromString } from "@medusajs/utils"
import { remapKeysForProduct, remapProduct } from "../helpers"
import { refetchProduct, remapKeysForProduct, remapProduct } from "../helpers"
export const GET = async (
req: AuthenticatedMedusaRequest,
@@ -47,7 +47,12 @@ export const POST = async (
throw errors[0].error
}
res.status(200).json({ product: remapProduct(result[0]) })
const product = await refetchProduct(
result[0].id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ product: remapProduct(product) })
}
export const DELETE = async (
@@ -8,9 +8,13 @@ import {
} from "@medusajs/core-flows"
import { UpdateProductVariantDTO } from "@medusajs/types"
import { defaultAdminProductsVariantFields } from "../../../query-config"
import { remoteQueryObjectFromString } from "@medusajs/utils"
import { remapKeysForVariant, remapVariant } from "../../../helpers"
import {
refetchProduct,
remapKeysForVariant,
remapProduct,
remapVariant,
} from "../../../helpers"
export const GET = async (
req: AuthenticatedMedusaRequest,
@@ -56,7 +60,12 @@ export const POST = async (
throw errors[0].error
}
res.status(200).json({ variant: remapVariant(result[0]) })
const product = await refetchProduct(
productId,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ product: remapProduct(product) })
}
export const DELETE = async (
@@ -77,9 +86,16 @@ export const DELETE = async (
throw errors[0].error
}
const product = await refetchProduct(
productId,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({
id: variantId,
object: "variant",
deleted: true,
parent: product,
})
}
@@ -7,7 +7,7 @@ import { CreateProductVariantDTO } from "@medusajs/types"
import { createProductVariantsWorkflow } from "@medusajs/core-flows"
import { remoteQueryObjectFromString } from "@medusajs/utils"
import {
remapKeysForProduct,
refetchProduct,
remapKeysForVariant,
remapProduct,
remapVariant,
@@ -64,15 +64,10 @@ export const POST = async (
throw errors[0].error
}
const remoteQuery = req.scope.resolve("remoteQuery")
const queryObject = remoteQueryObjectFromString({
entryPoint: "product",
variables: {
filters: { id: productId },
},
fields: remapKeysForProduct(req.remoteQueryConfig.fields ?? []),
})
const products = await remoteQuery(queryObject)
res.status(200).json({ product: remapProduct(products[0]) })
const product = await refetchProduct(
productId,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ product: remapProduct(product) })
}
@@ -1,14 +1,21 @@
import { ProductDTO, ProductVariantDTO } from "@medusajs/types"
import { MedusaContainer, ProductDTO, ProductVariantDTO } from "@medusajs/types"
import { remoteQueryObjectFromString } from "@medusajs/utils"
const isPricing = (fieldName: string) =>
fieldName.startsWith("variants.prices") ||
fieldName.startsWith("*variants.prices") ||
fieldName.startsWith("prices") ||
fieldName.startsWith("*prices")
// The variant had prices before, but that is not part of the price_set money amounts. Do we remap the request and response or not?
export const remapKeysForProduct = (selectFields: string[]) => {
const productFields = selectFields.filter(
(fieldName: string) => !fieldName.startsWith("variants.prices")
(fieldName: string) => !isPricing(fieldName)
)
const pricingFields = selectFields
.filter((fieldName: string) => fieldName.startsWith("variants.prices"))
.filter((fieldName: string) => isPricing(fieldName))
.map((fieldName: string) =>
fieldName.replace("variants.prices.", "variants.price_set.money_amounts.")
fieldName.replace("variants.prices", "variants.price_set.money_amounts")
)
return [...productFields, ...pricingFields]
@@ -16,12 +23,12 @@ export const remapKeysForProduct = (selectFields: string[]) => {
export const remapKeysForVariant = (selectFields: string[]) => {
const variantFields = selectFields.filter(
(fieldName: string) => !fieldName.startsWith("prices")
(fieldName: string) => !isPricing(fieldName)
)
const pricingFields = selectFields
.filter((fieldName: string) => fieldName.startsWith("prices"))
.filter((fieldName: string) => isPricing(fieldName))
.map((fieldName: string) =>
fieldName.replace("prices.", "price_set.money_amounts.")
fieldName.replace("prices", "price_set.money_amounts")
)
return [...variantFields, ...pricingFields]
@@ -44,3 +51,21 @@ export const remapVariant = (v: ProductVariantDTO) => {
price_set: undefined,
}
}
export const refetchProduct = async (
productId: string,
scope: MedusaContainer,
fields: string[]
) => {
const remoteQuery = scope.resolve("remoteQuery")
const queryObject = remoteQueryObjectFromString({
entryPoint: "product",
variables: {
filters: { id: productId },
},
fields: remapKeysForProduct(fields ?? []),
})
const products = await remoteQuery(queryObject)
return products[0]
}
@@ -29,7 +29,7 @@ export const adminProductRoutesMiddlewares: MiddlewareRoute[] = [
middlewares: [
transformQuery(
AdminGetProductsParams,
QueryConfig.listTransformQueryConfig
QueryConfig.listProductQueryConfig
),
],
},
@@ -39,24 +39,41 @@ export const adminProductRoutesMiddlewares: MiddlewareRoute[] = [
middlewares: [
transformQuery(
AdminGetProductsProductParams,
QueryConfig.retrieveTransformQueryConfig
QueryConfig.retrieveProductQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/admin/products",
middlewares: [transformBody(AdminPostProductsReq)],
middlewares: [
transformBody(AdminPostProductsReq),
transformQuery(
AdminGetProductsProductParams,
QueryConfig.retrieveProductQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/admin/products/:id",
middlewares: [transformBody(AdminPostProductsProductReq)],
middlewares: [
transformBody(AdminPostProductsProductReq),
transformQuery(
AdminGetProductsProductParams,
QueryConfig.retrieveProductQueryConfig
),
],
},
{
method: ["DELETE"],
matcher: "/admin/products/:id",
middlewares: [],
middlewares: [
transformQuery(
AdminGetProductsProductParams,
QueryConfig.retrieveProductQueryConfig
),
],
},
{
@@ -85,22 +102,32 @@ export const adminProductRoutesMiddlewares: MiddlewareRoute[] = [
matcher: "/admin/products/:id/variants",
middlewares: [
transformBody(AdminPostProductsProductVariantsReq),
// We specify the product here as that's what we return after updating the variant
transformQuery(
AdminGetProductsProductParams,
QueryConfig.retrieveTransformQueryConfig
QueryConfig.retrieveProductQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/admin/products/:id/variants/:variant_id",
middlewares: [transformBody(AdminPostProductsProductVariantsVariantReq)],
middlewares: [
transformBody(AdminPostProductsProductVariantsVariantReq),
transformQuery(
AdminGetProductsProductParams,
QueryConfig.retrieveProductQueryConfig
),
],
},
{
method: ["DELETE"],
matcher: "/admin/products/:id/variants/:variant_id",
middlewares: [],
middlewares: [
transformQuery(
AdminGetProductsProductParams,
QueryConfig.retrieveProductQueryConfig
),
],
},
// Note: New endpoint in v2
@@ -128,16 +155,33 @@ export const adminProductRoutesMiddlewares: MiddlewareRoute[] = [
{
method: ["POST"],
matcher: "/admin/products/:id/options",
middlewares: [transformBody(AdminPostProductsProductOptionsReq)],
middlewares: [
transformBody(AdminPostProductsProductOptionsReq),
transformQuery(
AdminGetProductsProductParams,
QueryConfig.retrieveProductQueryConfig
),
],
},
{
method: ["POST"],
matcher: "/admin/products/:id/options/:option_id",
middlewares: [transformBody(AdminPostProductsProductOptionsOptionReq)],
middlewares: [
transformBody(AdminPostProductsProductOptionsOptionReq),
transformQuery(
AdminGetProductsProductParams,
QueryConfig.retrieveProductQueryConfig
),
],
},
{
method: ["DELETE"],
matcher: "/admin/products/:id/options/:option_id",
middlewares: [],
middlewares: [
transformQuery(
AdminGetProductsProductParams,
QueryConfig.retrieveProductQueryConfig
),
],
},
]
@@ -22,14 +22,8 @@ export const defaultAdminProductsVariantFields = [
"ean",
"upc",
"barcode",
"prices.id",
"prices.currency_code",
"prices.amount",
"prices.created_at",
"prices.updated_at",
"options.id",
"options.option_value.value",
"options.option_value.option.title",
"*prices",
"*options",
]
export const retrieveVariantConfig = {
@@ -58,34 +52,6 @@ export const listOptionConfig = {
isList: true,
}
/* export const allowedAdminProductRelations = [
"variants",
// "variants.prices",
"variants.options",
"images",
// TODO: What is this?
// "profiles",
"options",
"options.values",
"tags",
"type",
"collection",
]*/
// TODO: This is what we had in the v1 list. Do we still want to expand that much by default? Also this doesn't work in v2 it seems.
/* export const defaultAdminProductRelations = [
"variants",
// "variants.prices",
// "variants.options",
// "profiles",
"images",
"options",
// "options.values",
"tags",
"type",
"collection",
]*/
export const defaultAdminProductFields = [
"id",
"title",
@@ -111,46 +77,24 @@ export const defaultAdminProductFields = [
"updated_at",
"deleted_at",
"metadata",
"type.id",
"type.value",
"type.metadata",
"type.created_at",
"type.updated_at",
"type.deleted_at",
"collection.id",
"collection.title",
"collection.handle",
"collection.created_at",
"collection.updated_at",
"options.id",
"options.product_id",
"options.title",
"options.values.id",
"options.values.value",
"options.created_at",
"options.updated_at",
"options.deleted_at",
"tags.id",
"tags.value",
"tags.created_at",
"tags.updated_at",
"images.id",
"images.url",
"images.metadata",
"images.created_at",
"images.updated_at",
"images.deleted_at",
// TODO: Until we support wildcards we have to do something like this.
...defaultAdminProductsVariantFields.map((f) => `variants.${f}`),
"*type",
"*collection",
"*options",
"*options.values",
"*tags",
"*images",
"*variants",
"*variants.prices",
"*variants.options",
]
export const retrieveTransformQueryConfig = {
export const retrieveProductQueryConfig = {
defaults: defaultAdminProductFields,
isList: false,
}
export const listTransformQueryConfig = {
...retrieveTransformQueryConfig,
export const listProductQueryConfig = {
...retrieveProductQueryConfig,
defaultLimit: 50,
isList: true,
}
@@ -11,7 +11,7 @@ import {
} from "../../../types/routing"
import { listPriceLists } from "../price-lists/queries"
import { AdminGetProductsParams } from "./validators"
import { remapKeysForProduct, remapProduct } from "./helpers"
import { refetchProduct, remapKeysForProduct, remapProduct } from "./helpers"
import { MedusaContainer } from "medusa-core-utils"
const applyVariantFiltersForPriceList = async (
@@ -103,5 +103,10 @@ export const POST = async (
throw errors[0].error
}
res.status(200).json({ product: remapProduct(result[0]) })
const product = await refetchProduct(
result[0].id,
req.scope,
req.remoteQueryConfig.fields
)
res.status(200).json({ product: remapProduct(product) })
}
@@ -11,7 +11,6 @@ import {
IsOptional,
IsString,
NotEquals,
Validate,
ValidateIf,
ValidateNested,
} from "class-validator"
@@ -19,7 +18,6 @@ import { FindParams, extendedFindParamsMixin } from "../../../types/common"
import { OperatorMapValidator } from "../../../types/validators/operator-map"
import { IsType } from "../../../utils"
import { optionalBooleanMapper } from "../../../utils/validators/is-boolean"
import { XorConstraint } from "../../../types/validators/xor"
export class AdminGetProductsProductParams extends FindParams {}
export class AdminGetProductsProductVariantsVariantParams extends FindParams {}