Adds admin API endpoints for product operations (#32)
This commit is contained in:
committed by
GitHub
parent
904a2369fb
commit
42fdd14e42
@@ -0,0 +1,43 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
||||
|
||||
describe("POST /admin/products/:id/options", () => {
|
||||
describe("successful add option", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/products/${IdMap.getId("productWithOptions")}/options`,
|
||||
{
|
||||
payload: {
|
||||
optionTitle: "Test option",
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls service addOption", () => {
|
||||
expect(ProductServiceMock.addOption).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.addOption).toHaveBeenCalledWith(
|
||||
IdMap.getId("productWithOptions"),
|
||||
"Test option"
|
||||
)
|
||||
})
|
||||
|
||||
it("returns the updated product decorated", () => {
|
||||
expect(subject.body._id).toEqual(IdMap.getId("productWithOptions"))
|
||||
expect(subject.body.decorated).toEqual(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,42 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
||||
|
||||
describe("POST /admin/products/:id/variants/:variantId", () => {
|
||||
describe("successful add variant", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/products/${IdMap.getId(
|
||||
"productWithOptions"
|
||||
)}/variants/${IdMap.getId("variant2")}`,
|
||||
{
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls service addVariant", () => {
|
||||
expect(ProductServiceMock.addVariant).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.addVariant).toHaveBeenCalledWith(
|
||||
IdMap.getId("productWithOptions"),
|
||||
IdMap.getId("variant2")
|
||||
)
|
||||
})
|
||||
|
||||
it("returns the updated product decorated", () => {
|
||||
expect(subject.body._id).toEqual(IdMap.getId("productWithOptions"))
|
||||
expect(subject.body.decorated).toEqual(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -23,16 +23,12 @@ describe("POST /admin/products", () => {
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(201)
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("returns created product draft", () => {
|
||||
expect(subject.body).toEqual({
|
||||
title: "Test Product",
|
||||
description: "Test Description",
|
||||
tags: "hi,med,dig",
|
||||
handle: "test-product",
|
||||
})
|
||||
expect(subject.body._id).toEqual(IdMap.getId("product1"))
|
||||
expect(subject.body.decorated).toEqual(true)
|
||||
})
|
||||
|
||||
it("calls service createDraft", () => {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
||||
|
||||
describe("DELETE /admin/products/:id/options/:optionId", () => {
|
||||
describe("successfully updates an option", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"DELETE",
|
||||
`/admin/products/${IdMap.getId(
|
||||
"productWithOptions"
|
||||
)}/options/${IdMap.getId("option1")}`,
|
||||
{
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 200 and correct delete info", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
expect(subject.body).toEqual({
|
||||
optionId: IdMap.getId("option1"),
|
||||
object: "option",
|
||||
deleted: true,
|
||||
})
|
||||
})
|
||||
|
||||
it("calls update", () => {
|
||||
expect(ProductServiceMock.deleteOption).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.deleteOption).toHaveBeenCalledWith(
|
||||
IdMap.getId("productWithOptions"),
|
||||
IdMap.getId("option1")
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,46 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
||||
|
||||
describe("DELETE /admin/products/:id", () => {
|
||||
describe("successfully deletes a product", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"DELETE",
|
||||
`/admin/products/${IdMap.getId("product1")}`,
|
||||
{
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("calls ProductService delete", () => {
|
||||
expect(ProductServiceMock.delete).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.delete).toHaveBeenCalledWith(
|
||||
IdMap.getId("product1")
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("returns correct delete data", () => {
|
||||
expect(subject.body).toEqual({
|
||||
id: IdMap.getId("product1"),
|
||||
object: "product",
|
||||
deleted: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,39 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
||||
|
||||
describe("GET /admin/products/:id", () => {
|
||||
describe("successfully gets a product", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"GET",
|
||||
`/admin/products/${IdMap.getId("product1")}`,
|
||||
{
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("calls get product from productSerice", () => {
|
||||
expect(ProductServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.retrieve).toHaveBeenCalledWith(
|
||||
IdMap.getId("product1")
|
||||
)
|
||||
})
|
||||
|
||||
it("returns product decorated", () => {
|
||||
expect(subject.body._id).toEqual(IdMap.getId("product1"))
|
||||
expect(subject.body.decorated).toEqual(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,34 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import {
|
||||
ProductServiceMock,
|
||||
products,
|
||||
} from "../../../../../services/__mocks__/product"
|
||||
|
||||
describe("GET /admin/products", () => {
|
||||
describe("successfully lists products", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request("GET", `/admin/products`, {
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it("returns 200 and decorated products", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
expect(subject.body[0]._id).toEqual(products.product1._id)
|
||||
expect(subject.body[0].decorated).toEqual(true)
|
||||
expect(subject.body[1]._id).toEqual(products.product2._id)
|
||||
expect(subject.body[1].decorated).toEqual(true)
|
||||
})
|
||||
|
||||
it("calls update", () => {
|
||||
expect(ProductServiceMock.list).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,38 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
||||
|
||||
describe("POST /admin/products/:id/publish", () => {
|
||||
describe("successful publish", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/products/${IdMap.getId("publish")}/publish`,
|
||||
{
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("returns product with published flag true", () => {
|
||||
expect(subject.body.published).toEqual(true)
|
||||
})
|
||||
|
||||
it("calls service publish", () => {
|
||||
expect(ProductServiceMock.publish).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.publish).toHaveBeenCalledWith(
|
||||
IdMap.getId("publish")
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,42 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
||||
|
||||
describe("POST /admin/products/:id/variants/:variantId", () => {
|
||||
describe("successful removes variant", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"DELETE",
|
||||
`/admin/products/${IdMap.getId(
|
||||
"productWithOptions"
|
||||
)}/variants/${IdMap.getId("variant1")}`,
|
||||
{
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls service removeVariant", () => {
|
||||
expect(ProductServiceMock.removeVariant).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.removeVariant).toHaveBeenCalledWith(
|
||||
IdMap.getId("productWithOptions"),
|
||||
IdMap.getId("variant1")
|
||||
)
|
||||
})
|
||||
|
||||
it("returns decorated product with variant removed", () => {
|
||||
expect(subject.body._id).toEqual(IdMap.getId("productWithOptions"))
|
||||
expect(subject.body.decorated).toEqual(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,43 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
||||
|
||||
describe("POST /admin/products/:id/options/:optionId", () => {
|
||||
describe("successfully updates an option", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/products/${IdMap.getId(
|
||||
"productWithOptions"
|
||||
)}/options/${IdMap.getId("option1")}`,
|
||||
{
|
||||
payload: {
|
||||
title: "Updated option title",
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls update", () => {
|
||||
expect(ProductServiceMock.updateOption).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.updateOption).toHaveBeenCalledWith(
|
||||
IdMap.getId("productWithOptions"),
|
||||
IdMap.getId("option1"),
|
||||
{
|
||||
title: "Updated option title",
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,90 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
||||
|
||||
describe("POST /admin/products/:id", () => {
|
||||
describe("successfully updates a product", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request(
|
||||
"POST",
|
||||
`/admin/products/${IdMap.getId("product1")}`,
|
||||
{
|
||||
payload: {
|
||||
title: "Product 1",
|
||||
description: "Updated test description",
|
||||
handle: "handle",
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
it("returns 200", () => {
|
||||
expect(subject.status).toEqual(200)
|
||||
})
|
||||
|
||||
it("calls update", () => {
|
||||
expect(ProductServiceMock.update).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.update).toHaveBeenCalledWith(
|
||||
IdMap.getId("product1"),
|
||||
{
|
||||
title: "Product 1",
|
||||
description: "Updated test description",
|
||||
handle: "handle",
|
||||
}
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("handles failed update operation", () => {
|
||||
it("throws if metadata is to be updated", async () => {
|
||||
try {
|
||||
await request("POST", `/admin/products/${IdMap.getId("product1")}`, {
|
||||
payload: {
|
||||
_id: IdMap.getId("product1"),
|
||||
title: "Product 1",
|
||||
metadata: "Test Description",
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
expect(error.status).toEqual(400)
|
||||
expect(error.message).toEqual(
|
||||
"Use setMetadata to update metadata fields"
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("throws if variants is to be updated", async () => {
|
||||
try {
|
||||
await request("POST", `/admin/products/${IdMap.getId("product1")}`, {
|
||||
payload: {
|
||||
_id: IdMap.getId("product1"),
|
||||
title: "Product 1",
|
||||
metadata: "Test Description",
|
||||
},
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
})
|
||||
} catch (error) {
|
||||
expect(error.status).toEqual(400)
|
||||
expect(error.message).toEqual(
|
||||
"Use addVariant, reorderVariants, removeVariant to update Product Variants"
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
34
packages/medusa/src/api/routes/admin/products/add-option.js
Normal file
34
packages/medusa/src/api/routes/admin/products/add-option.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
optionTitle: Validator.string().required(),
|
||||
})
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const productService = req.scope.resolve("productService")
|
||||
const product = await productService.retrieve(id)
|
||||
await productService.addOption(product._id, value.optionTitle)
|
||||
let newProduct = await productService.retrieve(product._id)
|
||||
newProduct = await productService.decorate(newProduct, [
|
||||
"title",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"images",
|
||||
"options",
|
||||
"variants",
|
||||
"published",
|
||||
])
|
||||
res.json(newProduct)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
23
packages/medusa/src/api/routes/admin/products/add-variant.js
Normal file
23
packages/medusa/src/api/routes/admin/products/add-variant.js
Normal file
@@ -0,0 +1,23 @@
|
||||
export default async (req, res) => {
|
||||
const { id, variantId } = req.params
|
||||
|
||||
try {
|
||||
const productService = req.scope.resolve("productService")
|
||||
const product = await productService.retrieve(id)
|
||||
await productService.addVariant(product._id, variantId)
|
||||
let newProduct = await productService.retrieve(product._id)
|
||||
newProduct = await productService.decorate(newProduct, [
|
||||
"title",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"images",
|
||||
"options",
|
||||
"variants",
|
||||
"published",
|
||||
])
|
||||
res.json(newProduct)
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,18 @@ export default async (req, res) => {
|
||||
|
||||
try {
|
||||
const productService = req.scope.resolve("productService")
|
||||
const data = await productService.createDraft(value)
|
||||
|
||||
// Return the created product draft
|
||||
res.status(201).json(data)
|
||||
let newProduct = await productService.createDraft(value)
|
||||
newProduct = await productService.decorate(newProduct, [
|
||||
"title",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"images",
|
||||
"options",
|
||||
"variants",
|
||||
"published",
|
||||
])
|
||||
res.json(newProduct)
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
export default async (req, res) => {
|
||||
const { id, optionId } = req.params
|
||||
|
||||
try {
|
||||
const productService = req.scope.resolve("productService")
|
||||
await productService.deleteOption(id, optionId)
|
||||
res.json({
|
||||
optionId,
|
||||
object: "option",
|
||||
deleted: true,
|
||||
})
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
try {
|
||||
const productService = req.scope.resolve("productService")
|
||||
await productService.delete(id)
|
||||
res.json({
|
||||
id,
|
||||
object: "product",
|
||||
deleted: true,
|
||||
})
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
19
packages/medusa/src/api/routes/admin/products/get-product.js
Normal file
19
packages/medusa/src/api/routes/admin/products/get-product.js
Normal file
@@ -0,0 +1,19 @@
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const productService = req.scope.resolve("productService")
|
||||
let product = await productService.retrieve(id)
|
||||
|
||||
product = await productService.decorate(product, [
|
||||
"title",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"images",
|
||||
"options",
|
||||
"variants",
|
||||
"published",
|
||||
])
|
||||
|
||||
res.json(product)
|
||||
}
|
||||
@@ -7,8 +7,33 @@ export default app => {
|
||||
app.use("/products", route)
|
||||
|
||||
route.post("/", middlewares.wrap(require("./create-product").default))
|
||||
route.post("/:id", middlewares.wrap(require("./update-product").default))
|
||||
route.post(
|
||||
"/:id/publish",
|
||||
middlewares.wrap(require("./publish-product").default)
|
||||
)
|
||||
route.post(
|
||||
"/:id/variants/:variantId",
|
||||
middlewares.wrap(require("./add-variant").default)
|
||||
)
|
||||
route.post(
|
||||
"/:id/options/:optionId",
|
||||
middlewares.wrap(require("./update-option").default)
|
||||
)
|
||||
route.post("/:id/options", middlewares.wrap(require("./add-option").default))
|
||||
|
||||
// route.get("/:productId", middlewares.wrap(require("./get-product").default))
|
||||
route.delete(
|
||||
"/:id/variants/:variantId",
|
||||
middlewares.wrap(require("./remove-variant").default)
|
||||
)
|
||||
route.delete("/:id", middlewares.wrap(require("./delete-product").default))
|
||||
route.delete(
|
||||
"/:id/options/:optionId",
|
||||
middlewares.wrap(require("./delete-option").default)
|
||||
)
|
||||
|
||||
route.get("/:id", middlewares.wrap(require("./get-product").default))
|
||||
route.get("/", middlewares.wrap(require("./list-products").default))
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
export default async (req, res) => {
|
||||
try {
|
||||
const productService = req.scope.resolve("productService")
|
||||
let products = await productService.list({})
|
||||
products = await Promise.all(
|
||||
products.map(
|
||||
async product =>
|
||||
await productService.decorate(product, [
|
||||
"title",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"images",
|
||||
"options",
|
||||
"variants",
|
||||
"published",
|
||||
])
|
||||
)
|
||||
)
|
||||
res.json(products)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
try {
|
||||
const productService = req.scope.resolve("productService")
|
||||
const product = await productService.retrieve(id)
|
||||
await productService.publish(product._id)
|
||||
let publishedProduct = await productService.retrieve(product._id)
|
||||
publishedProduct = await productService.decorate(publishedProduct, [
|
||||
"title",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"images",
|
||||
"options",
|
||||
"variants",
|
||||
"published",
|
||||
])
|
||||
res.json(publishedProduct)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
export default async (req, res) => {
|
||||
const { id, variantId } = req.params
|
||||
|
||||
try {
|
||||
const productService = req.scope.resolve("productService")
|
||||
await productService.removeVariant(id, variantId)
|
||||
let updatedProduct = await productService.retrieve(id)
|
||||
updatedProduct = await productService.decorate(updatedProduct, [
|
||||
"title",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"images",
|
||||
"options",
|
||||
"variants",
|
||||
"published",
|
||||
])
|
||||
res.json(updatedProduct)
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id, optionId } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
title: Validator.string(),
|
||||
values: Validator.array().items(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const productService = req.scope.resolve("productService")
|
||||
const product = await productService.retrieve(id)
|
||||
|
||||
await productService.updateOption(product._id, optionId, value)
|
||||
|
||||
let newProduct = await productService.retrieve(product._id)
|
||||
newProduct = await productService.decorate(newProduct, [
|
||||
"title",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"images",
|
||||
"options",
|
||||
"variants",
|
||||
"published",
|
||||
])
|
||||
|
||||
res.json(newProduct)
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import { MedusaError, Validator } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
const { id } = req.params
|
||||
|
||||
const schema = Validator.object().keys({
|
||||
title: Validator.string().required(),
|
||||
description: Validator.string().optional(),
|
||||
tags: Validator.string().optional(),
|
||||
handle: Validator.string().required(),
|
||||
images: Validator.array()
|
||||
.items(Validator.string())
|
||||
.optional(),
|
||||
variants: Validator.array()
|
||||
.items(Validator.string())
|
||||
.optional(),
|
||||
metadata: Validator.object().optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const productService = req.scope.resolve("productService")
|
||||
const oldProduct = await productService.retrieve(id)
|
||||
await productService.update(oldProduct._id, value)
|
||||
let newProduct = await productService.retrieve(oldProduct._id)
|
||||
newProduct = await productService.decorate(newProduct, [
|
||||
"title",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"images",
|
||||
"options",
|
||||
"variants",
|
||||
"published",
|
||||
])
|
||||
res.json(newProduct)
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
@@ -1,40 +1,24 @@
|
||||
import mongoose from "mongoose"
|
||||
import getProduct from "../get-product"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
|
||||
|
||||
describe("Get product by id", () => {
|
||||
const testId = `${mongoose.Types.ObjectId("56cb91bdc3464f14678934ca")}`
|
||||
const productServiceMock = {
|
||||
retrieve: jest.fn().mockImplementation(id => {
|
||||
if (id === testId) {
|
||||
return Promise.resolve({ _id: id, title: "test" })
|
||||
}
|
||||
return Promise.resolve(undefined)
|
||||
}),
|
||||
}
|
||||
const reqMock = id => {
|
||||
return {
|
||||
params: {
|
||||
productId: id,
|
||||
},
|
||||
scope: {
|
||||
resolve: jest.fn().mockImplementation(name => {
|
||||
if (name === "productService") {
|
||||
return productServiceMock
|
||||
}
|
||||
return undefined
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const resMock = {
|
||||
sendStatus: jest.fn().mockReturnValue(),
|
||||
json: jest.fn().mockReturnValue(),
|
||||
}
|
||||
|
||||
describe("get product by id successfull", () => {
|
||||
let subject
|
||||
beforeAll(async () => {
|
||||
await getProduct(reqMock(testId), resMock)
|
||||
subject = await request(
|
||||
"GET",
|
||||
`/admin/products/${IdMap.getId("product1")}`,
|
||||
{
|
||||
adminSession: {
|
||||
jwt: {
|
||||
userId: IdMap.getId("admin_user"),
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
@@ -42,52 +26,15 @@ describe("Get product by id", () => {
|
||||
})
|
||||
|
||||
it("calls get product from productSerice", () => {
|
||||
expect(productServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(productServiceMock.retrieve).toHaveBeenCalledWith(testId)
|
||||
expect(ProductServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(ProductServiceMock.retrieve).toHaveBeenCalledWith(
|
||||
IdMap.getId("product1")
|
||||
)
|
||||
})
|
||||
|
||||
it("calls res.json", () => {
|
||||
expect(resMock.json).toHaveBeenCalledTimes(1)
|
||||
expect(resMock.json).toHaveBeenCalledWith({
|
||||
_id: testId,
|
||||
title: "test",
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("returns 404 when product not found", () => {
|
||||
beforeAll(async () => {
|
||||
const id = mongoose.Types.ObjectId()
|
||||
await getProduct(reqMock(`${id}`), resMock)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("return 404", () => {
|
||||
expect(resMock.sendStatus).toHaveBeenCalledTimes(1)
|
||||
expect(resMock.json).toHaveBeenCalledTimes(0)
|
||||
expect(resMock.sendStatus).toHaveBeenCalledWith(404)
|
||||
})
|
||||
})
|
||||
|
||||
describe("fails when validation fails", () => {
|
||||
let res
|
||||
beforeAll(async () => {
|
||||
try {
|
||||
await getProduct(reqMock(`not object id`), resMock)
|
||||
} catch (err) {
|
||||
res = err
|
||||
}
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("return 404", () => {
|
||||
expect(res.name).toEqual("ValidationError")
|
||||
it("returns product decorated", () => {
|
||||
expect(subject.body._id).toEqual(IdMap.getId("product1"))
|
||||
expect(subject.body.decorated).toEqual(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -11,12 +11,18 @@ export default async (req, res) => {
|
||||
}
|
||||
|
||||
const productService = req.scope.resolve("productService")
|
||||
const product = await productService.retrieve(value)
|
||||
let product = await productService.retrieve(value)
|
||||
|
||||
if (!product) {
|
||||
res.sendStatus(404)
|
||||
return
|
||||
}
|
||||
product = await productService.decorate(product, [
|
||||
"title",
|
||||
"description",
|
||||
"tags",
|
||||
"handle",
|
||||
"images",
|
||||
"options",
|
||||
"variants",
|
||||
"published",
|
||||
])
|
||||
|
||||
res.json(product)
|
||||
}
|
||||
|
||||
@@ -1,32 +1,84 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
|
||||
export const products = {
|
||||
product1: {
|
||||
_id: IdMap.getId("product1"),
|
||||
name: "Product 1",
|
||||
title: "Product 1",
|
||||
},
|
||||
publishProduct: {
|
||||
_id: IdMap.getId("publish"),
|
||||
title: "Product 1",
|
||||
published: true,
|
||||
},
|
||||
product2: {
|
||||
_id: IdMap.getId("product2"),
|
||||
name: "Product 2",
|
||||
title: "Product 2",
|
||||
},
|
||||
productWithOptions: {
|
||||
_id: IdMap.getId("productWithOptions"),
|
||||
title: "Test",
|
||||
variants: [IdMap.getId("variant1")],
|
||||
options: [
|
||||
{
|
||||
_id: IdMap.getId("option1"),
|
||||
title: "Test",
|
||||
values: [IdMap.getId("optionValue1")],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export const ProductServiceMock = {
|
||||
createDraft: jest.fn().mockImplementation(data => {
|
||||
return Promise.resolve(data)
|
||||
return Promise.resolve(products.product1)
|
||||
}),
|
||||
retrieve: jest.fn().mockImplementation(id => {
|
||||
if (id === IdMap.getId("validId")) {
|
||||
return Promise.resolve({ _id: IdMap.getId("validId") })
|
||||
}
|
||||
if (id === IdMap.getId("product1")) {
|
||||
publish: jest.fn().mockImplementation(_ => {
|
||||
return Promise.resolve({
|
||||
_id: IdMap.getId("publish"),
|
||||
name: "Product 1",
|
||||
published: true,
|
||||
})
|
||||
}),
|
||||
delete: jest.fn().mockImplementation(_ => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
addVariant: jest.fn().mockImplementation((productId, variantId) => {
|
||||
return Promise.resolve(products.productWithOptions)
|
||||
}),
|
||||
removeVariant: jest.fn().mockImplementation((productId, variantId) => {
|
||||
return Promise.resolve(products.productWithOptions)
|
||||
}),
|
||||
decorate: jest.fn().mockImplementation((product, fields) => {
|
||||
product.decorated = true
|
||||
return product
|
||||
}),
|
||||
addOption: jest.fn().mockImplementation((productId, optionTitle) => {
|
||||
return Promise.resolve(products.productWithOptions)
|
||||
}),
|
||||
updateOption: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
deleteOption: jest.fn().mockReturnValue(Promise.resolve()),
|
||||
retrieve: jest.fn().mockImplementation(productId => {
|
||||
if (productId === IdMap.getId("product1")) {
|
||||
return Promise.resolve(products.product1)
|
||||
}
|
||||
if (id === IdMap.getId("product2")) {
|
||||
if (productId === IdMap.getId("product2")) {
|
||||
return Promise.resolve(products.product2)
|
||||
}
|
||||
if (productId === IdMap.getId("validId")) {
|
||||
return Promise.resolve({ _id: IdMap.getId("validId") })
|
||||
}
|
||||
if (productId === IdMap.getId("publish")) {
|
||||
return Promise.resolve(products.publishProduct)
|
||||
}
|
||||
if (productId === IdMap.getId("productWithOptions")) {
|
||||
return Promise.resolve(products.productWithOptions)
|
||||
}
|
||||
return Promise.resolve(undefined)
|
||||
}),
|
||||
update: jest.fn().mockImplementation((userId, data) => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
list: jest.fn().mockImplementation(data => {
|
||||
// Used to retrieve a product based on a variant id see
|
||||
// ProductVariantService.addOptionValue
|
||||
@@ -56,7 +108,10 @@ export const ProductServiceMock = {
|
||||
return Promise.resolve([])
|
||||
}
|
||||
|
||||
return Promise.resolve([products.product1, products.product2])
|
||||
return Promise.resolve([
|
||||
{ ...products.product1, decorated: true },
|
||||
{ ...products.product2, decorated: true },
|
||||
])
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
@@ -379,7 +379,7 @@ class ProductService extends BaseService {
|
||||
)
|
||||
}
|
||||
|
||||
const { title } = data
|
||||
const { title, values } = data
|
||||
const titleExists = product.options.some(
|
||||
o => o.title.toUpperCase() === title.toUpperCase()
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user