fix: use service methods instead of entity manager for product updates
This commit is contained in:
2 files changed
+87
-41
No files matched your search
+33
-5
@@ -180,6 +180,22 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
productTwo = res[1]
|
productTwo = res[1]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should update multiple products", async () => {
|
||||||
|
await service.upsertProducts([
|
||||||
|
{ id: productOne.id, title: "updated title 1" },
|
||||||
|
{ id: productTwo.id, title: "updated title 2" },
|
||||||
|
])
|
||||||
|
|
||||||
|
const products = await service.listProducts(
|
||||||
|
{ id: [productOne.id, productTwo.id] },
|
||||||
|
{ relations: ["*"] }
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(products).toHaveLength(2)
|
||||||
|
expect(products[0].title).toEqual("updated title 1")
|
||||||
|
expect(products[1].title).toEqual("updated title 2")
|
||||||
|
})
|
||||||
|
|
||||||
it("should update a product and upsert relations that are not created yet", async () => {
|
it("should update a product and upsert relations that are not created yet", async () => {
|
||||||
const tags = await service.createProductTags([{ value: "tag-1" }])
|
const tags = await service.createProductTags([{ value: "tag-1" }])
|
||||||
const data = buildProductAndRelationsData({
|
const data = buildProductAndRelationsData({
|
||||||
@@ -839,13 +855,25 @@ moduleIntegrationTestRunner<IProductModuleService>({
|
|||||||
|
|
||||||
expect(product.options).toHaveLength(1)
|
expect(product.options).toHaveLength(1)
|
||||||
expect(product.options[0].title).toEqual("material")
|
expect(product.options[0].title).toEqual("material")
|
||||||
expect(product.options[0].values).toHaveLength(2)
|
expect(product.options[0].values).toEqual(
|
||||||
expect(product.options[0].values[0].value).toEqual("cotton")
|
expect.arrayContaining([
|
||||||
expect(product.options[0].values[1].value).toEqual("silk")
|
expect.objectContaining({
|
||||||
|
value: "cotton",
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
value: "silk",
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
expect(product.variants).toHaveLength(1)
|
expect(product.variants).toHaveLength(1)
|
||||||
expect(product.variants[0].options).toHaveLength(1)
|
expect(product.variants[0].options).toEqual(
|
||||||
expect(product.variants[0].options[0].value).toEqual("cotton")
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
value: "cotton",
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should throw an error when some tag id does not exist", async () => {
|
it("should throw an error when some tag id does not exist", async () => {
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ import {
|
|||||||
removeUndefined,
|
removeUndefined,
|
||||||
toHandle,
|
toHandle,
|
||||||
} from "@medusajs/framework/utils"
|
} from "@medusajs/framework/utils"
|
||||||
import { wrap } from "@mikro-orm/core"
|
|
||||||
import {
|
import {
|
||||||
UpdateCategoryInput,
|
UpdateCategoryInput,
|
||||||
UpdateCollectionInput,
|
UpdateCollectionInput,
|
||||||
@@ -1663,44 +1662,22 @@ export default class ProductModuleService
|
|||||||
|
|
||||||
const products = await this.productService_.list(
|
const products = await this.productService_.list(
|
||||||
{ id: normalizedProducts.map((p) => p.id) },
|
{ id: normalizedProducts.map((p) => p.id) },
|
||||||
{ relations: ["*"] },
|
{
|
||||||
|
relations: [
|
||||||
|
"images",
|
||||||
|
"variants",
|
||||||
|
"variants.options",
|
||||||
|
"options",
|
||||||
|
"options.values",
|
||||||
|
],
|
||||||
|
}, // loading all relations is the only way for productService_.update to update deep relations, otherwise it triggers INSERTS for all relations
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
const productsMap = new Map(products.map((p) => [p.id, p]))
|
const productsMap = new Map(products.map((p) => [p.id, p]))
|
||||||
|
|
||||||
for (const normalizedProduct of normalizedProducts) {
|
await this.productService_.update(
|
||||||
const update = normalizedProduct as any
|
normalizedProducts.map((normalizedProduct: any) => {
|
||||||
const product = productsMap.get(update.id)!
|
const update = { ...normalizedProduct }
|
||||||
|
|
||||||
// Assign the options first, so they'll be available for the variants loop below
|
|
||||||
if (update.options) {
|
|
||||||
wrap(product).assign({ options: update.options })
|
|
||||||
delete update.options // already assigned above, so no longer necessary
|
|
||||||
}
|
|
||||||
|
|
||||||
if (update.variants) {
|
|
||||||
ProductModuleService.validateVariantOptions(
|
|
||||||
update.variants,
|
|
||||||
product.options
|
|
||||||
)
|
|
||||||
|
|
||||||
update.variants.forEach((variant: any) => {
|
|
||||||
if (variant.options) {
|
|
||||||
variant.options = Object.entries(variant.options).map(
|
|
||||||
([key, value]) => {
|
|
||||||
const productOption = product.options.find(
|
|
||||||
(option) => option.title === key
|
|
||||||
)!
|
|
||||||
const productOptionValue = productOption.values?.find(
|
|
||||||
(optionValue) => optionValue.value === value
|
|
||||||
)!
|
|
||||||
return productOptionValue.id
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (update.tags) {
|
if (update.tags) {
|
||||||
update.tags = update.tags.map((t: { id: string }) => t.id)
|
update.tags = update.tags.map((t: { id: string }) => t.id)
|
||||||
}
|
}
|
||||||
@@ -1714,9 +1691,50 @@ export default class ProductModuleService
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
wrap(product!).assign(update)
|
// There's an integration test that checks that metadata updates are all-or-nothing
|
||||||
|
// but productService_.update merges instead, so we update the field directly
|
||||||
|
productsMap.get(normalizedProduct.id)!.metadata = update.metadata
|
||||||
|
delete update.metadata
|
||||||
|
|
||||||
|
delete update.variants // variants are updated in the next step
|
||||||
|
|
||||||
|
return update
|
||||||
|
}),
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
// We update variants in a second step because we need the options to be updated first
|
||||||
|
await this.productService_.update(
|
||||||
|
normalizedProducts
|
||||||
|
.filter((p) => p.variants)
|
||||||
|
.map((normalizedProduct: any) => {
|
||||||
|
const update = {
|
||||||
|
id: normalizedProduct.id,
|
||||||
|
variants: normalizedProduct.variants,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const options = productsMap.get(normalizedProduct.id)!.options
|
||||||
|
ProductModuleService.validateVariantOptions(update.variants, options)
|
||||||
|
|
||||||
|
update.variants.forEach((variant: any) => {
|
||||||
|
if (variant.options) {
|
||||||
|
variant.options = Object.entries(variant.options).map(
|
||||||
|
([key, value]) => {
|
||||||
|
const option = options.find((option) => option.title === key)!
|
||||||
|
const optionValue = option.values?.find(
|
||||||
|
(optionValue) => optionValue.value === value
|
||||||
|
)!
|
||||||
|
return optionValue.id
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return update
|
||||||
|
}),
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
return products
|
return products
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user