feat(medusa): Apply query transformers to Collection and CustomerGroups (#1667)

This commit is contained in:
Adrien de Peretti
2022-07-02 13:01:52 +02:00
committed by GitHub
parent fee0f88a62
commit e53c06eab8
21 changed files with 200 additions and 206 deletions

View File

@@ -189,20 +189,20 @@ Object {
"collections": Array [
Object {
"created_at": Any<String>,
"handle": "test-collection",
"id": "test-collection",
"handle": "test-collection2",
"id": "test-collection2",
"products": Array [
Object {
"collection_id": "test-collection",
"collection_id": "test-collection2",
"created_at": Any<String>,
"deleted_at": null,
"description": "test-product-description",
"discountable": true,
"external_id": null,
"handle": "test-product",
"handle": "test-product_filtering_2",
"height": null,
"hs_code": null,
"id": "test-product",
"id": "test-product_filtering_2",
"is_giftcard": false,
"length": null,
"material": null,
@@ -210,44 +210,17 @@ Object {
"mid_code": null,
"origin_country": null,
"profile_id": StringMatching /\\^sp_\\*/,
"status": "draft",
"status": "published",
"subtitle": null,
"thumbnail": null,
"title": "Test product",
"type_id": "test-type",
"updated_at": Any<String>,
"weight": null,
"width": null,
},
Object {
"collection_id": "test-collection",
"created_at": Any<String>,
"deleted_at": null,
"description": "test-product-description1",
"discountable": true,
"external_id": null,
"handle": "test-product1",
"height": null,
"hs_code": null,
"id": "test-product1",
"is_giftcard": false,
"length": null,
"material": null,
"metadata": null,
"mid_code": null,
"origin_country": null,
"profile_id": StringMatching /\\^sp_\\*/,
"status": "draft",
"subtitle": null,
"thumbnail": null,
"title": "Test product1",
"title": "Test product filtering 2",
"type_id": "test-type",
"updated_at": Any<String>,
"weight": null,
"width": null,
},
],
"title": "Test collection",
"title": "Test collection 2",
"updated_at": Any<String>,
},
Object {
@@ -315,20 +288,20 @@ Object {
},
Object {
"created_at": Any<String>,
"handle": "test-collection2",
"id": "test-collection2",
"handle": "test-collection",
"id": "test-collection",
"products": Array [
Object {
"collection_id": "test-collection2",
"collection_id": "test-collection",
"created_at": Any<String>,
"deleted_at": null,
"description": "test-product-description",
"discountable": true,
"external_id": null,
"handle": "test-product_filtering_2",
"handle": "test-product",
"height": null,
"hs_code": null,
"id": "test-product_filtering_2",
"id": "test-product",
"is_giftcard": false,
"length": null,
"material": null,
@@ -336,17 +309,44 @@ Object {
"mid_code": null,
"origin_country": null,
"profile_id": StringMatching /\\^sp_\\*/,
"status": "published",
"status": "draft",
"subtitle": null,
"thumbnail": null,
"title": "Test product filtering 2",
"title": "Test product",
"type_id": "test-type",
"updated_at": Any<String>,
"weight": null,
"width": null,
},
Object {
"collection_id": "test-collection",
"created_at": Any<String>,
"deleted_at": null,
"description": "test-product-description1",
"discountable": true,
"external_id": null,
"handle": "test-product1",
"height": null,
"hs_code": null,
"id": "test-product1",
"is_giftcard": false,
"length": null,
"material": null,
"metadata": null,
"mid_code": null,
"origin_country": null,
"profile_id": StringMatching /\\^sp_\\*/,
"status": "draft",
"subtitle": null,
"thumbnail": null,
"title": "Test product1",
"type_id": "test-type",
"updated_at": Any<String>,
"weight": null,
"width": null,
},
],
"title": "Test collection 2",
"title": "Test collection",
"updated_at": Any<String>,
},
],

View File

@@ -176,20 +176,14 @@ describe("/admin/collections", () => {
expect(response.data).toMatchSnapshot({
collections: [
{
id: "test-collection",
handle: "test-collection",
title: "Test collection",
id: "test-collection2",
handle: "test-collection2",
title: "Test collection 2",
created_at: expect.any(String),
updated_at: expect.any(String),
products: [
{
collection_id: "test-collection",
created_at: expect.any(String),
updated_at: expect.any(String),
profile_id: expect.stringMatching(/^sp_*/),
},
{
collection_id: "test-collection",
collection_id: "test-collection2",
created_at: expect.any(String),
updated_at: expect.any(String),
profile_id: expect.stringMatching(/^sp_*/),
@@ -218,14 +212,20 @@ describe("/admin/collections", () => {
],
},
{
id: "test-collection2",
handle: "test-collection2",
title: "Test collection 2",
id: "test-collection",
handle: "test-collection",
title: "Test collection",
created_at: expect.any(String),
updated_at: expect.any(String),
products: [
{
collection_id: "test-collection2",
collection_id: "test-collection",
created_at: expect.any(String),
updated_at: expect.any(String),
profile_id: expect.stringMatching(/^sp_*/),
},
{
collection_id: "test-collection",
created_at: expect.any(String),
updated_at: expect.any(String),
profile_id: expect.stringMatching(/^sp_*/),

View File

@@ -1,6 +1,7 @@
import { ArrayNotEmpty, IsString } from "class-validator"
import ProductCollectionService from "../../../../services/product-collection"
import { validator } from "../../../../utils/validator"
import { Request, Response } from "express"
/**
* @oas [post] /collections/{id}/products/batch
* operationId: "PostProductsToCollection"
@@ -28,10 +29,9 @@ import { validator } from "../../../../utils/validator"
* "200":
* description: OK
*/
export default async (req, res) => {
export default async (req: Request, res: Response) => {
const { id } = req.params
const validated = await validator(AdminPostProductsToCollectionReq, req.body)
const { validatedBody } = req as { validatedBody: AdminPostProductsToCollectionReq }
const productCollectionService: ProductCollectionService = req.scope.resolve(
"productCollectionService"
@@ -39,7 +39,7 @@ export default async (req, res) => {
const collection = await productCollectionService.addProducts(
id,
validated.product_ids
validatedBody.product_ids
)
res.status(200).json({ collection })

View File

@@ -1,6 +1,7 @@
import { IsNotEmpty, IsObject, IsOptional, IsString } from "class-validator"
import ProductCollectionService from "../../../../services/product-collection"
import { validator } from "../../../../utils/validator"
import { Request, Response } from "express"
/**
* @oas [post] /collections
* operationId: "PostCollections"
@@ -35,14 +36,14 @@ import { validator } from "../../../../utils/validator"
* collection:
* $ref: "#/components/schemas/product_collection"
*/
export default async (req, res) => {
const validated = await validator(AdminPostCollectionsReq, req.body)
export default async (req: Request, res: Response) => {
const { validatedBody } = req
const productCollectionService: ProductCollectionService = req.scope.resolve(
"productCollectionService"
)
const created = await productCollectionService.create(validated)
const created = await productCollectionService.create(validatedBody)
const collection = await productCollectionService.retrieve(created.id)
res.status(200).json({ collection })

View File

@@ -1,4 +1,5 @@
import ProductCollectionService from "../../../../services/product-collection"
import { Request, Response } from "express"
/**
* @oas [delete] /collections/{id}
@@ -26,7 +27,7 @@ import ProductCollectionService from "../../../../services/product-collection"
* deleted:
* type: boolean
*/
export default async (req, res) => {
export default async (req: Request, res: Response) => {
const { id } = req.params
const productCollectionService: ProductCollectionService = req.scope.resolve(

View File

@@ -1,5 +1,6 @@
import { defaultAdminCollectionsRelations } from "."
import ProductCollectionService from "../../../../services/product-collection"
import { Request, Response } from "express"
/**
* @oas [get] /collections/{id}
* operationId: "GetCollectionsCollection"
@@ -20,17 +21,15 @@ import ProductCollectionService from "../../../../services/product-collection"
* collection:
* $ref: "#/components/schemas/product_collection"
*/
export default async (req, res) => {
export default async (req: Request, res: Response) => {
const { id } = req.params
const retrieveConfig = {
relations: defaultAdminCollectionsRelations,
}
const productCollectionService: ProductCollectionService = req.scope.resolve(
"productCollectionService"
)
const collection = await productCollectionService.retrieve(id, retrieveConfig)
const collection = await productCollectionService.retrieve(id, {
relations: defaultAdminCollectionsRelations,
})
res.status(200).json({ collection })
}

View File

@@ -2,27 +2,58 @@ import { Router } from "express"
import "reflect-metadata"
import { ProductCollection } from "../../../.."
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
import middlewares from "../../../middlewares"
const route = Router()
import middlewares, { transformBody, transformQuery } from "../../../middlewares"
import { AdminGetCollectionsParams } from "./list-collections"
import { AdminPostCollectionsReq } from "./create-collection"
import { AdminPostCollectionsCollectionReq } from "./update-collection"
import { AdminPostProductsToCollectionReq } from "./add-products"
import { AdminDeleteProductsFromCollectionReq } from "./remove-products"
export default (app) => {
const route = Router()
app.use("/collections", route)
route.post("/", middlewares.wrap(require("./create-collection").default))
route.post("/:id", middlewares.wrap(require("./update-collection").default))
route.delete("/:id", middlewares.wrap(require("./delete-collection").default))
route.get("/:id", middlewares.wrap(require("./get-collection").default))
route.get("/", middlewares.wrap(require("./list-collections").default))
route.post(
"/:id/products/batch",
"/",
transformBody(AdminPostCollectionsReq),
middlewares.wrap(require("./create-collection").default)
)
route.get(
"/",
transformQuery(
AdminGetCollectionsParams,
{
defaultRelations: defaultAdminCollectionsRelations,
defaultFields: defaultAdminCollectionsFields,
isList: true,
}
),
middlewares.wrap(require("./list-collections").default)
)
const collectionRouter = Router({ mergeParams: true })
route.use("/:id", collectionRouter)
collectionRouter.post(
"/",
transformBody(AdminPostCollectionsCollectionReq),
middlewares.wrap(require("./update-collection").default)
)
collectionRouter.get(
"/",
middlewares.wrap(require("./get-collection").default)
)
collectionRouter.delete(
"/",
middlewares.wrap(require("./delete-collection").default)
)
collectionRouter.post(
"/products/batch",
transformBody(AdminPostProductsToCollectionReq),
middlewares.wrap(require("./add-products").default)
)
route.delete(
"/:id/products/batch",
collectionRouter.delete(
"/products/batch",
transformBody(AdminDeleteProductsFromCollectionReq),
middlewares.wrap(require("./remove-products").default)
)

View File

@@ -1,13 +1,10 @@
import { Type } from "class-transformer"
import { IsNumber, IsOptional, IsString, ValidateNested } from "class-validator"
import _, { identity } from "lodash"
import {
defaultAdminCollectionsFields,
defaultAdminCollectionsRelations,
} from "."
import ProductCollectionService from "../../../../services/product-collection"
import { DateComparisonOperator } from "../../../../types/common"
import { validator } from "../../../../utils/validator"
import { Request, Response } from "express"
/**
* @oas [get] /collections
* operationId: "GetCollections"
@@ -34,21 +31,16 @@ import { validator } from "../../../../utils/validator"
* collection:
* $ref: "#/components/schemas/product_collection"
*/
export default async (req, res) => {
const validated = await validator(AdminGetCollectionsParams, req.query)
export default async (req: Request, res: Response) => {
const productCollectionService: ProductCollectionService = req.scope.resolve(
"productCollectionService"
)
const listConfig = {
select: defaultAdminCollectionsFields,
relations: defaultAdminCollectionsRelations,
skip: validated.offset,
take: validated.limit,
}
const filterableFields = _.omit(validated, ["limit", "offset"])
const {
validatedQuery: { limit, offset },
filterableFields,
listConfig
} = req
const [collections, count] = await productCollectionService.listAndCount(
_.pickBy(filterableFields, identity),
@@ -58,8 +50,8 @@ export default async (req, res) => {
res.status(200).json({
collections,
count,
offset: validated.offset,
limit: validated.limit,
offset,
limit,
})
}

View File

@@ -1,6 +1,7 @@
import { ArrayNotEmpty, IsString } from "class-validator"
import ProductCollectionService from "../../../../services/product-collection"
import { validator } from "../../../../utils/validator"
import { Request, Response } from "express"
/**
* @oas [delete] /collections/{id}/products/batch
* operationId: "DeleteProductsFromCollection"
@@ -28,24 +29,20 @@ import { validator } from "../../../../utils/validator"
* "200":
* description: OK
*/
export default async (req, res) => {
export default async (req: Request, res: Response) => {
const { id } = req.params
const validated = await validator(
AdminDeleteProductsFromCollectionReq,
req.body
)
const { validatedBody } = req as { validatedBody: AdminDeleteProductsFromCollectionReq }
const productCollectionService: ProductCollectionService = req.scope.resolve(
"productCollectionService"
)
await productCollectionService.removeProducts(id, validated.product_ids)
await productCollectionService.removeProducts(id, validatedBody.product_ids)
res.json({
id,
object: "product-collection",
removed_products: validated.product_ids,
removed_products: validatedBody.product_ids,
})
}

View File

@@ -1,6 +1,6 @@
import { IsObject, IsOptional, IsString } from "class-validator"
import ProductCollectionService from "../../../../services/product-collection"
import { validator } from "../../../../utils/validator"
import { Request, Response } from "express"
/**
* @oas [post] /collections/{id}
* operationId: "PostCollectionsCollection"
@@ -35,15 +35,15 @@ import { validator } from "../../../../utils/validator"
* collection:
* $ref: "#/components/schemas/product_collection"
*/
export default async (req, res) => {
export default async (req: Request, res: Response) => {
const { id } = req.params
const { validatedBody } = req
const validated = await validator(AdminPostCollectionsCollectionReq, req.body)
const productCollectionService: ProductCollectionService = req.scope.resolve(
"productCollectionService"
)
const updated = await productCollectionService.update(id, validated)
const updated = await productCollectionService.update(id, validatedBody)
const collection = await productCollectionService.retrieve(updated.id)
res.status(200).json({ collection })

View File

@@ -3,6 +3,7 @@ import { ValidateNested } from "class-validator"
import { CustomerGroupService } from "../../../../services"
import { CustomerGroupsBatchCustomer } from "../../../../types/customer-groups"
import { validator } from "../../../../utils/validator"
import { Request, Response } from "express"
/**
* @oas [post] /customer-groups/{id}/customers/batch
@@ -26,7 +27,7 @@ import { validator } from "../../../../utils/validator"
* $ref: "#/components/schemas/customergroup"
*/
export default async (req, res) => {
export default async (req: Request, res: Response) => {
const { id } = req.params
const validated = await validator(
AdminPostCustomerGroupsGroupCustomersBatchReq,

View File

@@ -1,6 +1,7 @@
import { IsObject, IsOptional, IsString } from "class-validator"
import { CustomerGroupService } from "../../../../services"
import { validator } from "../../../../utils/validator"
import { Request, Response } from "express"
/**
* @oas [post] /customer-groups
@@ -24,7 +25,7 @@ import { validator } from "../../../../utils/validator"
* $ref: "#/components/schemas/customer_group"
*/
export default async (req, res) => {
export default async (req: Request, res: Response) => {
const validated = await validator(AdminPostCustomerGroupsReq, req.body)
const customerGroupService: CustomerGroupService = req.scope.resolve(

View File

@@ -1,4 +1,5 @@
import { CustomerGroupService } from "../../../../services"
import { Request, Response } from "express"
/**
* @oas [delete] /customer-groups/{id}
@@ -27,7 +28,7 @@ import { CustomerGroupService } from "../../../../services"
* type: boolean
*/
export default async (req, res) => {
export default async (req: Request, res: Response) => {
const { id } = req.params
const customerGroupService: CustomerGroupService = req.scope.resolve(

View File

@@ -3,6 +3,7 @@ import { ValidateNested } from "class-validator"
import { CustomerGroupService } from "../../../../services"
import { CustomerGroupsBatchCustomer } from "../../../../types/customer-groups"
import { validator } from "../../../../utils/validator"
import { Request, Response } from "express"
/**
* @oas [delete] /customer-groups/{id}/customers/batch
@@ -26,7 +27,7 @@ import { validator } from "../../../../utils/validator"
* $ref: "#/components/schemas/customergroup"
*/
export default async (req, res) => {
export default async (req: Request, res: Response) => {
const { id } = req.params
const validated = await validator(
AdminDeleteCustomerGroupsGroupCustomerBatchReq,

View File

@@ -1,4 +1,5 @@
import CustomerController from "../../../../controllers/customers"
import { Request, Response } from "express"
/**
* @oas [get] /customer-groups/{id}/customers
@@ -18,7 +19,7 @@ import CustomerController from "../../../../controllers/customers"
* customer:
* $ref: "#/components/schemas/customer"
*/
export default async (req, res) => {
export default async (req: Request, res: Response) => {
const { id } = req.params
req.query.groups = [id]

View File

@@ -1,7 +1,6 @@
import { CustomerGroupService } from "../../../../services"
import { FindParams } from "../../../../types/common"
import { validator } from "../../../../utils/validator"
import { defaultAdminCustomerGroupsRelations } from "."
import { Request, Response } from "express"
/**
* @oas [get] /customer-groups/{id}
@@ -23,30 +22,17 @@ import { defaultAdminCustomerGroupsRelations } from "."
* customer_group:
* $ref: "#/components/schemas/customer_group"
*/
export default async (req, res) => {
export default async (req: Request, res: Response) => {
const { id } = req.params
const validated = await validator(
AdminGetCustomerGroupsGroupParams,
req.query
)
const customerGroupService: CustomerGroupService = req.scope.resolve(
"customerGroupService"
)
let expandFields: string[] = []
if (validated.expand) {
expandFields = validated.expand.split(",")
}
const findConfig = {
relations: expandFields.length
? expandFields
: defaultAdminCustomerGroupsRelations,
}
const customerGroup = await customerGroupService.retrieve(id, findConfig)
const customerGroup = await customerGroupService.retrieve(
id,
req.retrieveConfig
)
res.json({ customer_group: customerGroup })
}

View File

@@ -1,38 +1,53 @@
import { Router } from "express"
import { CustomerGroup } from "../../../.."
import { DeleteResponse, PaginatedResponse } from "../../../../types/common"
import middlewares from "../../../middlewares"
import middlewares, { transformQuery } from "../../../middlewares"
import { AdminGetCustomerGroupsGroupParams } from "./get-customer-group"
import { AdminGetCustomerGroupsParams } from "./list-customer-groups"
const route = Router()
export default (app) => {
app.use("/customer-groups", route)
route.get("/", middlewares.wrap(require("./list-customer-groups").default))
route.get("/:id", middlewares.wrap(require("./get-customer-group").default))
route.post("/", middlewares.wrap(require("./create-customer-group").default))
route.post(
"/:id/customers/batch",
middlewares.wrap(require("./add-customers-batch").default)
)
route.delete(
"/:id/customers/batch",
middlewares.wrap(require("./delete-customers-batch").default)
route.get(
"/",
transformQuery(AdminGetCustomerGroupsParams, {
defaultRelations: defaultAdminCustomerGroupsRelations,
isList: true,
}),
middlewares.wrap(require("./list-customer-groups").default)
)
route.delete(
"/:id",
const customerGroupRouter = Router({ mergeParams: true })
route.use("/:id", customerGroupRouter)
customerGroupRouter.get(
"/",
transformQuery(AdminGetCustomerGroupsGroupParams, {
defaultRelations: defaultAdminCustomerGroupsRelations,
}),
middlewares.wrap(require("./get-customer-group").default)
)
customerGroupRouter.delete(
"/",
middlewares.wrap(require("./delete-customer-group").default)
)
route.get(
"/:id/customers",
customerGroupRouter.post(
"/",
middlewares.wrap(require("./update-customer-group").default)
)
customerGroupRouter.get(
"/customers",
middlewares.wrap(require("./get-customer-group-customers").default)
)
route.post(
"/:id",
middlewares.wrap(require("./update-customer-group").default)
customerGroupRouter.post(
"/customers/batch",
middlewares.wrap(require("./add-customers-batch").default)
)
customerGroupRouter.delete(
"/customers/batch",
middlewares.wrap(require("./delete-customers-batch").default)
)
return app

View File

@@ -1,12 +1,8 @@
import { Type } from "class-transformer"
import { IsNumber, IsOptional, IsString } from "class-validator"
import omit from "lodash/omit"
import { defaultAdminCustomerGroupsRelations } from "."
import { CustomerGroup } from "../../../../models/customer-group"
import { CustomerGroupService } from "../../../../services"
import { FindConfig } from "../../../../types/common"
import { FilterableCustomerGroupProps } from "../../../../types/customer-groups"
import { validator } from "../../../../utils/validator"
import { Request, Response } from "express"
/**
* @oas [get] /customer-groups
@@ -37,53 +33,22 @@ import { validator } from "../../../../utils/validator"
* customerGroup:
* $ref: "#/components/schemas/customer_group"
*/
export default async (req, res) => {
const validated = await validator(AdminGetCustomerGroupsParams, req.query)
export default async (req: Request, res: Response) => {
const customerGroupService: CustomerGroupService = req.scope.resolve(
"customerGroupService"
)
let expandFields: string[] = []
if (validated.expand) {
expandFields = validated.expand.split(",")
}
const listConfig: FindConfig<CustomerGroup> = {
relations: expandFields.length
? expandFields
: defaultAdminCustomerGroupsRelations,
skip: validated.offset,
take: validated.limit,
order: { created_at: "DESC" } as { [k: string]: "DESC" },
}
if (typeof validated.order !== "undefined") {
if (validated.order.startsWith("-")) {
const [, field] = validated.order.split("-")
listConfig.order = { [field]: "DESC" }
} else {
listConfig.order = { [validated.order]: "ASC" }
}
}
const filterableFields = omit(validated, [
"limit",
"offset",
"expand",
"order",
])
const [data, count] = await customerGroupService.listAndCount(
filterableFields,
listConfig
req.filterableFields,
req.listConfig
)
const { limit, offset } = req.validatedQuery
res.json({
count,
customer_groups: data,
offset: validated.offset,
limit: validated.limit,
offset,
limit,
})
}

View File

@@ -4,6 +4,7 @@ import { defaultAdminCustomerGroupsRelations } from "."
import { CustomerGroupService } from "../../../../services"
import { FindParams } from "../../../../types/common"
import { validator } from "../../../../utils/validator"
import { Request, Response } from "express"
/**
* @oas [post] /customer-groups/{id}
@@ -28,7 +29,7 @@ import { validator } from "../../../../utils/validator"
* $ref: "#/components/schemas/customer_group"
*/
export default async (req, res) => {
export default async (req: Request, res: Response) => {
const { id } = req.params
const validatedBody = await validator(

View File

@@ -19,6 +19,7 @@ declare global {
}
}
export type ClassConstructor<T> = {
new (...args: unknown[]): T
}

View File

@@ -136,7 +136,7 @@ export function prepareRetrieveQuery<
expandRelations = expand.split(",")
}
let expandFields: (keyof TEntity)[] = []
let expandFields: (keyof TEntity)[] | undefined = undefined
if (fields) {
expandFields = fields.split(",") as (keyof TEntity)[]
}