@contrail/cli 3.5.0-alpha.dd20a19 → 3.5.1-alpha.68d57e0
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.
- package/README.md +1 -33
- package/node_modules/@contrail/loader/lib/color-processor.js +9 -23
- package/node_modules/@contrail/loader/lib/color-processor.spec.js +112 -62
- package/node_modules/@contrail/loader/lib/common/chunk-by-url-length.d.ts +1 -1
- package/node_modules/@contrail/loader/lib/common/chunk-by-url-length.js +2 -0
- package/node_modules/@contrail/loader/lib/common/chunk-by-url-length.spec.js +4 -0
- package/node_modules/@contrail/loader/lib/common/color-validation.d.ts +26 -0
- package/node_modules/@contrail/loader/lib/common/color-validation.js +163 -0
- package/node_modules/@contrail/loader/lib/common/color-validation.spec.js +102 -0
- package/node_modules/@contrail/loader/lib/common/size-range-helpers.js +0 -1
- package/node_modules/@contrail/loader/lib/constants.d.ts +0 -3
- package/node_modules/@contrail/loader/lib/constants.js +1 -4
- package/node_modules/@contrail/loader/lib/entities-v2/colors-v2/color-processor-v2.js +9 -23
- package/node_modules/@contrail/loader/lib/entities-v2/colors-v2/color-processor-v2.spec.js +112 -62
- package/node_modules/@contrail/loader/lib/entities-v2/entities-helper.js +0 -1
- package/node_modules/@contrail/loader/lib/entities-v2/linesheet-v2/assortment-processor.js +0 -1
- package/node_modules/@contrail/loader/lib/entities-v2/linesheet-v2/item-processor.js +8 -22
- package/node_modules/@contrail/loader/lib/entities-v2/linesheet-v2/item-processor.spec.js +43 -1
- package/node_modules/@contrail/loader/lib/pipeline/identifier-resolver.d.ts +5 -0
- package/node_modules/@contrail/loader/lib/pipeline/identifier-resolver.js +562 -0
- package/node_modules/@contrail/loader/lib/pipeline/identifier-resolver.spec.js +231 -0
- package/node_modules/@contrail/loader/lib/pipeline/item-execute-helpers.js +77 -3
- package/node_modules/@contrail/loader/lib/pipeline/item-execute-helpers.spec.js +82 -0
- package/node_modules/@contrail/loader/lib/pipeline/preview.js +0 -5
- package/node_modules/@contrail/loader/lib/pipeline/preview.spec.js +2 -2
- package/node_modules/@contrail/loader/lib/pipeline/project-item-execute-helpers.js +1 -1
- package/node_modules/@contrail/loader/lib/pipeline/transform.js +18 -0
- package/node_modules/@contrail/loader/lib/pipeline/transform.spec.js +3 -2
- package/node_modules/@contrail/loader/lib/pipeline/types.d.ts +5 -2
- package/node_modules/@contrail/loader/lib/pipeline/types.js +2 -0
- package/node_modules/@contrail/loader/lib/pipeline/validate.js +184 -437
- package/node_modules/@contrail/loader/lib/pipeline/validate.spec.js +157 -53
- package/node_modules/@contrail/loader/package.json +3 -2
- package/node_modules/@contrail/sdk/.github/workflows/publish-to-npm.yml +120 -0
- package/node_modules/@contrail/sdk/lib/auth/login.js +42 -3
- package/node_modules/@contrail/sdk/lib/core/request.d.ts +3 -0
- package/node_modules/@contrail/sdk/lib/core/request.js +100 -4
- package/node_modules/@contrail/sdk/lib/entities/entities.client.js +1 -1
- package/node_modules/@contrail/sdk/lib/entities/entity.resource.registry.js +1 -0
- package/node_modules/@contrail/sdk/lib/files/files.d.ts +13 -3
- package/node_modules/@contrail/sdk/lib/files/files.js +66 -25
- package/node_modules/@contrail/sdk/package.json +4 -1
- package/node_modules/validate-color/LICENSE +21 -0
- package/node_modules/validate-color/README.md +265 -0
- package/node_modules/validate-color/lib/index.d.ts +10 -0
- package/node_modules/validate-color/lib/index.js +1 -0
- package/node_modules/validate-color/package.json +91 -0
- package/node_modules/validate-color/test/redos.js +29 -0
- package/oclif.manifest.json +1 -79
- package/package.json +3 -6
- package/dist/api/dash0/client.d.ts +0 -13
- package/dist/api/dash0/client.js +0 -53
- package/dist/api/dash0/spans.d.ts +0 -8
- package/dist/api/dash0/spans.js +0 -91
- package/dist/api/dash0/types.d.ts +0 -84
- package/dist/api/dash0/types.js +0 -4
- package/dist/commands/event-workflows/assess-impact.d.ts +0 -19
- package/dist/commands/event-workflows/assess-impact.js +0 -104
- package/dist/helpers/event-workflow-impact/aggregate.d.ts +0 -23
- package/dist/helpers/event-workflow-impact/aggregate.js +0 -49
- package/dist/helpers/event-workflow-impact/aggregate.spec.js +0 -109
- package/dist/helpers/event-workflow-impact/classify.d.ts +0 -13
- package/dist/helpers/event-workflow-impact/classify.js +0 -135
- package/dist/helpers/event-workflow-impact/classify.spec.js +0 -138
- package/dist/helpers/event-workflow-impact/render.d.ts +0 -12
- package/dist/helpers/event-workflow-impact/render.js +0 -62
- package/dist/helpers/event-workflow-impact/subtree.d.ts +0 -20
- package/dist/helpers/event-workflow-impact/subtree.js +0 -93
- package/dist/helpers/event-workflow-impact/subtree.spec.d.ts +0 -1
- package/dist/helpers/event-workflow-impact/subtree.spec.js +0 -103
- /package/{dist/helpers/event-workflow-impact/aggregate.spec.d.ts → node_modules/@contrail/loader/lib/common/color-validation.spec.d.ts} +0 -0
- /package/{dist/helpers/event-workflow-impact/classify.spec.d.ts → node_modules/@contrail/loader/lib/pipeline/identifier-resolver.spec.d.ts} +0 -0
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.
|
|
20
|
+
@contrail/cli/3.5.1-alpha.68d57e0 linux-x64 node-v24.14.1
|
|
21
21
|
$ contrail --help [COMMAND]
|
|
22
22
|
USAGE
|
|
23
23
|
$ contrail COMMAND
|
|
@@ -51,7 +51,6 @@ To get instructions on how to set up autocomplete for your shell, run `contrail
|
|
|
51
51
|
* [`contrail config list`](#contrail-config-list)
|
|
52
52
|
* [`contrail config set`](#contrail-config-set)
|
|
53
53
|
* [`contrail config use [NAME]`](#contrail-config-use-name)
|
|
54
|
-
* [`contrail event-workflows assess-impact IDENTIFIER`](#contrail-event-workflows-assess-impact-identifier)
|
|
55
54
|
* [`contrail file upload FILELOCATION CONTENTTYPE`](#contrail-file-upload-filelocation-contenttype)
|
|
56
55
|
* [`contrail groups export`](#contrail-groups-export)
|
|
57
56
|
* [`contrail groups import GROUPSFILE`](#contrail-groups-import-groupsfile)
|
|
@@ -537,37 +536,6 @@ EXAMPLES
|
|
|
537
536
|
$ contrail config use demo-org
|
|
538
537
|
```
|
|
539
538
|
|
|
540
|
-
## `contrail event-workflows assess-impact IDENTIFIER`
|
|
541
|
-
|
|
542
|
-
Sample Dash0 traces of an event-workflow action and report per-invocation downstream impact.
|
|
543
|
-
|
|
544
|
-
```
|
|
545
|
-
USAGE
|
|
546
|
-
$ contrail event-workflows assess-impact IDENTIFIER [-l error|warn|info|debug] [-e true|false] [-s <value>] [--lookback <value>]
|
|
547
|
-
[--dataset <value>] [-j]
|
|
548
|
-
|
|
549
|
-
ARGUMENTS
|
|
550
|
-
IDENTIFIER Action identifier (contrail.event_workflow.action.identifier).
|
|
551
|
-
|
|
552
|
-
FLAGS
|
|
553
|
-
-e, --alwaysConfirmEnv=<option> [default: true] The Environment variables
|
|
554
|
-
<options: true|false>
|
|
555
|
-
-j, --json JSON output (not yet implemented; flag reserved).
|
|
556
|
-
-l, --logLevel=<option> The Log Level
|
|
557
|
-
<options: error|warn|info|debug>
|
|
558
|
-
-s, --samples=<value> [default: 50] Number of distinct traces to sample (10-100).
|
|
559
|
-
--dataset=<value> [default: prod] Dash0 dataset.
|
|
560
|
-
--lookback=<value> [default: 2d] How far back to search. Format: 30m | 6h | 2d.
|
|
561
|
-
|
|
562
|
-
DESCRIPTION
|
|
563
|
-
Sample Dash0 traces of an event-workflow action and report per-invocation downstream impact.
|
|
564
|
-
|
|
565
|
-
EXAMPLES
|
|
566
|
-
$ contrail event-workflows assess-impact @gap-automations:bomCcCostUpdate
|
|
567
|
-
|
|
568
|
-
$ contrail event-workflows assess-impact @gap-automations:bomCcCostUpdate --samples 25 --lookback 6h
|
|
569
|
-
```
|
|
570
|
-
|
|
571
539
|
## `contrail file upload FILELOCATION CONTENTTYPE`
|
|
572
540
|
|
|
573
541
|
Uploads a file to the VibeIQ platform
|
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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('
|
|
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
|
|
233
|
-
|
|
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
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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('
|
|
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
|
|
275
|
-
|
|
276
|
-
|
|
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('
|
|
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.
|
|
301
|
-
|
|
302
|
-
|
|
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[];
|
|
@@ -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
|
};
|