diff --git a/.changeset/tough-mails-vanish.md b/.changeset/tough-mails-vanish.md new file mode 100644 index 0000000000..c8c7a26fc6 --- /dev/null +++ b/.changeset/tough-mails-vanish.md @@ -0,0 +1,6 @@ +--- +"@medusajs/medusa": patch +"@medusajs/client-types": patch +--- + +feat(medusa): Add `metadata` to `StorePostCartsCartLineItemsItemReq` diff --git a/integration-tests/api/__tests__/store/cart/cart.js b/integration-tests/api/__tests__/store/cart/cart.js index 762aa551b0..8d0d22b93f 100644 --- a/integration-tests/api/__tests__/store/cart/cart.js +++ b/integration-tests/api/__tests__/store/cart/cart.js @@ -769,6 +769,9 @@ describe("/store/carts", () => { "/store/carts/test-cart-3/line-items/test-item3/", { quantity: 3, + metadata: { + another: "prop", + }, }, { withCredentials: true } ) @@ -782,6 +785,10 @@ describe("/store/carts", () => { variant_id: "test-variant-sale-cg", quantity: 3, adjustments: [], + metadata: { + "some-existing": "prop", + another: "prop", + }, }), ]) ) diff --git a/integration-tests/api/helpers/cart-seeder.js b/integration-tests/api/helpers/cart-seeder.js index 9bfbec1f3d..210686384f 100644 --- a/integration-tests/api/helpers/cart-seeder.js +++ b/integration-tests/api/helpers/cart-seeder.js @@ -938,6 +938,7 @@ module.exports = async (dataSource, data = {}) => { quantity: 1, variant_id: "test-variant-sale-cg", cart_id: "test-cart-3", + metadata: { "some-existing": "prop" }, }) await manager.save(li3) } diff --git a/packages/generated/client-types/src/lib/models/StorePostCartsCartLineItemsItemReq.ts b/packages/generated/client-types/src/lib/models/StorePostCartsCartLineItemsItemReq.ts index 786759d62f..add0438a6e 100644 --- a/packages/generated/client-types/src/lib/models/StorePostCartsCartLineItemsItemReq.ts +++ b/packages/generated/client-types/src/lib/models/StorePostCartsCartLineItemsItemReq.ts @@ -8,4 +8,8 @@ export interface StorePostCartsCartLineItemsItemReq { * The quantity to set the Line Item to. */ quantity: number + /** + * An optional key-value map with additional details about the Line Item. If omitted, the metadata will remain unchanged." + */ + metadata?: Record } diff --git a/packages/medusa/src/api/routes/store/carts/__tests__/update-line-item.js b/packages/medusa/src/api/routes/store/carts/__tests__/update-line-item.js index dd3a67031c..2893b7d968 100644 --- a/packages/medusa/src/api/routes/store/carts/__tests__/update-line-item.js +++ b/packages/medusa/src/api/routes/store/carts/__tests__/update-line-item.js @@ -73,7 +73,7 @@ describe("POST /store/carts/:id/line-items/:line_id", () => { jest.clearAllMocks() }) - it("calls CartService create", () => { + it("calls CartService removeLineItem", () => { expect(CartServiceMock.removeLineItem).toHaveBeenCalledTimes(1) expect(CartServiceMock.removeLineItem).toHaveBeenCalledWith( IdMap.getId("fr-cart"), @@ -89,4 +89,142 @@ describe("POST /store/carts/:id/line-items/:line_id", () => { expect(subject.body.cart.id).toEqual(IdMap.getId("fr-cart")) }) }) + + describe("updates metadata if included in request body", () => { + let subject + + beforeAll(async () => { + const cartId = IdMap.getId("fr-cart") + const lineId = IdMap.getId("existingLine") + subject = await request( + "POST", + `/store/carts/${cartId}/line-items/${lineId}`, + { + payload: { + quantity: 3, + metadata: { + potato: "tomato", + }, + }, + } + ) + }) + + afterAll(() => { + jest.clearAllMocks() + }) + + it("calls CartService updateLineItem", () => { + expect(CartServiceMock.updateLineItem).toHaveBeenCalledTimes(1) + expect(CartServiceMock.updateLineItem).toHaveBeenCalledWith( + IdMap.getId("fr-cart"), + IdMap.getId("existingLine"), + { + metadata: { + potato: "tomato", + }, + quantity: 3, + region_id: expect.any(String), + variant_id: expect.any(String), + } + ) + }) + + it("returns 200", () => { + expect(subject.status).toEqual(200) + }) + + it("returns the cart", () => { + expect(subject.body.cart.id).toEqual(IdMap.getId("fr-cart")) + }) + }) + + describe("uses empty metadata if no metadata in request body", () => { + let subject + + beforeAll(async () => { + const cartId = IdMap.getId("cartLineItemMetadata") + const lineId = IdMap.getId("lineWithMetadata") + subject = await request( + "POST", + `/store/carts/${cartId}/line-items/${lineId}`, + { + payload: { + quantity: 3, + }, + } + ) + }) + + afterAll(() => { + jest.clearAllMocks() + }) + + it("calls CartService updateLineItem", () => { + expect(CartServiceMock.updateLineItem).toHaveBeenCalledTimes(1) + expect(CartServiceMock.updateLineItem).toHaveBeenCalledWith( + IdMap.getId("cartLineItemMetadata"), + IdMap.getId("lineWithMetadata"), + { + metadata: {}, + quantity: 3, + region_id: expect.any(String), + variant_id: expect.any(String), + } + ) + }) + + it("returns 200", () => { + expect(subject.status).toEqual(200) + }) + + it("returns the cart", () => { + expect(subject.body.cart.id).toEqual(IdMap.getId("cartLineItemMetadata")) + }) + }) + + describe("uses metadata if in request body", () => { + let subject + + beforeAll(async () => { + const cartId = IdMap.getId("cartLineItemMetadata") + const lineId = IdMap.getId("lineWithMetadata") + subject = await request( + "POST", + `/store/carts/${cartId}/line-items/${lineId}`, + { + payload: { + quantity: 3, + metadata: { test: "this" }, + }, + } + ) + }) + + afterAll(() => { + jest.clearAllMocks() + }) + + it("calls CartService updateLineItem", () => { + expect(CartServiceMock.updateLineItem).toHaveBeenCalledTimes(1) + expect(CartServiceMock.updateLineItem).toHaveBeenCalledWith( + IdMap.getId("cartLineItemMetadata"), + IdMap.getId("lineWithMetadata"), + { + metadata: { test: "this" }, + quantity: 3, + region_id: expect.any(String), + variant_id: expect.any(String), + } + ) + }) + + it("returns 200", () => { + expect(subject.status).toEqual(200) + }) + + it("returns the cart", () => { + expect(subject.body.cart.id).toEqual(IdMap.getId("cartLineItemMetadata")) + }) + }) }) diff --git a/packages/medusa/src/api/routes/store/carts/update-line-item.ts b/packages/medusa/src/api/routes/store/carts/update-line-item.ts index c6a711a279..33c1160680 100644 --- a/packages/medusa/src/api/routes/store/carts/update-line-item.ts +++ b/packages/medusa/src/api/routes/store/carts/update-line-item.ts @@ -1,4 +1,4 @@ -import { IsInt } from "class-validator" +import { IsInt, IsOptional } from "class-validator" import { MedusaError } from "medusa-core-utils" import { EntityManager } from "typeorm" import { defaultStoreCartFields, defaultStoreCartRelations } from "." @@ -89,7 +89,7 @@ export default async (req, res) => { variant_id: existing.variant.id, region_id: cart.region_id, quantity: validated.quantity, - metadata: existing.metadata || {}, + metadata: validated.metadata || {}, } await cartService @@ -124,8 +124,14 @@ export default async (req, res) => { * quantity: * type: number * description: The quantity to set the Line Item to. + * metadata: + * type: object + * description: An optional key-value map with additional details about the Line Item. If omitted, the metadata will remain unchanged." */ export class StorePostCartsCartLineItemsItemReq { @IsInt() quantity: number + + @IsOptional() + metadata?: Record | undefined }