adds get cart and create cart endpoints; adds in
This commit is contained in:
@@ -0,0 +1,151 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { CartServiceMock } from "../../../../../services/__mocks__/cart"
|
||||
import { LineItemServiceMock } from "../../../../../services/__mocks__/line-item"
|
||||
|
||||
describe("POST /store/carts", () => {
|
||||
describe("successfully creates a cart", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request("POST", `/store/carts`, {
|
||||
payload: {
|
||||
region_id: IdMap.getId("testRegion"),
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("calls CartService create", () => {
|
||||
expect(CartServiceMock.create).toHaveBeenCalledTimes(1)
|
||||
expect(CartServiceMock.create).toHaveBeenCalledWith({
|
||||
region_id: IdMap.getId("testRegion"),
|
||||
})
|
||||
})
|
||||
|
||||
it("returns 201", () => {
|
||||
expect(subject.status).toEqual(201)
|
||||
})
|
||||
|
||||
it("returns the cart", () => {
|
||||
expect(subject.body._id).toEqual(IdMap.getId("regionCart"))
|
||||
})
|
||||
})
|
||||
|
||||
describe("handles failed create operation", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request("POST", `/store/carts`, {
|
||||
payload: {
|
||||
region_id: IdMap.getId("fail"),
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("returns error", () => {
|
||||
expect(subject.status).toEqual(400)
|
||||
expect(subject.body.message).toEqual("Region not found")
|
||||
})
|
||||
})
|
||||
|
||||
describe("returns invalid data if region_id is not set", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request("POST", `/store/carts`)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("returns error", () => {
|
||||
expect(subject.status).toEqual(400)
|
||||
})
|
||||
})
|
||||
|
||||
describe("creates cart with line items", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request("POST", `/store/carts`, {
|
||||
payload: {
|
||||
region_id: IdMap.getId("testRegion"),
|
||||
items: [
|
||||
{
|
||||
variant_id: IdMap.getId("testVariant"),
|
||||
quantity: 3,
|
||||
},
|
||||
{
|
||||
variant_id: IdMap.getId("testVariant1"),
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("returns 201", () => {
|
||||
expect(subject.status).toEqual(201)
|
||||
})
|
||||
|
||||
it("calls line item generate", () => {
|
||||
expect(LineItemServiceMock.generate).toHaveBeenCalledTimes(2)
|
||||
expect(LineItemServiceMock.generate).toHaveBeenCalledWith(
|
||||
IdMap.getId("testVariant"),
|
||||
3,
|
||||
IdMap.getId("testRegion")
|
||||
)
|
||||
expect(LineItemServiceMock.generate).toHaveBeenCalledWith(
|
||||
IdMap.getId("testVariant1"),
|
||||
1,
|
||||
IdMap.getId("testRegion")
|
||||
)
|
||||
})
|
||||
|
||||
it("returns cart", () => {
|
||||
expect(subject.body._id).toEqual(IdMap.getId("regionCart"))
|
||||
})
|
||||
})
|
||||
|
||||
describe("fails if line items are not formatted correctly", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request("POST", `/store/carts`, {
|
||||
payload: {
|
||||
region_id: IdMap.getId("testRegion"),
|
||||
items: [
|
||||
{
|
||||
quantity: 3,
|
||||
},
|
||||
{
|
||||
variant_id: IdMap.getId("testVariant1"),
|
||||
quantity: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("returns 400", () => {
|
||||
expect(subject.status).toEqual(400)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,49 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
import { request } from "../../../../../helpers/test-request"
|
||||
import { CartServiceMock } from "../../../../../services/__mocks__/cart"
|
||||
|
||||
describe("GET /store/carts", () => {
|
||||
describe("successfully gets a cart", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request("GET", `/store/carts/${IdMap.getId("emptyCart")}`)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("calls get product from productSerice", () => {
|
||||
expect(CartServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(CartServiceMock.retrieve).toHaveBeenCalledWith(
|
||||
IdMap.getId("emptyCart")
|
||||
)
|
||||
})
|
||||
|
||||
it("returns products", () => {
|
||||
expect(subject.body._id).toEqual(IdMap.getId("emptyCart"))
|
||||
})
|
||||
})
|
||||
|
||||
describe("returns 404 on undefined cart", () => {
|
||||
let subject
|
||||
|
||||
beforeAll(async () => {
|
||||
subject = await request("GET", `/store/carts/none`)
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("calls get product from productSerice", () => {
|
||||
expect(CartServiceMock.retrieve).toHaveBeenCalledTimes(1)
|
||||
expect(CartServiceMock.retrieve).toHaveBeenCalledWith("none")
|
||||
})
|
||||
|
||||
it("returns products", () => {
|
||||
expect(subject.status).toEqual(404)
|
||||
})
|
||||
})
|
||||
})
|
||||
42
packages/medusa/src/api/routes/store/carts/create-cart.js
Normal file
42
packages/medusa/src/api/routes/store/carts/create-cart.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import { Validator, MedusaError } from "medusa-core-utils"
|
||||
|
||||
export default async (req, res) => {
|
||||
const schema = Validator.object().keys({
|
||||
region_id: Validator.string().required(),
|
||||
items: Validator.array()
|
||||
.items({
|
||||
variant_id: Validator.string().required(),
|
||||
quantity: Validator.number().required(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
|
||||
const { value, error } = schema.validate(req.body)
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.details)
|
||||
}
|
||||
|
||||
try {
|
||||
const lineItemService = req.scope.resolve("lineItemService")
|
||||
const cartService = req.scope.resolve("cartService")
|
||||
let cart = await cartService.create({ region_id: value.region_id })
|
||||
|
||||
if (value.items) {
|
||||
await Promise.all(
|
||||
value.items.map(async i => {
|
||||
const lineItem = await lineItemService.generate(
|
||||
i.variant_id,
|
||||
i.quantity,
|
||||
value.region_id
|
||||
)
|
||||
await cartService.addLineItem(cart._id, lineItem)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
cart = await cartService.retrieve(cart._id)
|
||||
res.status(201).json(cart)
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
13
packages/medusa/src/api/routes/store/carts/get-cart.js
Normal file
13
packages/medusa/src/api/routes/store/carts/get-cart.js
Normal file
@@ -0,0 +1,13 @@
|
||||
export default async (req, res) => {
|
||||
const { cartId } = req.params
|
||||
|
||||
const cartService = req.scope.resolve("cartService")
|
||||
const cart = await cartService.retrieve(cartId)
|
||||
|
||||
if (!cart) {
|
||||
res.sendStatus(404)
|
||||
return
|
||||
}
|
||||
|
||||
res.json(cart)
|
||||
}
|
||||
13
packages/medusa/src/api/routes/store/carts/index.js
Normal file
13
packages/medusa/src/api/routes/store/carts/index.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Router } from "express"
|
||||
import middlewares from "../../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
|
||||
export default app => {
|
||||
app.use("/carts", route)
|
||||
|
||||
route.get("/:cartId", middlewares.wrap(require("./get-cart").default))
|
||||
route.post("/", middlewares.wrap(require("./create-cart").default))
|
||||
|
||||
return app
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Router } from "express"
|
||||
import productRoutes from './products'
|
||||
import productRoutes from "./products"
|
||||
import cartRoutes from "./carts"
|
||||
import middlewares from "../../middlewares"
|
||||
|
||||
const route = Router()
|
||||
@@ -8,6 +9,7 @@ export default app => {
|
||||
app.use("/store", route)
|
||||
|
||||
productRoutes(route)
|
||||
cartRoutes(route)
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
@@ -14,14 +14,14 @@ class CartModel extends BaseModel {
|
||||
|
||||
static schema = {
|
||||
email: { type: String },
|
||||
billing_address: { type: AddressSchema },
|
||||
shipping_address: { type: AddressSchema },
|
||||
billing_address: { type: AddressSchema, default: {} },
|
||||
shipping_address: { type: AddressSchema, default: {} },
|
||||
items: { type: [LineItemSchema], default: [] },
|
||||
region_id: { type: String },
|
||||
region_id: { type: String, required: true },
|
||||
discounts: { type: [String], default: [] },
|
||||
customer_id: { type: String },
|
||||
payment_method: { type: PaymentMethodSchema },
|
||||
shipping_methods: { type: [ShippingMethodSchema] },
|
||||
customer_id: { type: String, default: "" },
|
||||
payment_method: { type: PaymentMethodSchema, default: {} },
|
||||
shipping_methods: { type: [ShippingMethodSchema], default: [] },
|
||||
metadata: { type: mongoose.Schema.Types.Mixed, default: {} },
|
||||
}
|
||||
}
|
||||
|
||||
43
packages/medusa/src/services/__mocks__/cart.js
Normal file
43
packages/medusa/src/services/__mocks__/cart.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
|
||||
export const carts = {
|
||||
emptyCart: {
|
||||
_id: IdMap.getId("emptyCart"),
|
||||
items: [],
|
||||
},
|
||||
regionCart: {
|
||||
_id: IdMap.getId("regionCart"),
|
||||
name: "Product 1",
|
||||
region_id: IdMap.getId("testRegion"),
|
||||
},
|
||||
}
|
||||
|
||||
export const CartServiceMock = {
|
||||
create: jest.fn().mockImplementation(data => {
|
||||
if (data.region_id === IdMap.getId("testRegion")) {
|
||||
return Promise.resolve(carts.regionCart)
|
||||
}
|
||||
if (data.region_id === IdMap.getId("fail")) {
|
||||
throw new MedusaError(MedusaError.Types.INVALID_DATA, "Region not found")
|
||||
}
|
||||
}),
|
||||
retrieve: jest.fn().mockImplementation(cartId => {
|
||||
if (cartId === IdMap.getId("regionCart")) {
|
||||
return Promise.resolve(carts.regionCart)
|
||||
}
|
||||
if (cartId === IdMap.getId("emptyCart")) {
|
||||
return Promise.resolve(carts.emptyCart)
|
||||
}
|
||||
return Promise.resolve(undefined)
|
||||
}),
|
||||
addLineItem: jest.fn().mockImplementation((cartId, lineItem) => {
|
||||
return Promise.resolve()
|
||||
}),
|
||||
}
|
||||
|
||||
const mock = jest.fn().mockImplementation(() => {
|
||||
return CartServiceMock
|
||||
})
|
||||
|
||||
export default mock
|
||||
34
packages/medusa/src/services/__mocks__/line-item.js
Normal file
34
packages/medusa/src/services/__mocks__/line-item.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import { IdMap } from "medusa-test-utils"
|
||||
|
||||
export const LineItemServiceMock = {
|
||||
generate: jest.fn().mockImplementation((variantId, quantity, regionId) => {
|
||||
return Promise.resolve({
|
||||
content: {
|
||||
variant: {
|
||||
_id: variantId,
|
||||
},
|
||||
product: {
|
||||
_id: `p_${variantId}`,
|
||||
},
|
||||
quantity: 1,
|
||||
unit_price: 100,
|
||||
},
|
||||
quantity,
|
||||
})
|
||||
}),
|
||||
validate: jest.fn().mockImplementation(cartId => {
|
||||
if (cartId === IdMap.getId("regionCart")) {
|
||||
return Promise.resolve(carts.regionCart)
|
||||
}
|
||||
if (cartId === IdMap.getId("emptyCart")) {
|
||||
return Promise.resolve(carts.emptyCart)
|
||||
}
|
||||
return Promise.resolve(undefined)
|
||||
}),
|
||||
}
|
||||
|
||||
const mock = jest.fn().mockImplementation(() => {
|
||||
return LineItemServiceMock
|
||||
})
|
||||
|
||||
export default mock
|
||||
@@ -65,6 +65,28 @@ describe("CartService", () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe("create", () => {
|
||||
const cartService = new CartService({
|
||||
cartModel: CartModelMock,
|
||||
regionService: RegionServiceMock,
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("successfully creates a cart", async () => {
|
||||
await cartService.create({
|
||||
region_id: IdMap.getId("testRegion"),
|
||||
})
|
||||
|
||||
expect(CartModelMock.create).toHaveBeenCalledTimes(1)
|
||||
expect(CartModelMock.create).toHaveBeenCalledWith({
|
||||
region_id: IdMap.getId("testRegion"),
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("addLineItem", () => {
|
||||
const cartService = new CartService({
|
||||
cartModel: CartModelMock,
|
||||
|
||||
@@ -194,6 +194,37 @@ class CartService extends BaseService {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cart.
|
||||
* @param {Object} data - the data to create the cart with
|
||||
* @return {Promise} the result of the create operation
|
||||
*/
|
||||
async create(data) {
|
||||
const { region_id } = data
|
||||
if (!region_id) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`A region_id must be provided when creating a cart`
|
||||
)
|
||||
}
|
||||
|
||||
const region = await this.regionService_.retrieve(region_id)
|
||||
if (!region) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`A region with id: ${region_id} does not exist`
|
||||
)
|
||||
}
|
||||
|
||||
return this.cartModel_
|
||||
.create({
|
||||
region_id,
|
||||
})
|
||||
.catch(err => {
|
||||
throw new MedusaError(MedusaError.Types.DB_ERROR, err.message)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Decorates a cart.
|
||||
* @param {Cart} cart - the cart to decorate.
|
||||
|
||||
Reference in New Issue
Block a user