@contrail/cli 3.5.1-alpha.68d57e0 → 3.5.1-alpha.9da7ac5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +1 -1
  2. package/node_modules/@contrail/loader/lib/color-processor.js +23 -9
  3. package/node_modules/@contrail/loader/lib/color-processor.spec.js +62 -112
  4. package/node_modules/@contrail/loader/lib/common/chunk-by-url-length.d.ts +1 -1
  5. package/node_modules/@contrail/loader/lib/common/chunk-by-url-length.js +0 -2
  6. package/node_modules/@contrail/loader/lib/common/chunk-by-url-length.spec.js +0 -4
  7. package/node_modules/@contrail/loader/lib/common/size-range-helpers.js +1 -0
  8. package/node_modules/@contrail/loader/lib/constants.d.ts +3 -0
  9. package/node_modules/@contrail/loader/lib/constants.js +4 -1
  10. package/node_modules/@contrail/loader/lib/entities-v2/colors-v2/color-processor-v2.js +23 -9
  11. package/node_modules/@contrail/loader/lib/entities-v2/colors-v2/color-processor-v2.spec.js +62 -112
  12. package/node_modules/@contrail/loader/lib/entities-v2/entities-helper.js +1 -0
  13. package/node_modules/@contrail/loader/lib/entities-v2/linesheet-v2/assortment-processor.js +1 -0
  14. package/node_modules/@contrail/loader/lib/entities-v2/linesheet-v2/item-processor.js +22 -8
  15. package/node_modules/@contrail/loader/lib/entities-v2/linesheet-v2/item-processor.spec.js +1 -43
  16. package/node_modules/@contrail/loader/lib/pipeline/item-execute-helpers.js +3 -77
  17. package/node_modules/@contrail/loader/lib/pipeline/item-execute-helpers.spec.js +0 -82
  18. package/node_modules/@contrail/loader/lib/pipeline/preview.js +5 -0
  19. package/node_modules/@contrail/loader/lib/pipeline/preview.spec.js +2 -2
  20. package/node_modules/@contrail/loader/lib/pipeline/project-item-execute-helpers.js +1 -1
  21. package/node_modules/@contrail/loader/lib/pipeline/transform.js +0 -18
  22. package/node_modules/@contrail/loader/lib/pipeline/transform.spec.js +2 -3
  23. package/node_modules/@contrail/loader/lib/pipeline/types.d.ts +2 -5
  24. package/node_modules/@contrail/loader/lib/pipeline/types.js +0 -2
  25. package/node_modules/@contrail/loader/lib/pipeline/validate.js +437 -184
  26. package/node_modules/@contrail/loader/lib/pipeline/validate.spec.js +53 -157
  27. package/node_modules/@contrail/loader/package.json +2 -3
  28. package/oclif.manifest.json +1 -1
  29. package/package.json +2 -2
  30. package/node_modules/@contrail/loader/lib/common/color-validation.d.ts +0 -26
  31. package/node_modules/@contrail/loader/lib/common/color-validation.js +0 -163
  32. package/node_modules/@contrail/loader/lib/common/color-validation.spec.d.ts +0 -1
  33. package/node_modules/@contrail/loader/lib/common/color-validation.spec.js +0 -102
  34. package/node_modules/@contrail/loader/lib/pipeline/identifier-resolver.d.ts +0 -5
  35. package/node_modules/@contrail/loader/lib/pipeline/identifier-resolver.js +0 -562
  36. package/node_modules/@contrail/loader/lib/pipeline/identifier-resolver.spec.d.ts +0 -1
  37. package/node_modules/@contrail/loader/lib/pipeline/identifier-resolver.spec.js +0 -231
  38. package/node_modules/validate-color/LICENSE +0 -21
  39. package/node_modules/validate-color/README.md +0 -265
  40. package/node_modules/validate-color/lib/index.d.ts +0 -10
  41. package/node_modules/validate-color/lib/index.js +0 -1
  42. package/node_modules/validate-color/package.json +0 -91
  43. package/node_modules/validate-color/test/redos.js +0 -29
package/README.md CHANGED
@@ -17,7 +17,7 @@ $ npm install -g @contrail/cli
17
17
  $ contrail COMMAND
18
18
  running command...
19
19
  $ contrail (--version)
20
- @contrail/cli/3.5.1-alpha.68d57e0 linux-x64 node-v24.14.1
20
+ @contrail/cli/3.5.1-alpha.9da7ac5 linux-x64 node-v24.14.1
21
21
  $ contrail --help [COMMAND]
22
22
  USAGE
23
23
  $ contrail COMMAND
@@ -5,7 +5,14 @@ const entity_types_1 = require("@contrail/entity-types");
5
5
  const base_processor_1 = require("./base-processor");
6
6
  const entity_processor_1 = require("./entity-processor");
7
7
  const constants_1 = require("./constants");
8
- const color_validation_1 = require("./common/color-validation");
8
+ function removeHexHash(data) {
9
+ for (const row of data ?? []) {
10
+ if (row.hex?.startsWith('#')) {
11
+ row.hex = row.hex.slice(1);
12
+ }
13
+ }
14
+ return data;
15
+ }
9
16
  class ColorProcessor extends base_processor_1.BaseProcessor {
10
17
  static typesToTransformBy = ['color'];
11
18
  static requiredProperties = ['typePath', 'typeId', 'federatedId', 'name', 'hex', 'red', 'green', 'blue'];
@@ -13,7 +20,7 @@ class ColorProcessor extends base_processor_1.BaseProcessor {
13
20
  if (!data) {
14
21
  return [];
15
22
  }
16
- return (0, color_validation_1.applyHexPriorityToRows)(data);
23
+ return removeHexHash(data);
17
24
  }
18
25
  static validateTypePath(data, config, csvHeaders, generalErrors) {
19
26
  const typePathSource = {
@@ -49,13 +56,20 @@ class ColorProcessor extends base_processor_1.BaseProcessor {
49
56
  static validateLoadFile(data, config) {
50
57
  const { errors, warnings, generalErrors, generalWarnings, csvHeaders } = entity_processor_1.EntityProcessor.validateLoadFile(data, config, entity_types_1.LoadType.COLOR);
51
58
  this.validateTypePath(data, config, csvHeaders, generalErrors);
52
- data.forEach((row, index) => {
53
- const { rowErrors, rowWarnings } = (0, color_validation_1.buildColorRowIssues)(row, index + 2, config?.federatedMappings);
54
- if (rowErrors)
55
- errors.push(rowErrors);
56
- if (rowWarnings)
57
- warnings.push(rowWarnings);
58
- });
59
+ const redColumn = config?.federatedMappings?.red || 'red';
60
+ const blueColumn = config?.federatedMappings?.blue || 'blue';
61
+ const greenColumn = config?.federatedMappings?.green || 'green';
62
+ const hexColumn = config?.federatedMappings?.hex || 'hex';
63
+ const hasRedColumn = csvHeaders.includes(redColumn);
64
+ const hasBlueColumn = csvHeaders.includes(blueColumn);
65
+ const hasGreenColumn = csvHeaders.includes(greenColumn);
66
+ const hasHexColumn = csvHeaders.includes(hexColumn);
67
+ if (hasHexColumn && hasRedColumn && hasGreenColumn && hasBlueColumn) {
68
+ generalWarnings.push({
69
+ message: constants_1.COLOR_PROCESSOR_WARNINGS.HEX_IGNORED_WARNING,
70
+ issueType: entity_types_1.IssueType.CONFIG,
71
+ });
72
+ }
59
73
  return { errors, warnings, generalErrors, generalWarnings };
60
74
  }
61
75
  }
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const color_processor_1 = require("./color-processor");
4
4
  const entity_types_1 = require("@contrail/entity-types");
5
5
  const constants_1 = require("./constants");
6
- const color_validation_1 = require("./common/color-validation");
7
6
  describe('ColorProcessor', () => {
8
7
  const loaderProcess = {
9
8
  id: 'mock-loader-process-id',
@@ -220,38 +219,72 @@ describe('ColorProcessor', () => {
220
219
  });
221
220
  });
222
221
  describe('color column validation', () => {
223
- it('warns per-row when both hex and RGB are populated (hex prioritized)', () => {
222
+ it('should warn when both hex and RGB columns are present', () => {
224
223
  const data = [
225
- { name: 'test-color', red: '255', green: '0', blue: '0', hex: '#ff0000' },
226
- ];
227
- const result = color_processor_1.ColorProcessor.validateLoadFile(data, loaderProcess);
228
- expect(result.warnings).toEqual([
229
224
  {
230
- rowNumber: 2,
231
- columnIssues: [
232
- {
233
- columnName: 'hex',
234
- message: color_validation_1.COLOR_VALIDATION_MESSAGES.HEX_PRIORITIZED_OVER_RGB,
235
- issueType: entity_types_1.IssueType.INVALID_DATA,
236
- data: JSON.stringify(data[0]),
237
- },
238
- ],
225
+ name: 'test-color',
226
+ red: '255',
227
+ green: '0',
228
+ blue: '0',
229
+ hex: '#ff0000',
230
+ },
231
+ ];
232
+ const config = {
233
+ ...loaderProcess,
234
+ federatedMappings: {
235
+ ...loaderProcess.federatedMappings,
236
+ red: 'red',
237
+ green: 'green',
238
+ blue: 'blue',
239
+ hex: 'hex',
239
240
  },
240
- ]);
241
+ };
242
+ const result = color_processor_1.ColorProcessor.validateLoadFile(data, config);
243
+ expect(result.generalWarnings).toContainEqual({
244
+ message: constants_1.COLOR_PROCESSOR_WARNINGS.HEX_IGNORED_WARNING,
245
+ issueType: entity_types_1.IssueType.CONFIG,
246
+ });
241
247
  });
242
- it('does not warn when only RGB is populated and all components are valid', () => {
243
- const data = [{ name: 'test-color', red: '255', green: '0', blue: '0' }];
244
- const result = color_processor_1.ColorProcessor.validateLoadFile(data, loaderProcess);
245
- expect(result.warnings).toEqual([]);
246
- expect(result.errors).toEqual([]);
248
+ it('should not warn when only RGB columns are present', () => {
249
+ const data = [
250
+ {
251
+ name: 'test-color',
252
+ red: '255',
253
+ green: '0',
254
+ blue: '0',
255
+ },
256
+ ];
257
+ const config = {
258
+ ...loaderProcess,
259
+ federatedMappings: {
260
+ ...loaderProcess.federatedMappings,
261
+ red: 'red',
262
+ green: 'green',
263
+ blue: 'blue',
264
+ },
265
+ };
266
+ const result = color_processor_1.ColorProcessor.validateLoadFile(data, config);
267
+ expect(result.generalWarnings).not.toContainEqual({
268
+ message: constants_1.COLOR_PROCESSOR_WARNINGS.HEX_IGNORED_WARNING,
269
+ issueType: entity_types_1.IssueType.CONFIG,
270
+ });
247
271
  });
248
- it('does not warn when only a valid hex is populated', () => {
272
+ it('should not warn when only hex column is present', () => {
249
273
  const data = [{ name: 'test-color', hex: '#ff0000' }];
250
- const result = color_processor_1.ColorProcessor.validateLoadFile(data, loaderProcess);
251
- expect(result.warnings).toEqual([]);
252
- expect(result.errors).toEqual([]);
274
+ const config = {
275
+ ...loaderProcess,
276
+ federatedMappings: {
277
+ ...loaderProcess.federatedMappings,
278
+ hex: 'hex',
279
+ },
280
+ };
281
+ const result = color_processor_1.ColorProcessor.validateLoadFile(data, config);
282
+ expect(result.generalWarnings).not.toContainEqual({
283
+ message: constants_1.COLOR_PROCESSOR_WARNINGS.HEX_IGNORED_WARNING,
284
+ issueType: entity_types_1.IssueType.CONFIG,
285
+ });
253
286
  });
254
- it('warns per-row when both hex and RGB are populated under federatedMappings', () => {
287
+ it('should handle custom column names from federatedMappings', () => {
255
288
  const data = [{ name: 'test-color', r: '255', g: '0', b: '0', hexcode: '#ff0000' }];
256
289
  const config = {
257
290
  ...loaderProcess,
@@ -264,94 +297,11 @@ describe('ColorProcessor', () => {
264
297
  },
265
298
  };
266
299
  const result = color_processor_1.ColorProcessor.validateLoadFile(data, config);
267
- expect(result.warnings).toEqual([
268
- {
269
- rowNumber: 2,
270
- columnIssues: [
271
- {
272
- columnName: 'hex',
273
- message: color_validation_1.COLOR_VALIDATION_MESSAGES.HEX_PRIORITIZED_OVER_RGB,
274
- issueType: entity_types_1.IssueType.INVALID_DATA,
275
- data: JSON.stringify(data[0]),
276
- },
277
- ],
278
- },
279
- ]);
280
- });
281
- it('errors when hex is invalid', () => {
282
- const data = [{ name: 'bad', hex: 'notahex' }];
283
- const result = color_processor_1.ColorProcessor.validateLoadFile(data, loaderProcess);
284
- expect(result.errors).toHaveLength(1);
285
- expect(result.errors[0]).toMatchObject({
286
- rowNumber: 2,
287
- columnIssues: [
288
- {
289
- columnName: 'hex',
290
- message: color_validation_1.COLOR_VALIDATION_MESSAGES.HEX_INVALID('notahex'),
291
- issueType: entity_types_1.IssueType.INVALID_DATA,
292
- },
293
- ],
294
- });
295
- });
296
- it('errors when RGB is partially populated and hex is missing', () => {
297
- const data = [{ name: 'partial', red: '255', green: '128' }];
298
- const result = color_processor_1.ColorProcessor.validateLoadFile(data, loaderProcess);
299
- expect(result.errors).toHaveLength(1);
300
- expect(result.errors[0].rowNumber).toBe(2);
301
- expect(result.errors[0].columnIssues).toEqual([
302
- {
303
- columnName: 'blue',
304
- message: color_validation_1.COLOR_VALIDATION_MESSAGES.RGB_INCOMPLETE(['blue']),
305
- issueType: entity_types_1.IssueType.INVALID_DATA,
306
- data: JSON.stringify(data[0]),
307
- },
308
- ]);
309
- });
310
- it('errors when an RGB component is out of range or non-numeric', () => {
311
- const data = [
312
- { name: 'bad-red', red: '300', green: '0', blue: '0' },
313
- { name: 'nan-green', red: '10', green: 'foo', blue: '0' },
314
- ];
315
- const result = color_processor_1.ColorProcessor.validateLoadFile(data, loaderProcess);
316
- expect(result.errors).toHaveLength(2);
317
- expect(result.errors[0]).toMatchObject({
318
- rowNumber: 2,
319
- columnIssues: [
320
- { columnName: 'red', message: color_validation_1.COLOR_VALIDATION_MESSAGES.RGB_OUT_OF_RANGE('red', '300') },
321
- ],
322
- });
323
- expect(result.errors[1]).toMatchObject({
324
- rowNumber: 3,
325
- columnIssues: [
326
- { columnName: 'green', message: color_validation_1.COLOR_VALIDATION_MESSAGES.RGB_OUT_OF_RANGE('green', 'foo') },
327
- ],
300
+ expect(result.generalWarnings).toContainEqual({
301
+ message: constants_1.COLOR_PROCESSOR_WARNINGS.HEX_IGNORED_WARNING,
302
+ issueType: entity_types_1.IssueType.CONFIG,
328
303
  });
329
304
  });
330
- it('does not error when neither hex nor RGB are populated (defaults to black)', () => {
331
- const data = [{ name: 'no-color' }];
332
- const result = color_processor_1.ColorProcessor.validateLoadFile(data, loaderProcess);
333
- expect(result.errors).toEqual([]);
334
- expect(result.warnings).toEqual([]);
335
- });
336
- });
337
- });
338
- describe('transformData (hex priority)', () => {
339
- it('strips red/green/blue when hex is valid', () => {
340
- const data = [
341
- { hex: '#ff0000', red: '255', green: '0', blue: '0', name: 'red' },
342
- ];
343
- const result = color_processor_1.ColorProcessor.transformData(data);
344
- expect(result).toEqual([{ hex: 'ff0000', name: 'red' }]);
345
- });
346
- it('keeps red/green/blue and removes empty hex when hex is missing', () => {
347
- const data = [{ red: '255', green: '0', blue: '0', name: 'red' }];
348
- const result = color_processor_1.ColorProcessor.transformData(data);
349
- expect(result).toEqual([{ red: '255', green: '0', blue: '0', name: 'red' }]);
350
- });
351
- it('defaults to #000000 (without hash) when neither hex nor full RGB is provided', () => {
352
- const data = [{ name: 'fallback' }];
353
- const result = color_processor_1.ColorProcessor.transformData(data);
354
- expect(result).toEqual([{ name: 'fallback', hex: color_validation_1.DEFAULT_HEX }]);
355
305
  });
356
306
  });
357
307
  });
@@ -2,5 +2,5 @@ interface ChunkByUrlLengthOptions {
2
2
  maxBytes?: number;
3
3
  maxCount?: number;
4
4
  }
5
- export declare function chunkByUrlLength(ids: readonly string[] | null | undefined, options?: ChunkByUrlLengthOptions): string[][];
5
+ export declare function chunkByUrlLength(ids: string[], options?: ChunkByUrlLengthOptions): string[][];
6
6
  export {};
@@ -6,8 +6,6 @@ function wireEncodedLength(id) {
6
6
  return encodeURIComponent(id.replace(/,/g, '_COMMA_')).length;
7
7
  }
8
8
  function chunkByUrlLength(ids, options = {}) {
9
- if (!ids?.length)
10
- return [];
11
9
  const maxBytes = options.maxBytes ?? constants_1.FEDERATED_ID_FETCH_MAX_URL_BYTES;
12
10
  const maxCount = options.maxCount ?? constants_1.FEDERATED_ID_FETCH_MAX_COUNT;
13
11
  const result = [];
@@ -5,10 +5,6 @@ describe('chunkByUrlLength', () => {
5
5
  it('returns an empty array for empty input', () => {
6
6
  expect((0, chunk_by_url_length_1.chunkByUrlLength)([])).toEqual([]);
7
7
  });
8
- it('returns an empty array for null/undefined input (matches lodash.chunk behavior)', () => {
9
- expect((0, chunk_by_url_length_1.chunkByUrlLength)(undefined)).toEqual([]);
10
- expect((0, chunk_by_url_length_1.chunkByUrlLength)(null)).toEqual([]);
11
- });
12
8
  it('packs short ids into a single chunk up to maxCount', () => {
13
9
  const ids = Array.from({ length: 50 }, (_, i) => `id-${i}`);
14
10
  const chunks = (0, chunk_by_url_length_1.chunkByUrlLength)(ids, { maxCount: 200, maxBytes: 10000 });
@@ -53,6 +53,7 @@ class SizeRangeSanitizer {
53
53
  }
54
54
  const typePropertySlugs = type?.typeProperties?.map((x) => x.slug);
55
55
  if ((0, lodash_1.intersection)(typePropertySlugs, [exports.SIZE_RANGE_SLUG, exports.SIZE_RANGE_TEMPLATE_SLUG]).length !== 2) {
56
+ console.debug(`No size ranges for type ${JSON.stringify(type, null, 2)}, skipping.`);
56
57
  continue;
57
58
  }
58
59
  validTypePaths.push(typePath);
@@ -1,6 +1,9 @@
1
1
  export declare const COLOR_PROCESSOR_ERRORS: {
2
2
  readonly TYPEPATH_MISSING: "\"typePath\" with value \"color\" is required property but is not found in the federatedMapping config, conditionalColumns config, or as a header in the CSV file. Please add a mapping \"typePath\" mapping with value \"color\" with one of these methods, then try the load again.";
3
3
  };
4
+ export declare const COLOR_PROCESSOR_WARNINGS: {
5
+ readonly HEX_IGNORED_WARNING: "\"Hex\" is ignored when red, green, and blue columns are present";
6
+ };
4
7
  export declare const CUSTOM_ENTITY_PROCESSOR_ERRORS: {
5
8
  readonly TYPEPATH_MISSING: "\"typePath\" with value \"custom-entity:<subtypepath>\" is required property but is not found in the federatedMapping config, conditionalColumns config, or as a header in the CSV file. Please add a mapping \"typePath\" mapping with value \"custom-entity:<subtypepath>\" with one of these methods, then try the load again.";
6
9
  };
@@ -1,9 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ITEM_PROCESSOR_WARNINGS = exports.FEDERATED_ID_FETCH_MAX_COUNT = exports.FEDERATED_ID_FETCH_MAX_URL_BYTES = exports.ITEM_PROCESSOR_ERRORS = exports.ENTITY_PROCESSOR_ERRORS = exports.CUSTOM_ENTITY_PROCESSOR_ERRORS = exports.COLOR_PROCESSOR_ERRORS = void 0;
3
+ exports.ITEM_PROCESSOR_WARNINGS = exports.FEDERATED_ID_FETCH_MAX_COUNT = exports.FEDERATED_ID_FETCH_MAX_URL_BYTES = exports.ITEM_PROCESSOR_ERRORS = exports.ENTITY_PROCESSOR_ERRORS = exports.CUSTOM_ENTITY_PROCESSOR_ERRORS = exports.COLOR_PROCESSOR_WARNINGS = exports.COLOR_PROCESSOR_ERRORS = void 0;
4
4
  exports.COLOR_PROCESSOR_ERRORS = {
5
5
  TYPEPATH_MISSING: '"typePath" with value "color" is required property but is not found in the federatedMapping config, conditionalColumns config, or as a header in the CSV file. Please add a mapping "typePath" mapping with value "color" with one of these methods, then try the load again.',
6
6
  };
7
+ exports.COLOR_PROCESSOR_WARNINGS = {
8
+ HEX_IGNORED_WARNING: '"Hex" is ignored when red, green, and blue columns are present',
9
+ };
7
10
  exports.CUSTOM_ENTITY_PROCESSOR_ERRORS = {
8
11
  TYPEPATH_MISSING: '"typePath" with value "custom-entity:<subtypepath>" is required property but is not found in the federatedMapping config, conditionalColumns config, or as a header in the CSV file. Please add a mapping "typePath" mapping with value "custom-entity:<subtypepath>" with one of these methods, then try the load again.',
9
12
  };
@@ -6,7 +6,14 @@ const util_1 = require("../../common/util");
6
6
  const entity_processor_v2_1 = require("../entity-processor-v2");
7
7
  const constants_1 = require("../../constants");
8
8
  const base_processor_v2_1 = require("../base-processor-v2");
9
- const color_validation_1 = require("../../common/color-validation");
9
+ function removeHexHash(data) {
10
+ for (const row of data ?? []) {
11
+ if (row.hex?.startsWith('#')) {
12
+ row.hex = row.hex.slice(1);
13
+ }
14
+ }
15
+ return data;
16
+ }
10
17
  class ColorProcessorV2 extends base_processor_v2_1.BaseProcessorV2 {
11
18
  static requiredProperties = ['typePath', 'typeId', 'federatedId', 'name', 'hex', 'red', 'green', 'blue'];
12
19
  static canAddProperty(allPropertiesSlugs, propertyName) {
@@ -16,7 +23,7 @@ class ColorProcessorV2 extends base_processor_v2_1.BaseProcessorV2 {
16
23
  if (!data) {
17
24
  return [];
18
25
  }
19
- return (0, color_validation_1.applyHexPriorityToRows)(data);
26
+ return removeHexHash(data);
20
27
  }
21
28
  static validateLoadFile(data, config) {
22
29
  const { errors, warnings, generalErrors, generalWarnings, csvHeaders, returnImmediately } = entity_processor_v2_1.EntityProcessorV2.validateLoadFile(data, config);
@@ -34,13 +41,20 @@ class ColorProcessorV2 extends base_processor_v2_1.BaseProcessorV2 {
34
41
  if (duplicateFederatedIdErrors.length) {
35
42
  errors.push(...duplicateFederatedIdErrors);
36
43
  }
37
- data.forEach((row, index) => {
38
- const { rowErrors, rowWarnings } = (0, color_validation_1.buildColorRowIssues)(row, index + 2, config?.federatedMappings);
39
- if (rowErrors)
40
- errors.push(rowErrors);
41
- if (rowWarnings)
42
- warnings.push(rowWarnings);
43
- });
44
+ const redColumn = config?.federatedMappings?.red || 'red';
45
+ const blueColumn = config?.federatedMappings?.blue || 'blue';
46
+ const greenColumn = config?.federatedMappings?.green || 'green';
47
+ const hexColumn = config?.federatedMappings?.hex || 'hex';
48
+ const hasRedColumn = csvHeaders.includes(redColumn);
49
+ const hasBlueColumn = csvHeaders.includes(blueColumn);
50
+ const hasGreenColumn = csvHeaders.includes(greenColumn);
51
+ const hasHexColumn = csvHeaders.includes(hexColumn);
52
+ if (hasHexColumn && hasRedColumn && hasGreenColumn && hasBlueColumn) {
53
+ generalWarnings.push({
54
+ message: constants_1.COLOR_PROCESSOR_WARNINGS.HEX_IGNORED_WARNING,
55
+ issueType: entity_types_1.IssueType.CONFIG,
56
+ });
57
+ }
44
58
  return { errors, warnings, generalErrors, generalWarnings };
45
59
  }
46
60
  static validateTypePath(data, config, csvHeaders, generalErrors) {
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const constants_1 = require("../../constants");
4
4
  const color_processor_v2_1 = require("./color-processor-v2");
5
5
  const entity_types_1 = require("@contrail/entity-types");
6
- const color_validation_1 = require("../../common/color-validation");
7
6
  describe('ColorProcessorV2', () => {
8
7
  const loaderProcess = {
9
8
  id: 'mock-loader-process-id',
@@ -220,38 +219,72 @@ describe('ColorProcessorV2', () => {
220
219
  });
221
220
  });
222
221
  describe('color column validation', () => {
223
- it('warns per-row when both hex and RGB are populated (hex prioritized)', () => {
222
+ it('should warn when both hex and RGB columns are present', () => {
224
223
  const data = [
225
- { name: 'test-color', red: '255', green: '0', blue: '0', hex: '#ff0000' },
226
- ];
227
- const result = color_processor_v2_1.ColorProcessorV2.validateLoadFile(data, loaderProcess);
228
- expect(result.warnings).toEqual([
229
224
  {
230
- rowNumber: 2,
231
- columnIssues: [
232
- {
233
- columnName: 'hex',
234
- message: color_validation_1.COLOR_VALIDATION_MESSAGES.HEX_PRIORITIZED_OVER_RGB,
235
- issueType: entity_types_1.IssueType.INVALID_DATA,
236
- data: JSON.stringify(data[0]),
237
- },
238
- ],
225
+ name: 'test-color',
226
+ red: '255',
227
+ green: '0',
228
+ blue: '0',
229
+ hex: '#ff0000',
230
+ },
231
+ ];
232
+ const config = {
233
+ ...loaderProcess,
234
+ federatedMappings: {
235
+ ...loaderProcess.federatedMappings,
236
+ red: 'red',
237
+ green: 'green',
238
+ blue: 'blue',
239
+ hex: 'hex',
239
240
  },
240
- ]);
241
+ };
242
+ const result = color_processor_v2_1.ColorProcessorV2.validateLoadFile(data, config);
243
+ expect(result.generalWarnings).toContainEqual({
244
+ message: constants_1.COLOR_PROCESSOR_WARNINGS.HEX_IGNORED_WARNING,
245
+ issueType: entity_types_1.IssueType.CONFIG,
246
+ });
241
247
  });
242
- it('does not warn when only RGB is populated and all components are valid', () => {
243
- const data = [{ name: 'test-color', red: '255', green: '0', blue: '0' }];
244
- const result = color_processor_v2_1.ColorProcessorV2.validateLoadFile(data, loaderProcess);
245
- expect(result.warnings).toEqual([]);
246
- expect(result.errors).toEqual([]);
248
+ it('should not warn when only RGB columns are present', () => {
249
+ const data = [
250
+ {
251
+ name: 'test-color',
252
+ red: '255',
253
+ green: '0',
254
+ blue: '0',
255
+ },
256
+ ];
257
+ const config = {
258
+ ...loaderProcess,
259
+ federatedMappings: {
260
+ ...loaderProcess.federatedMappings,
261
+ red: 'red',
262
+ green: 'green',
263
+ blue: 'blue',
264
+ },
265
+ };
266
+ const result = color_processor_v2_1.ColorProcessorV2.validateLoadFile(data, config);
267
+ expect(result.generalWarnings).not.toContainEqual({
268
+ message: constants_1.COLOR_PROCESSOR_WARNINGS.HEX_IGNORED_WARNING,
269
+ issueType: entity_types_1.IssueType.CONFIG,
270
+ });
247
271
  });
248
- it('does not warn when only a valid hex is populated', () => {
272
+ it('should not warn when only hex column is present', () => {
249
273
  const data = [{ name: 'test-color', hex: '#ff0000' }];
250
- const result = color_processor_v2_1.ColorProcessorV2.validateLoadFile(data, loaderProcess);
251
- expect(result.warnings).toEqual([]);
252
- expect(result.errors).toEqual([]);
274
+ const config = {
275
+ ...loaderProcess,
276
+ federatedMappings: {
277
+ ...loaderProcess.federatedMappings,
278
+ hex: 'hex',
279
+ },
280
+ };
281
+ const result = color_processor_v2_1.ColorProcessorV2.validateLoadFile(data, config);
282
+ expect(result.generalWarnings).not.toContainEqual({
283
+ message: constants_1.COLOR_PROCESSOR_WARNINGS.HEX_IGNORED_WARNING,
284
+ issueType: entity_types_1.IssueType.CONFIG,
285
+ });
253
286
  });
254
- it('warns per-row when both hex and RGB are populated under federatedMappings', () => {
287
+ it('should handle custom column names from federatedMappings', () => {
255
288
  const data = [{ name: 'test-color', r: '255', g: '0', b: '0', hexcode: '#ff0000' }];
256
289
  const config = {
257
290
  ...loaderProcess,
@@ -264,94 +297,11 @@ describe('ColorProcessorV2', () => {
264
297
  },
265
298
  };
266
299
  const result = color_processor_v2_1.ColorProcessorV2.validateLoadFile(data, config);
267
- expect(result.warnings).toEqual([
268
- {
269
- rowNumber: 2,
270
- columnIssues: [
271
- {
272
- columnName: 'hex',
273
- message: color_validation_1.COLOR_VALIDATION_MESSAGES.HEX_PRIORITIZED_OVER_RGB,
274
- issueType: entity_types_1.IssueType.INVALID_DATA,
275
- data: JSON.stringify(data[0]),
276
- },
277
- ],
278
- },
279
- ]);
280
- });
281
- it('errors when hex is invalid', () => {
282
- const data = [{ name: 'bad', hex: 'notahex' }];
283
- const result = color_processor_v2_1.ColorProcessorV2.validateLoadFile(data, loaderProcess);
284
- expect(result.errors).toHaveLength(1);
285
- expect(result.errors[0]).toMatchObject({
286
- rowNumber: 2,
287
- columnIssues: [
288
- {
289
- columnName: 'hex',
290
- message: color_validation_1.COLOR_VALIDATION_MESSAGES.HEX_INVALID('notahex'),
291
- issueType: entity_types_1.IssueType.INVALID_DATA,
292
- },
293
- ],
294
- });
295
- });
296
- it('errors when RGB is partially populated and hex is missing', () => {
297
- const data = [{ name: 'partial', red: '255', green: '128' }];
298
- const result = color_processor_v2_1.ColorProcessorV2.validateLoadFile(data, loaderProcess);
299
- expect(result.errors).toHaveLength(1);
300
- expect(result.errors[0].rowNumber).toBe(2);
301
- expect(result.errors[0].columnIssues).toEqual([
302
- {
303
- columnName: 'blue',
304
- message: color_validation_1.COLOR_VALIDATION_MESSAGES.RGB_INCOMPLETE(['blue']),
305
- issueType: entity_types_1.IssueType.INVALID_DATA,
306
- data: JSON.stringify(data[0]),
307
- },
308
- ]);
309
- });
310
- it('errors when an RGB component is out of range or non-numeric', () => {
311
- const data = [
312
- { name: 'bad-red', red: '300', green: '0', blue: '0' },
313
- { name: 'nan-green', red: '10', green: 'foo', blue: '0' },
314
- ];
315
- const result = color_processor_v2_1.ColorProcessorV2.validateLoadFile(data, loaderProcess);
316
- expect(result.errors).toHaveLength(2);
317
- expect(result.errors[0]).toMatchObject({
318
- rowNumber: 2,
319
- columnIssues: [
320
- { columnName: 'red', message: color_validation_1.COLOR_VALIDATION_MESSAGES.RGB_OUT_OF_RANGE('red', '300') },
321
- ],
322
- });
323
- expect(result.errors[1]).toMatchObject({
324
- rowNumber: 3,
325
- columnIssues: [
326
- { columnName: 'green', message: color_validation_1.COLOR_VALIDATION_MESSAGES.RGB_OUT_OF_RANGE('green', 'foo') },
327
- ],
300
+ expect(result.generalWarnings).toContainEqual({
301
+ message: constants_1.COLOR_PROCESSOR_WARNINGS.HEX_IGNORED_WARNING,
302
+ issueType: entity_types_1.IssueType.CONFIG,
328
303
  });
329
304
  });
330
- it('does not error when neither hex nor RGB are populated (defaults to black)', () => {
331
- const data = [{ name: 'no-color' }];
332
- const result = color_processor_v2_1.ColorProcessorV2.validateLoadFile(data, loaderProcess);
333
- expect(result.errors).toEqual([]);
334
- expect(result.warnings).toEqual([]);
335
- });
336
- });
337
- describe('transformData (hex priority)', () => {
338
- it('strips red/green/blue when hex is valid', () => {
339
- const data = [
340
- { hex: '#ff0000', red: '255', green: '0', blue: '0', name: 'red' },
341
- ];
342
- const result = color_processor_v2_1.ColorProcessorV2.transformData(data);
343
- expect(result).toEqual([{ hex: 'ff0000', name: 'red' }]);
344
- });
345
- it('keeps red/green/blue and removes empty hex when hex is missing', () => {
346
- const data = [{ red: '255', green: '0', blue: '0', name: 'red' }];
347
- const result = color_processor_v2_1.ColorProcessorV2.transformData(data);
348
- expect(result).toEqual([{ red: '255', green: '0', blue: '0', name: 'red' }]);
349
- });
350
- it('defaults to #000000 (without hash) when neither hex nor full RGB is provided', () => {
351
- const data = [{ name: 'fallback' }];
352
- const result = color_processor_v2_1.ColorProcessorV2.transformData(data);
353
- expect(result).toEqual([{ name: 'fallback', hex: color_validation_1.DEFAULT_HEX }]);
354
- });
355
305
  });
356
306
  it('should return error if conditionalColumns format is incorrect', () => {
357
307
  const data = [{ name: 'color1' }];
@@ -67,6 +67,7 @@ async function retryOnTimeout(client, callType, callParams, maxRetries = 3) {
67
67
  }
68
68
  exports.retryOnTimeout = retryOnTimeout;
69
69
  function UpdateNeeded(incoming, existing, fieldsToSkip = [], fieldsToSkipIfOnlyOnExisting = [], type) {
70
+ console.debug('Comparing existing and incoming objects...');
70
71
  const diffs = compareIncomingAndExisting(incoming, existing, fieldsToSkip, fieldsToSkipIfOnlyOnExisting, type);
71
72
  return diffs?.length > 0;
72
73
  }
@@ -253,6 +253,7 @@ class AssortmentProcessor extends base_processor_v2_1.BaseProcessorV2 {
253
253
  }
254
254
  static async loadObjects(client, data, passedItemsByFederatedId, assortmentId, assortmentIdentifier, typesByTypePath) {
255
255
  const { allPropertiesSlugs, typePropertyToValueType } = await (0, item_helper_1.getAssortmentItemProperties)(client, this.requiredProperties, typesByTypePath);
256
+ console.debug('using assortment types:', JSON.stringify({ allPropertiesSlugs, typePropertyToValueType }, null, 2));
256
257
  const { incomingAssortmentItems, badAssortmentItems } = this.processIncomingData(data, allPropertiesSlugs, typePropertyToValueType);
257
258
  const targetAssortment = await (0, item_helper_1.getAssortment)(client, assortmentId, assortmentIdentifier, data);
258
259
  if (!targetAssortment) {