@contrail/cli 3.5.0-alpha.dd20a19 → 3.5.1-alpha.63493a5

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 (87) hide show
  1. package/README.md +3 -35
  2. package/dist/api/types.d.ts +0 -5
  3. package/dist/api/types.js +1 -36
  4. package/dist/commands/types/getAll.js +0 -18
  5. package/dist/commands/types/loadAll.js +0 -2
  6. package/dist/commands/types/mergeTypeProperties.js +0 -5
  7. package/dist/commands/types/uploadOptionSetHierarchies.js +0 -117
  8. package/dist/commands/types/uploadOptionSets.js +1 -1
  9. package/dist/commands/types/uploadRuleSets.js +2 -85
  10. package/dist/commands/types/uploadTypePolicies.js +2 -86
  11. package/dist/commands/types/uploadTypePropertyPolicies.d.ts +0 -1
  12. package/dist/commands/types/uploadTypePropertyPolicies.js +3 -139
  13. package/dist/commands/types/uploadUsageInfo.d.ts +3 -0
  14. package/dist/commands/types/uploadUsageInfo.js +4 -1
  15. package/dist/helpers/types/type-uploader.js +1 -1
  16. package/dist/templates/typescript/tsup.config.ts +0 -0
  17. package/node_modules/@contrail/loader/lib/color-processor.js +9 -23
  18. package/node_modules/@contrail/loader/lib/color-processor.spec.js +112 -62
  19. package/node_modules/@contrail/loader/lib/common/chunk-by-url-length.d.ts +1 -1
  20. package/node_modules/@contrail/loader/lib/common/chunk-by-url-length.js +2 -0
  21. package/node_modules/@contrail/loader/lib/common/chunk-by-url-length.spec.js +4 -0
  22. package/node_modules/@contrail/loader/lib/common/color-validation.d.ts +26 -0
  23. package/node_modules/@contrail/loader/lib/common/color-validation.js +163 -0
  24. package/node_modules/@contrail/loader/lib/common/color-validation.spec.js +102 -0
  25. package/node_modules/@contrail/loader/lib/common/size-range-helpers.js +0 -1
  26. package/node_modules/@contrail/loader/lib/constants.d.ts +0 -3
  27. package/node_modules/@contrail/loader/lib/constants.js +1 -4
  28. package/node_modules/@contrail/loader/lib/entities-v2/colors-v2/color-processor-v2.js +9 -23
  29. package/node_modules/@contrail/loader/lib/entities-v2/colors-v2/color-processor-v2.spec.js +112 -62
  30. package/node_modules/@contrail/loader/lib/entities-v2/entities-helper.js +0 -1
  31. package/node_modules/@contrail/loader/lib/entities-v2/linesheet-v2/assortment-processor.js +0 -1
  32. package/node_modules/@contrail/loader/lib/entities-v2/linesheet-v2/item-processor.js +8 -22
  33. package/node_modules/@contrail/loader/lib/entities-v2/linesheet-v2/item-processor.spec.js +43 -1
  34. package/node_modules/@contrail/loader/lib/pipeline/identifier-resolver.d.ts +5 -0
  35. package/node_modules/@contrail/loader/lib/pipeline/identifier-resolver.js +562 -0
  36. package/node_modules/@contrail/loader/lib/pipeline/identifier-resolver.spec.js +231 -0
  37. package/node_modules/@contrail/loader/lib/pipeline/item-execute-helpers.js +77 -3
  38. package/node_modules/@contrail/loader/lib/pipeline/item-execute-helpers.spec.js +82 -0
  39. package/node_modules/@contrail/loader/lib/pipeline/preview.js +0 -5
  40. package/node_modules/@contrail/loader/lib/pipeline/preview.spec.js +2 -2
  41. package/node_modules/@contrail/loader/lib/pipeline/project-item-execute-helpers.js +1 -1
  42. package/node_modules/@contrail/loader/lib/pipeline/transform.js +18 -0
  43. package/node_modules/@contrail/loader/lib/pipeline/transform.spec.js +3 -2
  44. package/node_modules/@contrail/loader/lib/pipeline/types.d.ts +5 -2
  45. package/node_modules/@contrail/loader/lib/pipeline/types.js +2 -0
  46. package/node_modules/@contrail/loader/lib/pipeline/validate.js +184 -437
  47. package/node_modules/@contrail/loader/lib/pipeline/validate.spec.js +157 -53
  48. package/node_modules/@contrail/loader/package.json +3 -2
  49. package/node_modules/@contrail/sdk/.github/workflows/publish-to-npm.yml +120 -0
  50. package/node_modules/@contrail/sdk/lib/auth/login.js +42 -3
  51. package/node_modules/@contrail/sdk/lib/core/request.d.ts +3 -0
  52. package/node_modules/@contrail/sdk/lib/core/request.js +100 -4
  53. package/node_modules/@contrail/sdk/lib/entities/entities.client.js +1 -1
  54. package/node_modules/@contrail/sdk/lib/entities/entity.resource.registry.js +1 -0
  55. package/node_modules/@contrail/sdk/lib/files/files.d.ts +13 -3
  56. package/node_modules/@contrail/sdk/lib/files/files.js +66 -25
  57. package/node_modules/@contrail/sdk/package.json +4 -1
  58. package/node_modules/validate-color/LICENSE +21 -0
  59. package/node_modules/validate-color/README.md +265 -0
  60. package/node_modules/validate-color/lib/index.d.ts +10 -0
  61. package/node_modules/validate-color/lib/index.js +1 -0
  62. package/node_modules/validate-color/package.json +91 -0
  63. package/node_modules/validate-color/test/redos.js +29 -0
  64. package/oclif.manifest.json +5 -80
  65. package/package.json +3 -6
  66. package/dist/api/dash0/client.d.ts +0 -13
  67. package/dist/api/dash0/client.js +0 -53
  68. package/dist/api/dash0/spans.d.ts +0 -8
  69. package/dist/api/dash0/spans.js +0 -91
  70. package/dist/api/dash0/types.d.ts +0 -84
  71. package/dist/api/dash0/types.js +0 -4
  72. package/dist/commands/event-workflows/assess-impact.d.ts +0 -19
  73. package/dist/commands/event-workflows/assess-impact.js +0 -104
  74. package/dist/helpers/event-workflow-impact/aggregate.d.ts +0 -23
  75. package/dist/helpers/event-workflow-impact/aggregate.js +0 -49
  76. package/dist/helpers/event-workflow-impact/aggregate.spec.js +0 -109
  77. package/dist/helpers/event-workflow-impact/classify.d.ts +0 -13
  78. package/dist/helpers/event-workflow-impact/classify.js +0 -135
  79. package/dist/helpers/event-workflow-impact/classify.spec.js +0 -138
  80. package/dist/helpers/event-workflow-impact/render.d.ts +0 -12
  81. package/dist/helpers/event-workflow-impact/render.js +0 -62
  82. package/dist/helpers/event-workflow-impact/subtree.d.ts +0 -20
  83. package/dist/helpers/event-workflow-impact/subtree.js +0 -93
  84. package/dist/helpers/event-workflow-impact/subtree.spec.d.ts +0 -1
  85. package/dist/helpers/event-workflow-impact/subtree.spec.js +0 -103
  86. /package/{dist/helpers/event-workflow-impact/aggregate.spec.d.ts → node_modules/@contrail/loader/lib/common/color-validation.spec.d.ts} +0 -0
  87. /package/{dist/helpers/event-workflow-impact/classify.spec.d.ts → node_modules/@contrail/loader/lib/pipeline/identifier-resolver.spec.d.ts} +0 -0
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyHexPriorityToRows = exports.buildColorRowIssues = exports.validateColorRow = exports.parseRgbComponent = exports.isValidHex = exports.DEFAULT_HEX = exports.COLOR_VALIDATION_MESSAGES = void 0;
4
+ const entity_types_1 = require("@contrail/entity-types");
5
+ const validate_color_1 = require("validate-color");
6
+ exports.COLOR_VALIDATION_MESSAGES = {
7
+ HEX_INVALID: (hex) => `"hex" value "${hex}" is not a valid hex color code (expected 3, 4, 6, or 8 hex digits, optionally prefixed with #).`,
8
+ RGB_INCOMPLETE: (missingColumns) => `Row has partial RGB data — "${missingColumns.join('", "')}" must also be provided when using RGB. Provide all of red/green/blue, or use "hex" instead.`,
9
+ RGB_OUT_OF_RANGE: (column, value) => `"${column}" value "${value}" is not a valid RGB component (must be a whole number between 0 and 255).`,
10
+ HEX_PRIORITIZED_OVER_RGB: 'Both "hex" and red/green/blue values are provided — "hex" will be used and red/green/blue will be ignored for this row.',
11
+ };
12
+ exports.DEFAULT_HEX = '000000';
13
+ const RGB_TARGET_KEYS = ['red', 'green', 'blue'];
14
+ const HEX_TARGET_KEY = 'hex';
15
+ function getColorValue(row, federatedMappings, targetKey) {
16
+ const target = row[targetKey];
17
+ if (!isEmpty(target))
18
+ return target;
19
+ const sourceColumn = federatedMappings?.[targetKey];
20
+ if (sourceColumn && sourceColumn !== targetKey)
21
+ return row[sourceColumn];
22
+ return target;
23
+ }
24
+ function isEmpty(value) {
25
+ return value === null || value === undefined || value === '';
26
+ }
27
+ function isValidHex(hex) {
28
+ if (typeof hex !== 'string' || hex.length === 0)
29
+ return false;
30
+ const normalized = hex.startsWith('#') ? hex : `#${hex}`;
31
+ return (0, validate_color_1.validateHTMLColorHex)(normalized);
32
+ }
33
+ exports.isValidHex = isValidHex;
34
+ function parseRgbComponent(value) {
35
+ if (isEmpty(value))
36
+ return null;
37
+ if (typeof value === 'number' && Number.isInteger(value) && value >= 0 && value <= 255)
38
+ return value;
39
+ if (typeof value === 'string') {
40
+ const trimmed = value.trim();
41
+ if (trimmed === '' || !/^-?\d+$/.test(trimmed))
42
+ return null;
43
+ const n = Number(trimmed);
44
+ if (!Number.isInteger(n) || n < 0 || n > 255)
45
+ return null;
46
+ return n;
47
+ }
48
+ return null;
49
+ }
50
+ exports.parseRgbComponent = parseRgbComponent;
51
+ function validateColorRow(row, federatedMappings) {
52
+ const errors = [];
53
+ const warnings = [];
54
+ const hexValue = getColorValue(row, federatedMappings, HEX_TARGET_KEY);
55
+ const rgbValues = {
56
+ red: getColorValue(row, federatedMappings, 'red'),
57
+ green: getColorValue(row, federatedMappings, 'green'),
58
+ blue: getColorValue(row, federatedMappings, 'blue'),
59
+ };
60
+ const rgbProvided = RGB_TARGET_KEYS.filter((k) => !isEmpty(rgbValues[k]));
61
+ const hexProvided = !isEmpty(hexValue);
62
+ if (hexProvided) {
63
+ if (!isValidHex(String(hexValue))) {
64
+ errors.push({
65
+ columnName: HEX_TARGET_KEY,
66
+ message: exports.COLOR_VALIDATION_MESSAGES.HEX_INVALID(String(hexValue)),
67
+ });
68
+ }
69
+ if (rgbProvided.length > 0) {
70
+ warnings.push({
71
+ columnName: HEX_TARGET_KEY,
72
+ message: exports.COLOR_VALIDATION_MESSAGES.HEX_PRIORITIZED_OVER_RGB,
73
+ });
74
+ }
75
+ return { errors, warnings };
76
+ }
77
+ if (rgbProvided.length === 0) {
78
+ return { errors, warnings };
79
+ }
80
+ if (rgbProvided.length < RGB_TARGET_KEYS.length) {
81
+ const missing = RGB_TARGET_KEYS.filter((k) => isEmpty(rgbValues[k]));
82
+ for (const k of missing) {
83
+ errors.push({
84
+ columnName: k,
85
+ message: exports.COLOR_VALIDATION_MESSAGES.RGB_INCOMPLETE([...missing]),
86
+ });
87
+ }
88
+ return { errors, warnings };
89
+ }
90
+ for (const k of RGB_TARGET_KEYS) {
91
+ if (parseRgbComponent(rgbValues[k]) === null) {
92
+ errors.push({
93
+ columnName: k,
94
+ message: exports.COLOR_VALIDATION_MESSAGES.RGB_OUT_OF_RANGE(k, String(rgbValues[k])),
95
+ });
96
+ }
97
+ }
98
+ return { errors, warnings };
99
+ }
100
+ exports.validateColorRow = validateColorRow;
101
+ function buildColorRowIssues(row, rowNumber, federatedMappings) {
102
+ const { errors, warnings } = validateColorRow(row, federatedMappings);
103
+ const rowJson = JSON.stringify(row);
104
+ const rowErrors = errors.length
105
+ ? {
106
+ rowNumber,
107
+ columnIssues: errors.map((e) => ({
108
+ columnName: e.columnName,
109
+ message: e.message,
110
+ issueType: entity_types_1.IssueType.INVALID_DATA,
111
+ data: rowJson,
112
+ })),
113
+ }
114
+ : null;
115
+ const rowWarnings = warnings.length
116
+ ? {
117
+ rowNumber,
118
+ columnIssues: warnings.map((w) => ({
119
+ columnName: w.columnName,
120
+ message: w.message,
121
+ issueType: entity_types_1.IssueType.INVALID_DATA,
122
+ data: rowJson,
123
+ })),
124
+ }
125
+ : null;
126
+ return { rowErrors, rowWarnings };
127
+ }
128
+ exports.buildColorRowIssues = buildColorRowIssues;
129
+ function applyHexPriorityToRows(rows) {
130
+ for (const r of rows ?? []) {
131
+ if (!r)
132
+ continue;
133
+ const row = r;
134
+ const hexRaw = row.hex;
135
+ const hexNormalized = typeof hexRaw === 'string' && hexRaw.startsWith('#') ? hexRaw.slice(1) : hexRaw;
136
+ const hexValid = !isEmpty(hexNormalized) && isValidHex(String(hexNormalized));
137
+ if (hexValid) {
138
+ row.hex = hexNormalized;
139
+ delete row.red;
140
+ delete row.green;
141
+ delete row.blue;
142
+ continue;
143
+ }
144
+ const rgbAllValid = parseRgbComponent(row.red) !== null &&
145
+ parseRgbComponent(row.green) !== null &&
146
+ parseRgbComponent(row.blue) !== null;
147
+ if (rgbAllValid) {
148
+ if (!isEmpty(hexNormalized)) {
149
+ row.hex = hexNormalized;
150
+ }
151
+ else if ('hex' in row) {
152
+ delete row.hex;
153
+ }
154
+ continue;
155
+ }
156
+ row.hex = exports.DEFAULT_HEX;
157
+ delete row.red;
158
+ delete row.green;
159
+ delete row.blue;
160
+ }
161
+ return rows;
162
+ }
163
+ exports.applyHexPriorityToRows = applyHexPriorityToRows;
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const color_validation_1 = require("./color-validation");
4
+ describe('isValidHex', () => {
5
+ it.each(['#ff0000', 'ff0000', '#FFF', 'fff', '#1a2b3c', '#FFFFFFFF'])('accepts %s', (hex) => {
6
+ expect((0, color_validation_1.isValidHex)(hex)).toBe(true);
7
+ });
8
+ it.each(['', 'notahex', 'gggggg', '#12345', '#1234567', ' ', '#'])('rejects %s', (hex) => {
9
+ expect((0, color_validation_1.isValidHex)(hex)).toBe(false);
10
+ });
11
+ });
12
+ describe('parseRgbComponent', () => {
13
+ it('parses valid integers in range', () => {
14
+ expect((0, color_validation_1.parseRgbComponent)('0')).toBe(0);
15
+ expect((0, color_validation_1.parseRgbComponent)('255')).toBe(255);
16
+ expect((0, color_validation_1.parseRgbComponent)(128)).toBe(128);
17
+ });
18
+ it('rejects out-of-range, non-integer, and empty values', () => {
19
+ expect((0, color_validation_1.parseRgbComponent)('-1')).toBe(null);
20
+ expect((0, color_validation_1.parseRgbComponent)('256')).toBe(null);
21
+ expect((0, color_validation_1.parseRgbComponent)('1.5')).toBe(null);
22
+ expect((0, color_validation_1.parseRgbComponent)('foo')).toBe(null);
23
+ expect((0, color_validation_1.parseRgbComponent)('')).toBe(null);
24
+ expect((0, color_validation_1.parseRgbComponent)(null)).toBe(null);
25
+ expect((0, color_validation_1.parseRgbComponent)(undefined)).toBe(null);
26
+ });
27
+ });
28
+ describe('validateColorRow', () => {
29
+ it('returns no errors when only valid hex is present', () => {
30
+ expect((0, color_validation_1.validateColorRow)({ hex: '#ff0000' })).toEqual({ errors: [], warnings: [] });
31
+ });
32
+ it('returns no errors when only valid RGB is present', () => {
33
+ expect((0, color_validation_1.validateColorRow)({ red: '255', green: '0', blue: '0' })).toEqual({
34
+ errors: [],
35
+ warnings: [],
36
+ });
37
+ });
38
+ it('errors on invalid hex', () => {
39
+ const result = (0, color_validation_1.validateColorRow)({ hex: 'zzz' });
40
+ expect(result.errors).toEqual([
41
+ { columnName: 'hex', message: color_validation_1.COLOR_VALIDATION_MESSAGES.HEX_INVALID('zzz') },
42
+ ]);
43
+ });
44
+ it('warns when both hex and RGB are populated', () => {
45
+ const result = (0, color_validation_1.validateColorRow)({ hex: '#fff', red: '1', green: '2', blue: '3' });
46
+ expect(result.errors).toEqual([]);
47
+ expect(result.warnings).toEqual([
48
+ { columnName: 'hex', message: color_validation_1.COLOR_VALIDATION_MESSAGES.HEX_PRIORITIZED_OVER_RGB },
49
+ ]);
50
+ });
51
+ it('errors per-missing-column when RGB is partial and hex is missing', () => {
52
+ const result = (0, color_validation_1.validateColorRow)({ red: '255' });
53
+ expect(result.errors).toEqual([
54
+ { columnName: 'green', message: color_validation_1.COLOR_VALIDATION_MESSAGES.RGB_INCOMPLETE(['green', 'blue']) },
55
+ { columnName: 'blue', message: color_validation_1.COLOR_VALIDATION_MESSAGES.RGB_INCOMPLETE(['green', 'blue']) },
56
+ ]);
57
+ });
58
+ it('errors when an RGB component is out of range', () => {
59
+ const result = (0, color_validation_1.validateColorRow)({ red: '999', green: '0', blue: '0' });
60
+ expect(result.errors).toEqual([
61
+ { columnName: 'red', message: color_validation_1.COLOR_VALIDATION_MESSAGES.RGB_OUT_OF_RANGE('red', '999') },
62
+ ]);
63
+ });
64
+ it('reports no errors when neither hex nor RGB is present', () => {
65
+ expect((0, color_validation_1.validateColorRow)({ name: 'foo' })).toEqual({ errors: [], warnings: [] });
66
+ });
67
+ it('resolves federatedMappings to source columns', () => {
68
+ const row = { hexcode: '#fff', r: '255', g: '0', b: '0' };
69
+ const mappings = { hex: 'hexcode', red: 'r', green: 'g', blue: 'b' };
70
+ const result = (0, color_validation_1.validateColorRow)(row, mappings);
71
+ expect(result.errors).toEqual([]);
72
+ expect(result.warnings).toEqual([
73
+ { columnName: 'hex', message: color_validation_1.COLOR_VALIDATION_MESSAGES.HEX_PRIORITIZED_OVER_RGB },
74
+ ]);
75
+ });
76
+ });
77
+ describe('applyHexPriorityToRows', () => {
78
+ it('strips RGB columns when hex is valid', () => {
79
+ const result = (0, color_validation_1.applyHexPriorityToRows)([
80
+ { hex: '#ff0000', red: '255', green: '0', blue: '0', name: 'foo' },
81
+ ]);
82
+ expect(result).toEqual([{ hex: 'ff0000', name: 'foo' }]);
83
+ });
84
+ it('keeps RGB and removes empty hex when hex is absent', () => {
85
+ const result = (0, color_validation_1.applyHexPriorityToRows)([{ red: '1', green: '2', blue: '3', name: 'foo' }]);
86
+ expect(result).toEqual([{ red: '1', green: '2', blue: '3', name: 'foo' }]);
87
+ });
88
+ it('defaults to DEFAULT_HEX when neither valid hex nor full RGB is present', () => {
89
+ const result = (0, color_validation_1.applyHexPriorityToRows)([{ name: 'foo' }]);
90
+ expect(result).toEqual([{ name: 'foo', hex: color_validation_1.DEFAULT_HEX }]);
91
+ });
92
+ it('treats invalid hex as missing and falls through to RGB or default', () => {
93
+ const result = (0, color_validation_1.applyHexPriorityToRows)([
94
+ { hex: 'zzz', red: '1', green: '2', blue: '3', name: 'with-rgb' },
95
+ { hex: 'zzz', name: 'no-rgb' },
96
+ ]);
97
+ expect(result).toEqual([
98
+ { hex: 'zzz', red: '1', green: '2', blue: '3', name: 'with-rgb' },
99
+ { hex: color_validation_1.DEFAULT_HEX, name: 'no-rgb' },
100
+ ]);
101
+ });
102
+ });
@@ -53,7 +53,6 @@ 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.`);
57
56
  continue;
58
57
  }
59
58
  validTypePaths.push(typePath);
@@ -1,9 +1,6 @@
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
- };
7
4
  export declare const CUSTOM_ENTITY_PROCESSOR_ERRORS: {
8
5
  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.";
9
6
  };
@@ -1,12 +1,9 @@
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_WARNINGS = 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_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
- };
10
7
  exports.CUSTOM_ENTITY_PROCESSOR_ERRORS = {
11
8
  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.',
12
9
  };
@@ -6,14 +6,7 @@ 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
- 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
- }
9
+ const color_validation_1 = require("../../common/color-validation");
17
10
  class ColorProcessorV2 extends base_processor_v2_1.BaseProcessorV2 {
18
11
  static requiredProperties = ['typePath', 'typeId', 'federatedId', 'name', 'hex', 'red', 'green', 'blue'];
19
12
  static canAddProperty(allPropertiesSlugs, propertyName) {
@@ -23,7 +16,7 @@ class ColorProcessorV2 extends base_processor_v2_1.BaseProcessorV2 {
23
16
  if (!data) {
24
17
  return [];
25
18
  }
26
- return removeHexHash(data);
19
+ return (0, color_validation_1.applyHexPriorityToRows)(data);
27
20
  }
28
21
  static validateLoadFile(data, config) {
29
22
  const { errors, warnings, generalErrors, generalWarnings, csvHeaders, returnImmediately } = entity_processor_v2_1.EntityProcessorV2.validateLoadFile(data, config);
@@ -41,20 +34,13 @@ class ColorProcessorV2 extends base_processor_v2_1.BaseProcessorV2 {
41
34
  if (duplicateFederatedIdErrors.length) {
42
35
  errors.push(...duplicateFederatedIdErrors);
43
36
  }
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
- }
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
+ });
58
44
  return { errors, warnings, generalErrors, generalWarnings };
59
45
  }
60
46
  static validateTypePath(data, config, csvHeaders, generalErrors) {
@@ -3,6 +3,7 @@ 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");
6
7
  describe('ColorProcessorV2', () => {
7
8
  const loaderProcess = {
8
9
  id: 'mock-loader-process-id',
@@ -219,72 +220,38 @@ describe('ColorProcessorV2', () => {
219
220
  });
220
221
  });
221
222
  describe('color column validation', () => {
222
- it('should warn when both hex and RGB columns are present', () => {
223
+ it('warns per-row when both hex and RGB are populated (hex prioritized)', () => {
223
224
  const data = [
224
- {
225
- name: 'test-color',
226
- red: '255',
227
- green: '0',
228
- blue: '0',
229
- hex: '#ff0000',
230
- },
225
+ { name: 'test-color', red: '255', green: '0', blue: '0', hex: '#ff0000' },
231
226
  ];
232
- const config = {
233
- ...loaderProcess,
234
- federatedMappings: {
235
- ...loaderProcess.federatedMappings,
236
- red: 'red',
237
- green: 'green',
238
- blue: 'blue',
239
- hex: 'hex',
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
- });
247
- });
248
- it('should not warn when only RGB columns are present', () => {
249
- const data = [
227
+ const result = color_processor_v2_1.ColorProcessorV2.validateLoadFile(data, loaderProcess);
228
+ expect(result.warnings).toEqual([
250
229
  {
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',
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
+ ],
264
239
  },
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
- });
240
+ ]);
241
+ });
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([]);
271
247
  });
272
- it('should not warn when only hex column is present', () => {
248
+ it('does not warn when only a valid hex is populated', () => {
273
249
  const data = [{ name: 'test-color', hex: '#ff0000' }];
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
- });
250
+ const result = color_processor_v2_1.ColorProcessorV2.validateLoadFile(data, loaderProcess);
251
+ expect(result.warnings).toEqual([]);
252
+ expect(result.errors).toEqual([]);
286
253
  });
287
- it('should handle custom column names from federatedMappings', () => {
254
+ it('warns per-row when both hex and RGB are populated under federatedMappings', () => {
288
255
  const data = [{ name: 'test-color', r: '255', g: '0', b: '0', hexcode: '#ff0000' }];
289
256
  const config = {
290
257
  ...loaderProcess,
@@ -297,11 +264,94 @@ describe('ColorProcessorV2', () => {
297
264
  },
298
265
  };
299
266
  const result = color_processor_v2_1.ColorProcessorV2.validateLoadFile(data, config);
300
- expect(result.generalWarnings).toContainEqual({
301
- message: constants_1.COLOR_PROCESSOR_WARNINGS.HEX_IGNORED_WARNING,
302
- issueType: entity_types_1.IssueType.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
+ ],
303
294
  });
304
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
+ ],
328
+ });
329
+ });
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
+ });
305
355
  });
306
356
  it('should return error if conditionalColumns format is incorrect', () => {
307
357
  const data = [{ name: 'color1' }];
@@ -67,7 +67,6 @@ 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...');
71
70
  const diffs = compareIncomingAndExisting(incoming, existing, fieldsToSkip, fieldsToSkipIfOnlyOnExisting, type);
72
71
  return diffs?.length > 0;
73
72
  }
@@ -253,7 +253,6 @@ 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));
257
256
  const { incomingAssortmentItems, badAssortmentItems } = this.processIncomingData(data, allPropertiesSlugs, typePropertyToValueType);
258
257
  const targetAssortment = await (0, item_helper_1.getAssortment)(client, assortmentId, assortmentIdentifier, data);
259
258
  if (!targetAssortment) {
@@ -60,7 +60,7 @@ class ItemProcessor extends base_processor_v2_1.BaseProcessorV2 {
60
60
  return { errors, warnings, generalErrors, generalWarnings };
61
61
  }
62
62
  this.validateLoadType(config, generalErrors);
63
- this.validateTypePathMapping(config, csvHeaders, data, generalErrors);
63
+ this.validateTypePathMapping(data, generalErrors);
64
64
  this.validateOptionNameMapping(config, csvHeaders, generalWarnings);
65
65
  this.validateItemOptionFederatedIdMapping(config, csvHeaders, generalWarnings);
66
66
  this.validateFederatedIds(data, config, errors, generalErrors, csvHeaders);
@@ -75,27 +75,13 @@ class ItemProcessor extends base_processor_v2_1.BaseProcessorV2 {
75
75
  });
76
76
  }
77
77
  }
78
- static validateTypePathMapping(config, csvHeaders, data, generalErrors) {
79
- if (Array.isArray(config?.conditionalColumns) && config.conditionalColumns.length) {
80
- const hasTypePathColumn = config.conditionalColumns.some((column) => column.toProperty === 'typePath');
81
- const hasRequiredConditionalColumn = config.conditionalColumns.some((column) => column.toProperty === 'typePath' &&
82
- (column.default === 'item' ||
83
- (column.default?.startsWith('item:') && column.default?.split(':')[1]?.length > 0)));
84
- if (hasTypePathColumn && !hasRequiredConditionalColumn) {
85
- generalErrors.push({
86
- message: constants_1.ITEM_PROCESSOR_ERRORS.TYPEPATH_INVALID,
87
- issueType: entity_types_1.IssueType.CONFIG,
88
- });
89
- }
90
- }
91
- else if (csvHeaders.includes('typePath')) {
92
- const hasCorrectTypePath = data.every((row) => row.typePath === 'item' || (row.typePath?.startsWith('item:') && row.typePath?.split(':')[1]?.length > 0));
93
- if (!hasCorrectTypePath) {
94
- generalErrors.push({
95
- message: constants_1.ITEM_PROCESSOR_ERRORS.TYPEPATH_INVALID,
96
- issueType: entity_types_1.IssueType.CONFIG,
97
- });
98
- }
78
+ static validateTypePathMapping(data, generalErrors) {
79
+ const hasCorrectTypePath = data.every((row) => row.typePath === 'item' || (row.typePath?.startsWith('item:') && row.typePath?.split(':')[1]?.length > 0));
80
+ if (!hasCorrectTypePath) {
81
+ generalErrors.push({
82
+ message: constants_1.ITEM_PROCESSOR_ERRORS.TYPEPATH_INVALID,
83
+ issueType: entity_types_1.IssueType.CONFIG,
84
+ });
99
85
  }
100
86
  }
101
87
  static validateOptionNameMapping(config, csvHeaders, generalWarnings) {