@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
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.findAllOccurrences = exports.uploadTypePropertyPolicies = void 0;
3
+ exports.uploadTypePropertyPolicies = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const sdk_1 = require("@contrail/sdk");
6
6
  const fs_1 = tslib_1.__importDefault(require("fs"));
@@ -8,12 +8,10 @@ const path = tslib_1.__importStar(require("path"));
8
8
  const login_1 = require("../../auth/login");
9
9
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
10
10
  const core_1 = require("@oclif/core");
11
- const type_extractor_1 = require("../../helpers/types/type-extractor");
12
11
  const users_1 = require("../../api/users");
13
12
  const basecommand_1 = require("../../basecommand");
14
13
  const logger_1 = tslib_1.__importDefault(require("../../logger"));
15
14
  const js_yaml_1 = tslib_1.__importDefault(require("js-yaml"));
16
- const _ = tslib_1.__importStar(require("lodash"));
17
15
  class UploadTypePropertyPolicies extends basecommand_1.BaseCommand {
18
16
  static description = 'Upload type property policies';
19
17
  static examples = [
@@ -38,14 +36,12 @@ async function uploadTypePropertyPolicies(dir) {
38
36
  try {
39
37
  await (0, login_1.authUser)();
40
38
  logger_1.default.info(`${chalk_1.default.yellow.bold('TYPE PROPERTY POLICY')} Uploading`);
41
- const [reducedTypePropertyPolicies, reducedTypePropertyPolicyLinks] = await loadTPPolicies(dir);
39
+ const reducedTypePropertyPolicies = await loadTPPolicies(dir);
42
40
  await uploadTPPolicyObjects(reducedTypePropertyPolicies);
43
- await uploadTypePropertyPolicyLinks(reducedTypePropertyPolicyLinks);
44
41
  logger_1.default.info(`${chalk_1.default.yellow.bold('TYPE PROPERTY POLICY')} Uploaded`);
45
42
  }
46
43
  catch (e) {
47
44
  logger_1.default.info(`${chalk_1.default.red.bold('FAIL')} Type Property Policy Upload Failed . ${e}`);
48
- //TODO make this only print if a debug flag is set
49
45
  console.trace(e);
50
46
  return;
51
47
  }
@@ -54,15 +50,13 @@ async function uploadTypePropertyPolicies(dir) {
54
50
  exports.uploadTypePropertyPolicies = uploadTypePropertyPolicies;
55
51
  function loadTPPolicies(fromDir) {
56
52
  const tpps = [];
57
- let tppLinks = [];
58
53
  const dir = path.join(fromDir, 'type-property-policies');
59
54
  try {
60
55
  const files = fs_1.default.readdirSync(dir);
61
56
  for (const file of files) {
62
57
  tpps.push(js_yaml_1.default.load(fs_1.default.readFileSync(path.join(dir, file), 'utf8')));
63
58
  }
64
- tppLinks = js_yaml_1.default.load(fs_1.default.readFileSync(path.join(fromDir, 'type-property-policy-links.yml'), 'utf8'));
65
- return [tpps, tppLinks];
59
+ return tpps;
66
60
  }
67
61
  catch (e) {
68
62
  throw new Error(`ERROR PARSING YAML: ${e}`);
@@ -155,133 +149,3 @@ async function uploadTPPolicyObjects(reducedTypePropertyPolicies) {
155
149
  }
156
150
  }
157
151
  }
158
- async function uploadTypePropertyPolicyLinks(reducedTypePropertyPolicyLinks) {
159
- const typesWithProperties = await (0, type_extractor_1.getTypes)();
160
- const typeProperties = [];
161
- typesWithProperties.forEach((type) => {
162
- if (type.typeProperties) {
163
- for (const tp of type.typeProperties) {
164
- tp.typeId = type.id;
165
- tp.typeSlug = type.slug;
166
- typeProperties.push(tp);
167
- }
168
- }
169
- });
170
- const currentTPPs = await new sdk_1.Entities().get({
171
- entityName: 'type-property-policy',
172
- });
173
- const currentTPPLinks = await new sdk_1.Entities().get({
174
- entityName: 'type-property-policy-link',
175
- });
176
- logger_1.default.info(`found ${currentTPPLinks.length} current type property policy links`);
177
- logger_1.default.info(`found ${reducedTypePropertyPolicyLinks.length} type property policy links to load`);
178
- const currentLinksHashes = {};
179
- for (const currentLink of currentTPPLinks) {
180
- const foundTPs = findAllOccurrences(typeProperties, (tp) => tp.id === currentLink.typePropertyId);
181
- for (const foundTP of foundTPs) {
182
- const foundTPP = currentTPPs.find((tpp) => tpp.id === currentLink.typePropertyPolicyId);
183
- if (!foundTP || !foundTPP) {
184
- logger_1.default.info(`skipping unhashable, invalid policy link: ${JSON.stringify(currentLink, null, 2)}`);
185
- continue;
186
- }
187
- const hash = `${foundTP.typeSlug}|${foundTP.slug}|${foundTPP.identifier}`;
188
- currentLinksHashes[hash] = currentLink;
189
- }
190
- }
191
- logger_1.default.debug(`current links hashes: ${JSON.stringify(currentLinksHashes, null, 2)}`);
192
- const reducedLinksByHashes = {};
193
- for (const loadedLink of reducedTypePropertyPolicyLinks) {
194
- const hash = `${loadedLink.typeSlug}|${loadedLink.typePropertySlug}|${loadedLink.typePropertyPolicyIdentifier}`;
195
- reducedLinksByHashes[hash] = loadedLink;
196
- }
197
- const toCreate = Object.keys(reducedLinksByHashes)
198
- .filter((x) => !currentLinksHashes[x])
199
- .map((x) => {
200
- return {
201
- [x]: reducedLinksByHashes[x],
202
- };
203
- })
204
- .reduce((a, b) => Object.assign(a, b), {});
205
- const toDelete = _.uniqBy(Object.keys(currentLinksHashes)
206
- .filter((x) => !reducedLinksByHashes[x])
207
- .map((x) => {
208
- return {
209
- [x]: currentLinksHashes[x],
210
- };
211
- }), 'id').reduce((a, b) => Object.assign(a, b), {});
212
- logger_1.default.debug(`reduced links hashes: ${JSON.stringify(reducedLinksByHashes, null, 2)}`);
213
- logger_1.default.info(`found ${Object.keys(toCreate).length} type property policy links to create`);
214
- logger_1.default.info(`found ${Object.keys(toDelete).length} type property policy links to delete`);
215
- logger_1.default.debug(`to create: ${JSON.stringify(toCreate, null, 2)}`);
216
- logger_1.default.debug(`to delete: ${JSON.stringify(toDelete, null, 2)}`);
217
- logger_1.default.debug(`current links hashes to delete: ${JSON.stringify(Object.keys(currentLinksHashes).filter((x) => !reducedLinksByHashes[x]), null, 2)}`);
218
- // fs.writeFileSync('toCreate.json', JSON.stringify(toCreate, null, 2));
219
- // fs.writeFileSync('toDelete.json', JSON.stringify(toDelete, null, 2));
220
- // fs.writeFileSync('currentLinksHashes.json', JSON.stringify(currentLinksHashes, null, 2));
221
- // fs.writeFileSync('reducedLinksByHashes.json', JSON.stringify(reducedLinksByHashes, null, 2));
222
- // fs.writeFileSync(
223
- // 'current_link_hashes_to_delete.json',
224
- // JSON.stringify(
225
- // Object.keys(currentLinksHashes).filter((x) => !reducedLinksByHashes[x]),
226
- // null,
227
- // 2,
228
- // ),
229
- // );
230
- // return;
231
- for (const [hash, link] of Object.entries(toCreate)) {
232
- try {
233
- if (!transformReducedLink(link, typeProperties, currentTPPs)) {
234
- continue;
235
- }
236
- logger_1.default.info(`Creating: ${hash}: ${JSON.stringify(link, null, 2)}`);
237
- await new sdk_1.Entities().create({
238
- entityName: 'type-property-policy-link',
239
- object: {
240
- typePropertyPolicyId: link.typePropertyPolicyId,
241
- typePropertyId: link.typePropertyId,
242
- },
243
- });
244
- }
245
- catch (err) {
246
- logger_1.default.error(`Error Uploading type property policy links: ${err}`);
247
- }
248
- }
249
- for (const [hash, link] of Object.entries(toDelete)) {
250
- try {
251
- logger_1.default.info(`Deleting: ${hash}`);
252
- await new sdk_1.Entities().delete({
253
- entityName: 'type-property-policy-link',
254
- id: link['id'],
255
- });
256
- }
257
- catch (err) {
258
- logger_1.default.error(`Error Deleting type property policy link: ${err}`);
259
- }
260
- }
261
- function transformReducedLink(TPPLink, typeProperties, typePropertyPolicies) {
262
- const foundTP = typeProperties.find((tp) => tp.slug === TPPLink.typePropertySlug && tp.typeSlug === TPPLink.typeSlug);
263
- if (!foundTP) {
264
- logger_1.default.error(`Could not find type property with slug ${TPPLink.typePropertySlug} `);
265
- return false;
266
- }
267
- TPPLink.typePropertyId = foundTP.id;
268
- const foundTPP = typePropertyPolicies.find((tpp) => tpp.identifier === TPPLink.typePropertyPolicyIdentifier);
269
- if (!foundTPP) {
270
- logger_1.default.error(`Could not find type property policy with identifier ${TPPLink.typePropertyPolicyIdentifier} `);
271
- return false;
272
- }
273
- TPPLink.typePropertyPolicyId = foundTPP.id;
274
- return true;
275
- }
276
- }
277
- function findAllOccurrences(array, evaluator) {
278
- const found = [];
279
- for (let i = 0; i < array.length; i++) {
280
- const element = array[i];
281
- if (evaluator(element)) {
282
- found.push(element);
283
- }
284
- }
285
- return found;
286
- }
287
- exports.findAllOccurrences = findAllOccurrences;
@@ -2,6 +2,9 @@ import { BaseCommand } from '../../basecommand';
2
2
  import { PersistReducedUsageInfo } from '../../interfaces';
3
3
  export default class UploadUsageInfo extends BaseCommand {
4
4
  static description: string;
5
+ static deprecationOptions: {
6
+ message: string;
7
+ };
5
8
  static examples: string[];
6
9
  static flags: {
7
10
  dir: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
@@ -13,7 +13,10 @@ const basecommand_1 = require("../../basecommand");
13
13
  const logger_1 = tslib_1.__importDefault(require("../../logger"));
14
14
  const js_yaml_1 = tslib_1.__importDefault(require("js-yaml"));
15
15
  class UploadUsageInfo extends basecommand_1.BaseCommand {
16
- static description = 'Upload extracted usage info';
16
+ static description = '[DEPRECATED] Upload extracted usage info. Use uploadTypeOverrides instead.';
17
+ static deprecationOptions = {
18
+ message: 'uploadUsageInfo is deprecated. Usage info is now managed as type overrides. Use `contrail types uploadTypeOverrides` instead.',
19
+ };
17
20
  static examples = [
18
21
  `$ contrail types uploadUsageInfo
19
22
  `,
@@ -303,7 +303,7 @@ async function uploadReducedTypeProperties(type, currentPropertyTypes, newProper
303
303
  }
304
304
  }
305
305
  catch (err) {
306
- throw new Error(`Error Deleting Properties: ${err}`);
306
+ logger_1.default.error(`Error Deleting Type-Property ${currentPropertyType.slug}: ${err}`);
307
307
  }
308
308
  }
309
309
  }
File without changes
@@ -5,14 +5,7 @@ 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
- 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
- }
8
+ const color_validation_1 = require("./common/color-validation");
16
9
  class ColorProcessor extends base_processor_1.BaseProcessor {
17
10
  static typesToTransformBy = ['color'];
18
11
  static requiredProperties = ['typePath', 'typeId', 'federatedId', 'name', 'hex', 'red', 'green', 'blue'];
@@ -20,7 +13,7 @@ class ColorProcessor extends base_processor_1.BaseProcessor {
20
13
  if (!data) {
21
14
  return [];
22
15
  }
23
- return removeHexHash(data);
16
+ return (0, color_validation_1.applyHexPriorityToRows)(data);
24
17
  }
25
18
  static validateTypePath(data, config, csvHeaders, generalErrors) {
26
19
  const typePathSource = {
@@ -56,20 +49,13 @@ class ColorProcessor extends base_processor_1.BaseProcessor {
56
49
  static validateLoadFile(data, config) {
57
50
  const { errors, warnings, generalErrors, generalWarnings, csvHeaders } = entity_processor_1.EntityProcessor.validateLoadFile(data, config, entity_types_1.LoadType.COLOR);
58
51
  this.validateTypePath(data, config, csvHeaders, generalErrors);
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
- }
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
+ });
73
59
  return { errors, warnings, generalErrors, generalWarnings };
74
60
  }
75
61
  }
@@ -3,6 +3,7 @@ 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");
6
7
  describe('ColorProcessor', () => {
7
8
  const loaderProcess = {
8
9
  id: 'mock-loader-process-id',
@@ -219,72 +220,38 @@ describe('ColorProcessor', () => {
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_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
- });
247
- });
248
- it('should not warn when only RGB columns are present', () => {
249
- const data = [
227
+ const result = color_processor_1.ColorProcessor.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_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
- });
240
+ ]);
271
241
  });
272
- it('should not warn when only hex column is present', () => {
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([]);
247
+ });
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_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
- });
250
+ const result = color_processor_1.ColorProcessor.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('ColorProcessor', () => {
297
264
  },
298
265
  };
299
266
  const result = color_processor_1.ColorProcessor.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_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
+ ],
303
328
  });
304
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_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 }]);
305
355
  });
306
356
  });
307
357
  });
@@ -2,5 +2,5 @@ interface ChunkByUrlLengthOptions {
2
2
  maxBytes?: number;
3
3
  maxCount?: number;
4
4
  }
5
- export declare function chunkByUrlLength(ids: string[], options?: ChunkByUrlLengthOptions): string[][];
5
+ export declare function chunkByUrlLength(ids: readonly string[] | null | undefined, options?: ChunkByUrlLengthOptions): string[][];
6
6
  export {};
@@ -6,6 +6,8 @@ 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 [];
9
11
  const maxBytes = options.maxBytes ?? constants_1.FEDERATED_ID_FETCH_MAX_URL_BYTES;
10
12
  const maxCount = options.maxCount ?? constants_1.FEDERATED_ID_FETCH_MAX_COUNT;
11
13
  const result = [];
@@ -5,6 +5,10 @@ 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
+ });
8
12
  it('packs short ids into a single chunk up to maxCount', () => {
9
13
  const ids = Array.from({ length: 50 }, (_, i) => `id-${i}`);
10
14
  const chunks = (0, chunk_by_url_length_1.chunkByUrlLength)(ids, { maxCount: 200, maxBytes: 10000 });
@@ -0,0 +1,26 @@
1
+ import { ProcessingIssues } from '@contrail/entity-types';
2
+ export declare const COLOR_VALIDATION_MESSAGES: {
3
+ readonly HEX_INVALID: (hex: string) => string;
4
+ readonly RGB_INCOMPLETE: (missingColumns: string[]) => string;
5
+ readonly RGB_OUT_OF_RANGE: (column: string, value: string) => string;
6
+ readonly 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.";
7
+ };
8
+ export declare const DEFAULT_HEX = "000000";
9
+ export declare function isValidHex(hex: string): boolean;
10
+ export declare function parseRgbComponent(value: any): number | null;
11
+ export interface ColorRowValidationResult {
12
+ errors: {
13
+ columnName: string;
14
+ message: string;
15
+ }[];
16
+ warnings: {
17
+ columnName: string;
18
+ message: string;
19
+ }[];
20
+ }
21
+ export declare function validateColorRow(row: Record<string, any>, federatedMappings?: Record<string, string>): ColorRowValidationResult;
22
+ export declare function buildColorRowIssues(row: Record<string, any>, rowNumber: number, federatedMappings?: Record<string, string>): {
23
+ rowErrors: ProcessingIssues | null;
24
+ rowWarnings: ProcessingIssues | null;
25
+ };
26
+ export declare function applyHexPriorityToRows<T extends Record<string, any>>(rows: T[]): T[];