From a61d1825eaef4bece20704fe6ae21d9da841b2f2 Mon Sep 17 00:00:00 2001 From: William Bouchard <46496014+willbouch@users.noreply.github.com> Date: Tue, 14 Oct 2025 08:08:20 -0400 Subject: [PATCH] fix(utils,core-flows): fix import erasing tags, categories and others (#13724) * fix(utils,core-flows): fix import erasins tags, categories and others * Create pink-pumpkins-sin.md * fix changeset * fix tests --- .changeset/pink-pumpkins-sin.md | 6 ++++ .../product/__tests__/csv-normalizer.spec.ts | 34 +++++-------------- .../core/utils/src/product/csv-normalizer.ts | 27 +++++++++------ 3 files changed, 32 insertions(+), 35 deletions(-) create mode 100644 .changeset/pink-pumpkins-sin.md diff --git a/.changeset/pink-pumpkins-sin.md b/.changeset/pink-pumpkins-sin.md new file mode 100644 index 0000000000..b950a01791 --- /dev/null +++ b/.changeset/pink-pumpkins-sin.md @@ -0,0 +1,6 @@ +--- +"@medusajs/core-flows": patch +"@medusajs/utils": patch +--- + +fix(utils,core-flows): fix import erasing tags, categories and others diff --git a/packages/core/utils/src/product/__tests__/csv-normalizer.spec.ts b/packages/core/utils/src/product/__tests__/csv-normalizer.spec.ts index d6112d6fc5..98be635379 100644 --- a/packages/core/utils/src/product/__tests__/csv-normalizer.spec.ts +++ b/packages/core/utils/src/product/__tests__/csv-normalizer.spec.ts @@ -26,7 +26,6 @@ describe("CSV processor", () => { { "toCreate": { "sweatshirt": { - "categories": [], "description": "Reimagine the feeling of a classic sweatshirt. With our cotton sweatshirt, everyday essentials no longer have to be ordinary.", "discountable": true, "handle": "sweatshirt", @@ -52,7 +51,6 @@ describe("CSV processor", () => { }, ], "status": "published", - "tags": [], "thumbnail": "https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatshirt-vintage-front.png", "title": "Medusa Sweatshirt", "variants": [ @@ -99,7 +97,6 @@ describe("CSV processor", () => { { "toCreate": { "sweatshirt": { - "categories": [], "description": "Reimagine the feeling of a classic sweatshirt. With our cotton sweatshirt, everyday essentials no longer have to be ordinary.", "discountable": true, "handle": "sweatshirt", @@ -127,7 +124,6 @@ describe("CSV processor", () => { }, ], "status": "published", - "tags": [], "thumbnail": "https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatshirt-vintage-front.png", "title": "Medusa Sweatshirt", "variants": [ @@ -216,7 +212,6 @@ describe("CSV processor", () => { { "toCreate": { "sweatshirt": { - "categories": [], "description": "Reimagine the feeling of a classic sweatshirt. With our cotton sweatshirt, everyday essentials no longer have to be ordinary.", "discountable": true, "handle": "sweatshirt", @@ -251,7 +246,6 @@ describe("CSV processor", () => { }, ], "status": "published", - "tags": [], "thumbnail": "https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatshirt-vintage-front.png", "title": "Medusa Sweatshirt", "variants": [ @@ -343,7 +337,6 @@ describe("CSV processor", () => { { "toCreate": { "shorts": { - "categories": [], "description": "Reimagine the feeling of classic shorts. With our cotton shorts, everyday essentials no longer have to be ordinary.", "discountable": true, "handle": "shorts", @@ -372,7 +365,6 @@ describe("CSV processor", () => { }, ], "status": "published", - "tags": [], "thumbnail": "https://medusa-public-images.s3.eu-west-1.amazonaws.com/shorts-vintage-front.png", "title": "Medusa Shorts", "variants": [ @@ -464,7 +456,6 @@ describe("CSV processor", () => { "weight": 400, }, "sweatpants": { - "categories": [], "description": "Reimagine the feeling of classic sweatpants. With our cotton sweatpants, everyday essentials no longer have to be ordinary.", "discountable": true, "handle": "sweatpants", @@ -493,7 +484,6 @@ describe("CSV processor", () => { }, ], "status": "published", - "tags": [], "thumbnail": "https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatpants-gray-front.png", "title": "Medusa Sweatpants", "variants": [ @@ -585,7 +575,6 @@ describe("CSV processor", () => { "weight": 400, }, "t-shirt": { - "categories": [], "description": "Reimagine the feeling of a classic T-shirt. With our cotton T-shirts, everyday essentials no longer have to be ordinary.", "discountable": true, "handle": "t-shirt", @@ -627,7 +616,6 @@ describe("CSV processor", () => { }, ], "status": "published", - "tags": [], "thumbnail": "https://medusa-public-images.s3.eu-west-1.amazonaws.com/tee-black-front.png", "title": "Medusa T-Shirt", "variants": [ @@ -813,7 +801,6 @@ describe("CSV processor", () => { }, "toUpdate": { "prod_01JSXX3ZVW4M4RS0NH4MSWCQWA": { - "categories": [], "description": "Reimagine the feeling of a classic sweatshirt. With our cotton sweatshirt, everyday essentials no longer have to be ordinary.", "discountable": true, "handle": "sweatshirt", @@ -843,7 +830,6 @@ describe("CSV processor", () => { }, ], "status": "published", - "tags": [], "thumbnail": "https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatshirt-vintage-front.png", "title": "Medusa Sweatshirt", "variants": [ @@ -935,7 +921,6 @@ describe("CSV processor", () => { "weight": 400, }, "prod_01JT598HEWAE555V0A6BD602MG": { - "categories": [], "description": "Every programmer's best friend.", "discountable": true, "handle": "coffee-mug-v3", @@ -955,7 +940,6 @@ describe("CSV processor", () => { ], "sales_channels": [], "status": "published", - "tags": [], "thumbnail": "https://medusa-public-images.s3.eu-west-1.amazonaws.com/coffee-mug.png", "title": "Medusa Coffee Mug", "variants": [ @@ -983,7 +967,6 @@ describe("CSV processor", () => { "weight": 400, }, "prod_01JT598HEX26EHDG7SRK37Q3FG": { - "categories": [], "description": "Reimagine the feeling of classic sweatpants. With our cotton sweatpants, everyday essentials no longer have to be ordinary.", "discountable": true, "handle": "sweatpants-v2", @@ -1009,7 +992,6 @@ describe("CSV processor", () => { ], "sales_channels": [], "status": "published", - "tags": [], "thumbnail": "https://medusa-public-images.s3.eu-west-1.amazonaws.com/sweatpants-gray-front.png", "title": "Medusa Sweatpants", "variants": [ @@ -1123,7 +1105,7 @@ describe("CSV processor", () => { const csvRow = { "Product Handle": "test-product", "Variant Title": "Test Variant", - "Variant Metadata": 'invalid json', + "Variant Metadata": "invalid json", } const normalized = CSVNormalizer.preProcess(csvRow, 1) @@ -1158,12 +1140,12 @@ describe("CSV processor", () => { // Should be in toCreate since we only have handle expect(result.toCreate["test-product"]).toBeDefined() expect(result.toCreate["test-product"].is_giftcard).toBe(true) - + // Timestamp fields should not be in the output since they're ignored expect(result.toCreate["test-product"]["created_at"]).toBeUndefined() expect(result.toCreate["test-product"]["updated_at"]).toBeUndefined() expect(result.toCreate["test-product"]["deleted_at"]).toBeUndefined() - + // Verify that the timestamp fields are present in normalized data but ignored during processing expect(normalized["product created at"]).toBe("") expect(normalized["product updated at"]).toBe("") @@ -1194,17 +1176,17 @@ describe("CSV processor", () => { // Should be in toCreate since we only have handle expect(result.toCreate["test-product"]).toBeDefined() expect(result.toCreate["test-product"].variants).toHaveLength(1) - + const variant = result.toCreate["test-product"].variants[0] expect(variant.title).toBe("Test Variant") expect(variant.sku).toBe("TEST-SKU") - + // Timestamp fields should not be in the variant output since they're ignored expect(variant["created_at"]).toBeUndefined() expect(variant["updated_at"]).toBeUndefined() expect(variant["deleted_at"]).toBeUndefined() expect(variant["product_id"]).toBeUndefined() - + // Verify that the timestamp fields are present in normalized data but ignored during processing expect(normalized["variant created at"]).toBe("") expect(normalized["variant updated at"]).toBe("") @@ -1259,7 +1241,9 @@ describe("CSV processor", () => { const processor = new CSVNormalizer([normalized]) const result = processor.proccess() - expect(result.toCreate[`test-product-${value}`].is_giftcard).toBe(expected) + expect(result.toCreate[`test-product-${value}`].is_giftcard).toBe( + expected + ) }) }) }) diff --git a/packages/core/utils/src/product/csv-normalizer.ts b/packages/core/utils/src/product/csv-normalizer.ts index b3fc4e1283..d6497b3498 100644 --- a/packages/core/utils/src/product/csv-normalizer.ts +++ b/packages/core/utils/src/product/csv-normalizer.ts @@ -1,9 +1,9 @@ import { isPresent, - tryConvertToNumber, - tryConvertToBoolean, MedusaError, normalizeCSVValue, + tryConvertToBoolean, + tryConvertToNumber, } from "../common" import { AdminCreateProduct, AdminCreateProductVariant } from "@medusajs/types" @@ -89,11 +89,14 @@ function processAsJson( return (csvRow, _, rowNumber, output) => { const value = csvRow[inputKey] if (isPresent(value)) { - if (typeof value === 'string') { + if (typeof value === "string") { try { - output[outputKey] = JSON.parse(value); + output[outputKey] = JSON.parse(value) } catch (error) { - throw createError(rowNumber, `Invalid value provided for "${inputKey}". Expected a valid JSON string, received "${value}"`); + throw createError( + rowNumber, + `Invalid value provided for "${inputKey}". Expected a valid JSON string, received "${value}"` + ) } } } @@ -164,17 +167,21 @@ function processAsCounterValue>( outputKey: keyof Output ): ColumnProcessor { return (csvRow, rowColumns, _, output) => { - output[outputKey] = output[outputKey] ?? [] - const existingIds = output[outputKey].map((item) => item[arrayItemKey]) + const matchingColumns = rowColumns.filter((rowKey) => inputMatcher.test(rowKey)) - rowColumns - .filter((rowKey) => inputMatcher.test(rowKey)) - .forEach((rowKey) => { + // Only initialize the array if there are matching columns in the CSV + if (matchingColumns.length > 0) { + output[outputKey] = output[outputKey] ?? [] + + const existingIds = output[outputKey].map((item) => item[arrayItemKey]) + + matchingColumns.forEach((rowKey) => { const value = csvRow[rowKey] if (!existingIds.includes(value) && isPresent(value)) { output[outputKey].push({ [arrayItemKey]: value }) } }) + } } }