feat: add and remove products to/from collection in bulk endpoints (#1032)
* adds bulk add/remove products to/from collection. Adds endpoint updateProducts on collections that uses these bulk operations * fix integration tests and test description * undo change to swap * made requested changes * added removeProducts endpoint * made requested changes * fix: set collection_id null * updated collection_id to type string | undefined
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductCollectionServiceMock } from "../../../../../services/__mocks__/product-collection"
|
||||
|
||||
describe("POST /admin/collections/:id/products/batch", () => {
|
||||
describe("successfully adds products to collection", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/collections/${IdMap.getId("col")}/products/batch`,
|
||||
{
|
||||
payload: {
|
||||
product_ids: ["prod_1", "prod_2"],
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("returns updated collection with new products", () => {
|
||||
expect(subject.body.collection.id).toEqual(IdMap.getId("col"))
|
||||
})
|
||||
|
||||
it("product collection service update", () => {
|
||||
expect(ProductCollectionServiceMock.addProducts).toHaveBeenCalledTimes(1)
|
||||
expect(
|
||||
ProductCollectionServiceMock.addProducts
|
||||
).toHaveBeenCalledWith(IdMap.getId("col"), ["prod_1", "prod_2"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("error on non-existing collection", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/collections/null/products/batch`,
|
||||
{
|
||||
payload: {
|
||||
product_ids: ["prod_1", "prod_2"],
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("throws error", () => {
|
||||
expect(subject.body.message).toBe("Product collection not found")
|
||||
})
|
||||
})
|
||||
|
||||
describe("error invalid request", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/collections/${IdMap.getId("col")}/products/batch`,
|
||||
{
|
||||
payload: {
|
||||
product_ids: [],
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 400", () => {
|
||||
expect(subject.status).toEqual(400)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,64 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductCollectionServiceMock } from "../../../../../services/__mocks__/product-collection"
|
||||
|
||||
describe("DELETE /admin/collections/:id/products/batch", () => {
|
||||
describe("successfully removes products from collection", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"DELETE",
|
||||
`/admin/collections/${IdMap.getId("col")}/products/batch`,
|
||||
{
|
||||
payload: {
|
||||
product_ids: ["prod_1", "prod_2"],
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("product collection service remove products", () => {
|
||||
expect(ProductCollectionServiceMock.removeProducts).toHaveBeenCalledTimes(
|
||||
1
|
||||
)
|
||||
expect(
|
||||
ProductCollectionServiceMock.removeProducts
|
||||
).toHaveBeenCalledWith(IdMap.getId("col"), ["prod_1", "prod_2"])
|
||||
})
|
||||
})
|
||||
|
||||
describe("error on invalid request", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"DELETE",
|
||||
`/admin/collections/${IdMap.getId("col")}/products/batch`,
|
||||
{
|
||||
payload: {
|
||||
product_ids: [],
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 400", () => {
|
||||
expect(subject.status).toEqual(400)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,52 @@
|
||||
import { ArrayNotEmpty, IsString } from "class-validator"
|
||||
import ProductCollectionService from "../../../../services/product-collection"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [post] /collections/{id}/products/batch
|
||||
* operationId: "PostProductsToCollection"
|
||||
* summary: "Updates products associated with a Product Collection"
|
||||
* description: "Updates products associated with a Product Collection"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Collection.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* product_ids:
|
||||
* description: "An array of Product IDs to add to the Product Collection."
|
||||
* type: array
|
||||
* items:
|
||||
* properties:
|
||||
* id:
|
||||
* description: "The ID of a Product to add to the Product Collection."
|
||||
* type: string
|
||||
* tags:
|
||||
* - Collection
|
||||
* responses:
|
||||
* "200":
|
||||
* description: OK
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const validated = await validator(AdminPostProductsToCollectionReq, req.body)
|
||||
|
||||
const productCollectionService: ProductCollectionService = req.scope.resolve(
|
||||
"productCollectionService"
|
||||
)
|
||||
|
||||
const collection = await productCollectionService.addProducts(
|
||||
id,
|
||||
validated.product_ids
|
||||
)
|
||||
|
||||
res.status(200).json({ collection })
|
||||
}
|
||||
|
||||
export class AdminPostProductsToCollectionReq {
|
||||
@ArrayNotEmpty()
|
||||
@IsString({ each: true })
|
||||
product_ids: string[]
|
||||
}
|
||||
@@ -17,6 +17,9 @@ export default (app) => {
|
||||
route.get("/:id", middlewares.wrap(require("./get-collection").default))
|
||||
route.get("/", middlewares.wrap(require("./list-collections").default))
|
||||
|
||||
route.post("/:id/products/batch", middlewares.wrap(require("./add-products").default))
|
||||
route.delete("/:id/products/batch", middlewares.wrap(require("./remove-products").default))
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { ArrayNotEmpty, IsString } from "class-validator"
|
||||
import ProductCollectionService from "../../../../services/product-collection"
|
||||
import { validator } from "../../../../utils/validator"
|
||||
/**
|
||||
* @oas [delete] /collections/{id}/products/batch
|
||||
* operationId: "DeleteProductsFromCollection"
|
||||
* summary: "Removes products associated with a Product Collection"
|
||||
* description: "Removes products associated with a Product Collection"
|
||||
* x-authenticated: true
|
||||
* parameters:
|
||||
* - (path) id=* {string} The id of the Collection.
|
||||
* requestBody:
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* properties:
|
||||
* product_ids:
|
||||
* description: "An array of Product IDs to remove from the Product Collection."
|
||||
* type: array
|
||||
* items:
|
||||
* properties:
|
||||
* id:
|
||||
* description: "The ID of a Product to remove from the Product Collection."
|
||||
* type: string
|
||||
* tags:
|
||||
* - Collection
|
||||
* responses:
|
||||
* "200":
|
||||
* description: OK
|
||||
*/
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const validated = await validator(
|
||||
AdminDeleteProductsFromCollectionReq,
|
||||
req.body
|
||||
)
|
||||
|
||||
const productCollectionService: ProductCollectionService = req.scope.resolve(
|
||||
"productCollectionService"
|
||||
)
|
||||
|
||||
await productCollectionService.removeProducts(id, validated.product_ids)
|
||||
|
||||
res.json({
|
||||
id,
|
||||
object: "product-collection",
|
||||
removed_products: validated.product_ids,
|
||||
})
|
||||
}
|
||||
|
||||
export class AdminDeleteProductsFromCollectionReq {
|
||||
@ArrayNotEmpty()
|
||||
@IsString({ each: true })
|
||||
product_ids: string[]
|
||||
}
|
||||
Reference in New Issue
Block a user