diff --git a/integration-tests/http/__tests__/product/store/product.spec.ts b/integration-tests/http/__tests__/product/store/product.spec.ts index 0f7cd18f3c..780fc8cba1 100644 --- a/integration-tests/http/__tests__/product/store/product.spec.ts +++ b/integration-tests/http/__tests__/product/store/product.spec.ts @@ -766,8 +766,7 @@ medusaIntegrationTestRunner({ ]) }) - // TODO: This test is flaky for some reason, reenable once that is resolved - it.skip("returns a list of products filtered by variant options", async () => { + it("returns a list of products filtered by variant options", async () => { const response = await api.get( `/store/products?variants.options[option_id]=${product.options[1].id}&variants.options[value]=large` ) diff --git a/packages/modules/product/integration-tests/__tests__/product-module-service/products.spec.ts b/packages/modules/product/integration-tests/__tests__/product-module-service/products.spec.ts index beaf4725eb..77bf616aac 100644 --- a/packages/modules/product/integration-tests/__tests__/product-module-service/products.spec.ts +++ b/packages/modules/product/integration-tests/__tests__/product-module-service/products.spec.ts @@ -264,6 +264,90 @@ moduleIntegrationTestRunner({ ) }) + it("should preserve option and value identity on update", async () => { + const productBefore = await service.retrieveProduct(productTwo.id, { + relations: [ + "images", + "variants", + "options", + "options.values", + "variants.options", + "tags", + "type", + ], + }) + + const updatedProducts = await service.upsertProducts([ + { + id: productBefore.id, + title: "updated title", + options: [ + { + title: "size", + values: ["large", "small"], + }, + { + title: "color", + values: ["red"], + }, + { + title: "material", + values: ["cotton"], + }, + ], + }, + ]) + + expect(updatedProducts).toHaveLength(1) + const product = await service.retrieveProduct(productBefore.id, { + relations: [ + "images", + "variants", + "options", + "options.values", + "variants.options", + "tags", + "type", + ], + }) + + const beforeOption = productBefore.options.find( + (opt) => opt.title === "size" + )! + expect(product.options).toHaveLength(3) + expect(product.options).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + id: beforeOption.id, + title: beforeOption.title, + values: expect.arrayContaining([ + expect.objectContaining({ + id: beforeOption.values[0].id, + value: beforeOption.values[0].value, + }), + ]), + }), + expect.objectContaining({ + title: "color", + values: expect.arrayContaining([ + expect.objectContaining({ + value: "red", + }), + ]), + }), + expect.objectContaining({ + id: expect.any(String), + title: "material", + values: expect.arrayContaining([ + expect.objectContaining({ + value: "cotton", + }), + ]), + }), + ]) + ) + }) + it("should emit events through event bus", async () => { const eventBusSpy = jest.spyOn(MockEventBusService.prototype, "emit") const data = buildProductAndRelationsData({ diff --git a/packages/modules/product/src/services/product-module-service.ts b/packages/modules/product/src/services/product-module-service.ts index 93bbd14e56..e32944fb6b 100644 --- a/packages/modules/product/src/services/product-module-service.ts +++ b/packages/modules/product/src/services/product-module-service.ts @@ -1630,14 +1630,24 @@ export default class ProductModuleService } if (productData.options?.length) { + const dbOptions = await this.productOptionService_.list( + { product_id: productData.id }, + { take: null, relations: ["values"] }, + sharedContext + ) + ;(productData as any).options = productData.options?.map((option) => { + const dbOption = dbOptions.find((o) => o.title === option.title) return { title: option.title, values: option.values?.map((value) => { + const dbValue = dbOption?.values?.find((val) => val.value === value) return { value: value, + ...(dbValue ? { id: dbValue.id } : {}), } }), + ...(dbOption ? { id: dbOption.id } : {}), } }) }