create product endpoint

This commit is contained in:
Sebastian Rindom
2020-01-21 16:27:02 +01:00
parent ed472e9fba
commit 4ba63ccc0d
8 changed files with 161 additions and 67 deletions

View File

@@ -2,6 +2,7 @@ import { Router } from "express"
import admin from "./routes/admin"
import store from "./routes/store"
import users from "./routes/users"
import errorHandler from "./middlewares/error-handler"
// guaranteed to get dependencies
export default () => {
@@ -11,5 +12,7 @@ export default () => {
admin(app)
store(app)
app.use(errorHandler())
return app
}

View File

@@ -0,0 +1,22 @@
import { MedusaError } from "medusa-core-utils"
export default () => {
return (err, req, res, next) => {
const logger = req.scope.resolve("logger")
logger.error(err.message)
let statusCode = 500
switch (err.name) {
case MedusaError.Types.INVALID_DATA:
statusCode = 400
break
case MedusaError.Types.DB_ERROR:
statusCode = 500
break
default:
break
}
res.status(statusCode).json(err)
}
}

View File

@@ -1,17 +1,76 @@
import IdMap from "../../../../../helpers/id-map"
import { request } from "../../../../../helpers/test-request"
import { ProductServiceMock } from "../../../../../services/__mocks__/product"
describe("POST /admin/products", () => {
describe("successful creation", () => {
it("calls mock function", async () => {
const res = await request("POST", "/admin/products", {
let subject
beforeAll(async () => {
subject = await request("POST", "/admin/products", {
payload: {
title: "Test Product",
description: "Test Description",
tags: "hi,med,dig",
handle: "test-product",
},
adminSession: {
jwt: {
userId: IdMap.getId("admin_user"),
},
},
})
expect(res.status).toEqual(200)
})
it("returns 200", () => {
expect(subject.status).toEqual(201)
})
it("returns created product draft", () => {
expect(subject.body).toEqual({
title: "Test Product",
description: "Test Description",
tags: "hi,med,dig",
handle: "test-product",
})
})
it("calls service createDraft", () => {
expect(ProductServiceMock.createDraft).toHaveBeenCalledTimes(1)
expect(ProductServiceMock.createDraft).toHaveBeenCalledWith({
title: "Test Product",
description: "Test Description",
tags: "hi,med,dig",
handle: "test-product",
})
})
})
describe("invalid data returns error details", () => {
let subject
beforeAll(async () => {
subject = await request("POST", "/admin/products", {
payload: {
description: "Test Description",
tags: "hi,med,dig",
handle: "test-product",
},
adminSession: {
jwt: {
userId: IdMap.getId("admin_user"),
},
},
})
})
it("returns 400", () => {
expect(subject.status).toEqual(400)
})
it("returns error details", () => {
expect(subject.body.name).toEqual("invalid_data")
expect(subject.body.message[0].message).toEqual(`"title" is required`)
})
})
})

View File

@@ -1,11 +1,28 @@
import { Validator } from "medusa-core-utils"
import { MedusaError, Validator } from "medusa-core-utils"
export default async (req, res) => {
try {
const variantService = req.scope.resolve("productVariantService")
} catch (err) {
console.log(err)
const schema = Validator.object().keys({
title: Validator.string().required(),
description: Validator.string(),
tags: Validator.string(),
images: Validator.array().items(Validator.string()),
variants: Validator.array().items(Validator.string()),
metadata: Validator.object(),
handle: Validator.string().required(),
})
const { value, error } = schema.validate(req.body)
if (error) {
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
}
res.sendStatus(200)
try {
const productService = req.scope.resolve("productService")
const data = await productService.createDraft(value)
// Return the created product draft
res.status(201).json(data)
} catch (err) {
throw err
}
}

View File

@@ -1,7 +1,6 @@
import "core-js/stable"
import "regenerator-runtime/runtime"
import express from "express"
import { MedusaError } from "medusa-core-utils"
import loaders from "./loaders"
import Logger from "./loaders/logger"
@@ -12,28 +11,6 @@ const startServer = async () => {
await loaders({ expressApp: app })
app.use((err, req, res, next) => {
const logger = req.scope.resolve("logger")
logger.error(err.message)
let statusCode = 500
switch (err.name) {
case "ValidationError":
statusCode = 400
break
case MedusaError.Types.INVALID_DATA:
statusCode = 400
break
case MedusaError.Types.DB_ERROR:
statusCode = 500
break
default:
break
}
res.json(err).status(statusCode)
})
app.listen(PORT, err => {
if (err) {
console.log(err)

View File

@@ -1,4 +1,4 @@
import { createContainer } from "awilix"
import { createContainer, asValue } from "awilix"
import express from "express"
import supertest from "supertest"
import jwt from "jsonwebtoken"
@@ -13,6 +13,11 @@ import config from "../config"
const testApp = express()
const container = createContainer()
container.register({
logger: asValue({
error: () => {},
}),
})
servicesLoader({ container })
expressLoader({ app: testApp })
@@ -29,13 +34,13 @@ apiLoader({ app: testApp })
const supertestRequest = supertest(testApp)
let adminSessionOpts = {
cookieName: "adminSession",
cookieName: "session",
secret: "test",
}
export { adminSessionOpts }
let clientSessionOpts = {
cookieName: "clientSession",
cookieName: "session",
secret: "test",
}
export { clientSessionOpts }

View File

@@ -90,39 +90,39 @@ export const variants = {
empty_variant: emptyVariant,
}
export const ProductVariantServiceMock = {
retrieve: jest.fn().mockImplementation(variantId => {
if (variantId === "1") {
return Promise.resolve(variant1)
}
if (variantId === "2") {
return Promise.resolve(variant2)
}
if (variantId === "3") {
return Promise.resolve(variant3)
}
if (variantId === "4") {
return Promise.resolve(variant4)
}
if (variantId === "invalid_option") {
return Promise.resolve(invalidVariant)
}
if (variantId === "empty_option") {
return Promise.resolve(emptyVariant)
}
return Promise.resolve(undefined)
}),
delete: jest.fn().mockReturnValue(Promise.resolve()),
addOptionValue: jest.fn().mockImplementation((variantId, optionId, value) => {
return Promise.resolve({})
}),
deleteOptionValue: jest.fn().mockImplementation((variantId, optionId) => {
return Promise.resolve({})
}),
}
const mock = jest.fn().mockImplementation(() => {
return {
retrieve: jest.fn().mockImplementation(variantId => {
if (variantId === "1") {
return Promise.resolve(variant1)
}
if (variantId === "2") {
return Promise.resolve(variant2)
}
if (variantId === "3") {
return Promise.resolve(variant3)
}
if (variantId === "4") {
return Promise.resolve(variant4)
}
if (variantId === "invalid_option") {
return Promise.resolve(invalidVariant)
}
if (variantId === "empty_option") {
return Promise.resolve(emptyVariant)
}
return Promise.resolve(undefined)
}),
delete: jest.fn().mockReturnValue(Promise.resolve()),
addOptionValue: jest
.fn()
.mockImplementation((variantId, optionId, value) => {
return Promise.resolve({})
}),
deleteOptionValue: jest.fn().mockImplementation((variantId, optionId) => {
return Promise.resolve({})
}),
}
return ProductVariantServiceMock
})
export default mock

View File

@@ -0,0 +1,11 @@
export const ProductServiceMock = {
createDraft: jest.fn().mockImplementation(data => {
return Promise.resolve(data)
}),
}
const mock = jest.fn().mockImplementation(() => {
return ProductServiceMock
})
export default mock