@contrail/cli 3.5.0 → 3.5.1-alpha.64bf880
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 -25
- package/dist/api/types.d.ts +0 -5
- package/dist/api/types.js +1 -36
- package/dist/commands/types/getAll.js +0 -18
- package/dist/commands/types/loadAll.js +0 -2
- package/dist/commands/types/mergeTypeProperties.js +0 -5
- package/dist/commands/types/uploadOptionSetHierarchies.js +0 -117
- package/dist/commands/types/uploadOptionSets.js +1 -1
- package/dist/commands/types/uploadRuleSets.js +2 -85
- package/dist/commands/types/uploadTypeOverrides.js +10 -2
- package/dist/commands/types/uploadTypePolicies.js +2 -86
- package/dist/commands/types/uploadTypePropertyPolicies.d.ts +0 -1
- package/dist/commands/types/uploadTypePropertyPolicies.js +3 -139
- package/dist/helpers/types/type-uploader.js +1 -1
- package/dist/interfaces.d.ts +0 -25
- package/dist/templates/typescript/tsup.config.ts +0 -0
- 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.d.ts +1 -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 -52
- package/package.json +3 -3
- package/dist/commands/types/uploadUsageInfo.d.ts +0 -15
- package/dist/commands/types/uploadUsageInfo.js +0 -180
- package/dist/commands/types/uploadUsageInfo.spec.js +0 -249
- /package/{dist/commands/types/uploadUsageInfo.spec.d.ts → node_modules/@contrail/loader/lib/common/color-validation.spec.d.ts} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
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
|
|
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
|
-
|
|
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;
|
|
@@ -303,7 +303,7 @@ async function uploadReducedTypeProperties(type, currentPropertyTypes, newProper
|
|
|
303
303
|
}
|
|
304
304
|
}
|
|
305
305
|
catch (err) {
|
|
306
|
-
|
|
306
|
+
logger_1.default.error(`Error Deleting Type-Property ${currentPropertyType.slug}: ${err}`);
|
|
307
307
|
}
|
|
308
308
|
}
|
|
309
309
|
}
|
package/dist/interfaces.d.ts
CHANGED
|
@@ -370,31 +370,6 @@ export interface ReducedOptionSet {
|
|
|
370
370
|
isArchived: boolean;
|
|
371
371
|
optionSet: any[];
|
|
372
372
|
}
|
|
373
|
-
export interface ReducedUsageInfo {
|
|
374
|
-
typeId: string;
|
|
375
|
-
typePropertyId: string;
|
|
376
|
-
typePath: string;
|
|
377
|
-
typePropertySlug: string;
|
|
378
|
-
editable: boolean;
|
|
379
|
-
used: boolean;
|
|
380
|
-
formulaFunction?: string;
|
|
381
|
-
validationFunction?: string;
|
|
382
|
-
numberFormat?: any;
|
|
383
|
-
defaultValue?: string;
|
|
384
|
-
label?: string;
|
|
385
|
-
required?: boolean;
|
|
386
|
-
searchable?: boolean;
|
|
387
|
-
isUniqueProperty?: boolean;
|
|
388
|
-
isUniquePropertyInFamily?: boolean;
|
|
389
|
-
copyBehavior?: string;
|
|
390
|
-
copyDefault?: string;
|
|
391
|
-
propertyLevel?: string;
|
|
392
|
-
isArchived: boolean;
|
|
393
|
-
isFederationProperty?: boolean;
|
|
394
|
-
carryOverBehavior?: string;
|
|
395
|
-
carryOverDefault?: string;
|
|
396
|
-
}
|
|
397
|
-
export type PersistReducedUsageInfo = Omit<ReducedUsageInfo, 'typePath' | 'typePropertySlug'>;
|
|
398
373
|
export declare enum PropertyType {
|
|
399
374
|
Text = "text",
|
|
400
375
|
String = "string",
|
|
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
|
-
|
|
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[];
|