diff --git a/.changeset/tall-beans-shave.md b/.changeset/tall-beans-shave.md new file mode 100644 index 0000000000..74725a9dd7 --- /dev/null +++ b/.changeset/tall-beans-shave.md @@ -0,0 +1,6 @@ +--- +"@medusajs/product": patch +"@medusajs/utils": patch +--- + +fix(product, utils): handle metadata key deletion on product update diff --git a/integration-tests/http/__tests__/product/admin/product.spec.ts b/integration-tests/http/__tests__/product/admin/product.spec.ts index 172b2c8d95..b9144823dc 100644 --- a/integration-tests/http/__tests__/product/admin/product.spec.ts +++ b/integration-tests/http/__tests__/product/admin/product.spec.ts @@ -1912,7 +1912,7 @@ medusaIntegrationTestRunner({ const payload = { metadata: { - "test-key": "", + "test-key": "", // item is deleted by setting to empty string "test-key-2": null, }, } @@ -1924,13 +1924,11 @@ medusaIntegrationTestRunner({ ) expect(response.status).toEqual(200) - expect(response.data.product.metadata).toEqual( - // BREAKING: Metadata updates are all-or-nothing in v2 - { - "test-key": "", - "test-key-2": null, - } - ) + expect(response.data.product.metadata).toEqual({ + // "test-key" is deleted + "test-key-2": null, // updated + "test-key-3": "test-value-3", // preserved + }) }) it("updates products sales channels", async () => { diff --git a/packages/core/utils/src/common/index.ts b/packages/core/utils/src/common/index.ts index b024d57fd8..b3fb55eba4 100644 --- a/packages/core/utils/src/common/index.ts +++ b/packages/core/utils/src/common/index.ts @@ -72,7 +72,6 @@ export * from "./resolve-exports" export * from "./rules" export * from "./selector-constraints-to-string" export * from "./serialize-error" -export * from "./set-metadata" export * from "./simple-hash" export * from "./string-to-select-relation-object" export * from "./stringify-circular" diff --git a/packages/core/utils/src/common/set-metadata.ts b/packages/core/utils/src/common/set-metadata.ts deleted file mode 100644 index 4dc5f6d42a..0000000000 --- a/packages/core/utils/src/common/set-metadata.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { MedusaError } from "./errors" - -/** - * Dedicated method to set metadata. - * @param obj - the entity to apply metadata to. - * @param metadata - the metadata to set - * @return resolves to the updated result. - */ -export function setMetadata( - obj: { metadata: Record | null }, - metadata: Record -): Record { - const existing = obj.metadata || {} - const newData = {} - - for (const [key, value] of Object.entries(metadata)) { - if (typeof key !== "string") { - throw new MedusaError( - MedusaError.Types.INVALID_ARGUMENT, - "Key type is invalid. Metadata keys must be strings" - ) - } - - /** - * We reserve the empty string as a way to delete a key. - * If the value is an empty string, we don't - * set it, and if it exists in the existing metadata, we - * unset the field. - */ - if (value === "") { - if (key in existing) { - delete existing[key] - } - - continue - } - - newData[key] = value - } - - return { - ...existing, - ...newData, - } -} diff --git a/packages/modules/product/src/repositories/product.ts b/packages/modules/product/src/repositories/product.ts index 1c4990c8f4..fc1e4ee220 100644 --- a/packages/modules/product/src/repositories/product.ts +++ b/packages/modules/product/src/repositories/product.ts @@ -6,6 +6,8 @@ import { buildQuery, DALUtils, MedusaError, + isPresent, + mergeMetadata, } from "@medusajs/framework/utils" import { SqlEntityManager, wrap } from "@mikro-orm/postgresql" @@ -148,6 +150,10 @@ export class ProductRepository extends DALUtils.mikroOrmBaseRepositoryFactory( ) } + if (isPresent(productToUpdate.metadata)) { + productToUpdate.metadata = mergeMetadata(product.metadata ?? {}, productToUpdate.metadata) + } + wrappedProduct.assign(productToUpdate) }