feat: Line Items API Routes (#6478)

**What**
- `POST /store/carts/:id/line-items`
- `POST /store/carts/:id/line-items/:id`
- `DELETE /store/carts/:id/line-items/:id`

**Outstanding**
- Integration tests
- Module integrations: Payment, Fulfillment, Promotions

Depends on #6475 and #6449.
This commit is contained in:
Oli Juhl
2024-02-27 12:47:00 +00:00
committed by GitHub
parent f5c2256286
commit 3ee0f599c1
30 changed files with 937 additions and 208 deletions
@@ -1,7 +1,12 @@
import {
addToCartWorkflow,
createCartWorkflow,
deleteLineItemsStepId,
deleteLineItemsWorkflow,
findOrCreateCustomerStepId,
updateLineItemInCartWorkflow,
updateLineItemsStepId,
} from "@medusajs/core-flows"
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import {
@@ -409,4 +414,260 @@ describe("Carts workflows", () => {
])
})
})
describe("updateLineItemInCartWorkflow", () => {
it("should update item in cart", async () => {
const [product] = await productModule.create([
{
title: "Test product",
variants: [
{
title: "Test variant",
},
],
},
])
const priceSet = await pricingModule.create({
prices: [
{
amount: 3000,
currency_code: "usd",
},
],
})
await remoteLink.create([
{
productService: {
variant_id: product.variants[0].id,
},
pricingService: {
price_set_id: priceSet.id,
},
},
])
let cart = await cartModuleService.create({
currency_code: "usd",
items: [
{
variant_id: product.variants[0].id,
quantity: 1,
unit_price: 5000,
title: "Test item",
},
],
})
cart = await cartModuleService.retrieve(cart.id, {
select: ["id", "region_id", "currency_code"],
relations: ["items", "items.variant_id", "items.metadata"],
})
const item = cart.items?.[0]!
await updateLineItemInCartWorkflow(appContainer).run({
input: {
cart,
item,
update: {
metadata: {
foo: "bar",
},
quantity: 2,
},
},
throwOnError: false,
})
const updatedItem = await cartModuleService.retrieveLineItem(item.id)
expect(updatedItem).toEqual(
expect.objectContaining({
id: item.id,
unit_price: 3000,
quantity: 2,
title: "Test item",
})
)
})
describe("compensation", () => {
it("should revert line item update to original state", async () => {
expect.assertions(2)
const workflow = updateLineItemInCartWorkflow(appContainer)
workflow.appendAction("throw", updateLineItemsStepId, {
invoke: async function failStep() {
throw new Error(`Failed to update something after line items`)
},
})
const [product] = await productModule.create([
{
title: "Test product",
variants: [
{
title: "Test variant",
},
],
},
])
let cart = await cartModuleService.create({
currency_code: "usd",
items: [
{
variant_id: product.variants[0].id,
quantity: 1,
unit_price: 3000,
title: "Test item",
},
],
})
const priceSet = await pricingModule.create({
prices: [
{
amount: 5000,
currency_code: "usd",
},
],
})
await remoteLink.create([
{
productService: {
variant_id: product.variants[0].id,
},
pricingService: {
price_set_id: priceSet.id,
},
},
])
cart = await cartModuleService.retrieve(cart.id, {
select: ["id", "region_id", "currency_code"],
relations: ["items", "items.variant_id", "items.metadata"],
})
const item = cart.items?.[0]!
const { errors } = await workflow.run({
input: {
cart,
item,
update: {
metadata: {
foo: "bar",
},
title: "Test item updated",
quantity: 2,
},
},
throwOnError: false,
})
expect(errors).toEqual([
{
action: "throw",
handlerType: "invoke",
error: new Error(`Failed to update something after line items`),
},
])
const updatedItem = await cartModuleService.retrieveLineItem(item.id)
expect(updatedItem).toEqual(
expect.objectContaining({
id: item.id,
unit_price: 3000,
quantity: 1,
title: "Test item",
})
)
})
})
})
describe("deleteLineItems", () => {
it("should delete items in cart", async () => {
const cart = await cartModuleService.create({
currency_code: "usd",
items: [
{
quantity: 1,
unit_price: 5000,
title: "Test item",
},
],
})
const items = await cartModuleService.listLineItems({ cart_id: cart.id })
await deleteLineItemsWorkflow(appContainer).run({
input: {
ids: items.map((i) => i.id),
},
throwOnError: false,
})
const [deletedItem] = await cartModuleService.listLineItems({
id: items.map((i) => i.id),
})
expect(deletedItem).toBeUndefined()
})
describe("compensation", () => {
it("should restore line item if delete fails", async () => {
const workflow = deleteLineItemsWorkflow(appContainer)
workflow.appendAction("throw", deleteLineItemsStepId, {
invoke: async function failStep() {
throw new Error(`Failed to do something after deleting line items`)
},
})
const cart = await cartModuleService.create({
currency_code: "usd",
items: [
{
quantity: 1,
unit_price: 3000,
title: "Test item",
},
],
})
const items = await cartModuleService.listLineItems({
cart_id: cart.id,
})
const { errors } = await workflow.run({
input: {
ids: items.map((i) => i.id),
},
throwOnError: false,
})
expect(errors).toEqual([
{
action: "throw",
handlerType: "invoke",
error: new Error(
`Failed to do something after deleting line items`
),
},
])
const updatedItem = await cartModuleService.retrieveLineItem(
items[0].id
)
expect(updatedItem).not.toBeUndefined()
})
})
})
})