@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
|
@@ -0,0 +1,562 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveIdentifiers = exports.isProjectItemAndAssortmentLoad = void 0;
|
|
4
|
+
const entities_helper_1 = require("../entities-v2/entities-helper");
|
|
5
|
+
const types_1 = require("./types");
|
|
6
|
+
const CONFIG_CATEGORY = types_1.PreValidationCategory.CONFIG;
|
|
7
|
+
const MISSING_REQUIRED_FIELD_CATEGORY = types_1.PreValidationCategory.MISSING_REQUIRED_FIELD;
|
|
8
|
+
const REFERENCE_NOT_FOUND_CATEGORY = types_1.PreValidationCategory.REFERENCE_DATA_NOT_FOUND;
|
|
9
|
+
function isEmpty(value) {
|
|
10
|
+
return value === null || value === undefined || value === '';
|
|
11
|
+
}
|
|
12
|
+
function forEachRow(rows, callback) {
|
|
13
|
+
rows.forEach((row, index) => callback(row, index + 2));
|
|
14
|
+
}
|
|
15
|
+
function isProjectItemAndAssortmentLoad(config) {
|
|
16
|
+
const types = new Set(config.loadType);
|
|
17
|
+
return types.has(types_1.LoadType.PROJECT_ITEM) && types.has(types_1.LoadType.ASSORTMENT);
|
|
18
|
+
}
|
|
19
|
+
exports.isProjectItemAndAssortmentLoad = isProjectItemAndAssortmentLoad;
|
|
20
|
+
async function resolveIdentifiers(rows, config, client) {
|
|
21
|
+
const issues = [];
|
|
22
|
+
if (!rows?.length)
|
|
23
|
+
return { issues };
|
|
24
|
+
const hasProjectSource = rows.some((row) => row.workspaceIdentifier !== undefined || row.projectId !== undefined);
|
|
25
|
+
const useComboMode = isProjectItemAndAssortmentLoad(config) && !hasProjectSource;
|
|
26
|
+
const projectItemColumnIssues = useComboMode ? [] : validateProjectItemColumns(rows, config);
|
|
27
|
+
issues.push(...projectItemColumnIssues);
|
|
28
|
+
if (!useComboMode && client && projectItemColumnIssues.length === 0) {
|
|
29
|
+
issues.push(...(await validateAndResolveProjectIdentifiers(rows, config, client)));
|
|
30
|
+
}
|
|
31
|
+
const assortmentColumnIssues = validateAssortmentColumns(rows, config);
|
|
32
|
+
issues.push(...assortmentColumnIssues);
|
|
33
|
+
if (client && assortmentColumnIssues.length === 0) {
|
|
34
|
+
issues.push(...(await validateAndResolveAssortmentIdentifiers(rows, config, client)));
|
|
35
|
+
}
|
|
36
|
+
if (useComboMode && client && assortmentColumnIssues.length === 0) {
|
|
37
|
+
issues.push(...(await stampProjectIdsFromAssortmentWorkspaces(rows, client)));
|
|
38
|
+
}
|
|
39
|
+
return { issues };
|
|
40
|
+
}
|
|
41
|
+
exports.resolveIdentifiers = resolveIdentifiers;
|
|
42
|
+
async function validateAndResolveProjectIdentifiers(rows, config, client) {
|
|
43
|
+
const isProjectItemLoad = config.loadType.some((lt) => lt === types_1.LoadType.PROJECT_ITEM);
|
|
44
|
+
if (!isProjectItemLoad)
|
|
45
|
+
return [];
|
|
46
|
+
const hasWorkspaceIdentifier = rows.some((row) => row.workspaceIdentifier);
|
|
47
|
+
if (hasWorkspaceIdentifier) {
|
|
48
|
+
return validateWorkspaceIdentifiers(rows, client);
|
|
49
|
+
}
|
|
50
|
+
return validateProjectIds(rows, client);
|
|
51
|
+
}
|
|
52
|
+
async function validateWorkspaceIdentifiers(rows, client) {
|
|
53
|
+
const issues = [];
|
|
54
|
+
const cache = {};
|
|
55
|
+
const uniqueIdentifiers = new Set();
|
|
56
|
+
for (const row of rows) {
|
|
57
|
+
if (row.workspaceIdentifier)
|
|
58
|
+
uniqueIdentifiers.add(row.workspaceIdentifier);
|
|
59
|
+
}
|
|
60
|
+
for (const wsIdentifier of uniqueIdentifiers) {
|
|
61
|
+
try {
|
|
62
|
+
const project = await resolveProjectIdFromWorkspace(client, wsIdentifier);
|
|
63
|
+
cache[wsIdentifier] = project;
|
|
64
|
+
if (!project) {
|
|
65
|
+
const affectedRows = [];
|
|
66
|
+
rows.forEach((row, index) => {
|
|
67
|
+
if (row.workspaceIdentifier === wsIdentifier) {
|
|
68
|
+
affectedRows.push({ row: index + 2, value: wsIdentifier });
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
issues.push({
|
|
72
|
+
severity: types_1.PreValidationSeverity.ERROR,
|
|
73
|
+
category: REFERENCE_NOT_FOUND_CATEGORY,
|
|
74
|
+
columnName: 'workspaceIdentifier',
|
|
75
|
+
propertySlug: 'workspaceIdentifier',
|
|
76
|
+
propertyType: 'string',
|
|
77
|
+
message: `Could not resolve workspace identifier "${wsIdentifier}" to a project. Verify the workspace exists and is a PROJECT type workspace.`,
|
|
78
|
+
affectedRows,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
issues.push({
|
|
84
|
+
severity: types_1.PreValidationSeverity.ERROR,
|
|
85
|
+
category: REFERENCE_NOT_FOUND_CATEGORY,
|
|
86
|
+
columnName: 'workspaceIdentifier',
|
|
87
|
+
propertySlug: 'workspaceIdentifier',
|
|
88
|
+
propertyType: 'string',
|
|
89
|
+
message: `Failed to resolve workspace identifier "${wsIdentifier}": ${e.message}`,
|
|
90
|
+
affectedRows: [],
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (issues.length === 0) {
|
|
95
|
+
for (const row of rows) {
|
|
96
|
+
if (row.workspaceIdentifier) {
|
|
97
|
+
const project = cache[row.workspaceIdentifier];
|
|
98
|
+
row.projectId = project.id;
|
|
99
|
+
row.projectName = project.name;
|
|
100
|
+
delete row.workspaceIdentifier;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return issues;
|
|
105
|
+
}
|
|
106
|
+
async function validateProjectIds(rows, client) {
|
|
107
|
+
const issues = [];
|
|
108
|
+
const nameCache = {};
|
|
109
|
+
const uniqueProjectIds = new Set();
|
|
110
|
+
for (const row of rows) {
|
|
111
|
+
if (row.projectId)
|
|
112
|
+
uniqueProjectIds.add(row.projectId);
|
|
113
|
+
}
|
|
114
|
+
if (uniqueProjectIds.size === 0)
|
|
115
|
+
return [];
|
|
116
|
+
for (const projectId of uniqueProjectIds) {
|
|
117
|
+
try {
|
|
118
|
+
const project = await (0, entities_helper_1.retryOnTimeout)(client, entities_helper_1.EntitiesLambdaCallType.GET, {
|
|
119
|
+
entityName: 'project',
|
|
120
|
+
id: projectId,
|
|
121
|
+
skipHydrateRelationships: true,
|
|
122
|
+
});
|
|
123
|
+
if (!project) {
|
|
124
|
+
const affectedRows = [];
|
|
125
|
+
rows.forEach((row, index) => {
|
|
126
|
+
if (row.projectId === projectId) {
|
|
127
|
+
affectedRows.push({ row: index + 2, value: projectId });
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
issues.push({
|
|
131
|
+
severity: types_1.PreValidationSeverity.ERROR,
|
|
132
|
+
category: REFERENCE_NOT_FOUND_CATEGORY,
|
|
133
|
+
columnName: 'projectId',
|
|
134
|
+
propertySlug: 'projectId',
|
|
135
|
+
propertyType: 'string',
|
|
136
|
+
message: `Project "${projectId}" not found. Verify the project ID is correct and the project exists.`,
|
|
137
|
+
affectedRows,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
nameCache[projectId] = project.name || project.identifier || projectId;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch (e) {
|
|
145
|
+
const affectedRows = [];
|
|
146
|
+
rows.forEach((row, index) => {
|
|
147
|
+
if (row.projectId === projectId) {
|
|
148
|
+
affectedRows.push({ row: index + 2, value: projectId });
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
issues.push({
|
|
152
|
+
severity: types_1.PreValidationSeverity.ERROR,
|
|
153
|
+
category: REFERENCE_NOT_FOUND_CATEGORY,
|
|
154
|
+
columnName: 'projectId',
|
|
155
|
+
propertySlug: 'projectId',
|
|
156
|
+
propertyType: 'string',
|
|
157
|
+
message: `Failed to validate project "${projectId}": ${e.message}`,
|
|
158
|
+
affectedRows,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (issues.length === 0) {
|
|
163
|
+
for (const row of rows) {
|
|
164
|
+
if (row.projectId && nameCache[row.projectId]) {
|
|
165
|
+
row.projectName = nameCache[row.projectId];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return issues;
|
|
170
|
+
}
|
|
171
|
+
async function resolveProjectIdFromWorkspace(client, workspaceIdentifier) {
|
|
172
|
+
const workspaces = await (0, entities_helper_1.retryOnTimeout)(client, entities_helper_1.EntitiesLambdaCallType.GET, {
|
|
173
|
+
entityName: 'workspace',
|
|
174
|
+
criteria: {
|
|
175
|
+
identifier: workspaceIdentifier,
|
|
176
|
+
workspaceType: 'PROJECT',
|
|
177
|
+
},
|
|
178
|
+
skipHydrateRelationships: true,
|
|
179
|
+
});
|
|
180
|
+
if (!workspaces?.length)
|
|
181
|
+
return null;
|
|
182
|
+
const workspaceId = workspaces[0]?.id;
|
|
183
|
+
if (!workspaceId)
|
|
184
|
+
return null;
|
|
185
|
+
const workspace = await (0, entities_helper_1.retryOnTimeout)(client, entities_helper_1.EntitiesLambdaCallType.GET, {
|
|
186
|
+
entityName: 'workspace',
|
|
187
|
+
id: workspaceId,
|
|
188
|
+
relations: ['project'],
|
|
189
|
+
skipHydrateRelationships: true,
|
|
190
|
+
});
|
|
191
|
+
const project = workspace?.project;
|
|
192
|
+
if (!project?.id)
|
|
193
|
+
return null;
|
|
194
|
+
return { id: project.id, name: project.name || project.id };
|
|
195
|
+
}
|
|
196
|
+
function validateProjectItemColumns(rows, config) {
|
|
197
|
+
const isProjectItemLoad = config.loadType.some((lt) => lt === types_1.LoadType.PROJECT_ITEM);
|
|
198
|
+
if (!isProjectItemLoad)
|
|
199
|
+
return [];
|
|
200
|
+
const issues = [];
|
|
201
|
+
const hasWorkspaceIdentifier = rows.some((row) => row.workspaceIdentifier !== undefined);
|
|
202
|
+
const hasProjectId = rows.some((row) => row.projectId !== undefined);
|
|
203
|
+
if (hasWorkspaceIdentifier && hasProjectId) {
|
|
204
|
+
issues.push({
|
|
205
|
+
severity: types_1.PreValidationSeverity.ERROR,
|
|
206
|
+
category: CONFIG_CATEGORY,
|
|
207
|
+
columnName: 'workspaceIdentifier / projectId',
|
|
208
|
+
propertySlug: '',
|
|
209
|
+
propertyType: '',
|
|
210
|
+
message: 'Rows contain both "workspaceIdentifier" and "projectId" columns. Use one or the other to identify the target project.',
|
|
211
|
+
affectedRows: [],
|
|
212
|
+
});
|
|
213
|
+
return issues;
|
|
214
|
+
}
|
|
215
|
+
if (!hasWorkspaceIdentifier && !hasProjectId) {
|
|
216
|
+
issues.push({
|
|
217
|
+
severity: types_1.PreValidationSeverity.ERROR,
|
|
218
|
+
category: CONFIG_CATEGORY,
|
|
219
|
+
columnName: 'workspaceIdentifier / projectId',
|
|
220
|
+
propertySlug: '',
|
|
221
|
+
propertyType: '',
|
|
222
|
+
message: 'PROJECT_ITEM loads require a "workspaceIdentifier" or "projectId" column (or a config-level workspaceIdentifier) to identify the target project.',
|
|
223
|
+
affectedRows: [],
|
|
224
|
+
});
|
|
225
|
+
return issues;
|
|
226
|
+
}
|
|
227
|
+
const columnName = hasWorkspaceIdentifier ? 'workspaceIdentifier' : 'projectId';
|
|
228
|
+
const emptyRows = [];
|
|
229
|
+
forEachRow(rows, (row, rowNumber) => {
|
|
230
|
+
if (isEmpty(row[columnName])) {
|
|
231
|
+
emptyRows.push({ row: rowNumber, value: '' });
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
if (emptyRows.length > 0) {
|
|
235
|
+
issues.push({
|
|
236
|
+
severity: types_1.PreValidationSeverity.ERROR,
|
|
237
|
+
category: MISSING_REQUIRED_FIELD_CATEGORY,
|
|
238
|
+
columnName,
|
|
239
|
+
propertySlug: columnName,
|
|
240
|
+
propertyType: 'string',
|
|
241
|
+
message: `"${columnName}" cannot be empty for project-item rows. ${emptyRows.length} row(s) have an empty value.`,
|
|
242
|
+
affectedRows: emptyRows,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
return issues;
|
|
246
|
+
}
|
|
247
|
+
function validateAssortmentColumns(rows, config) {
|
|
248
|
+
const isAssortmentLoad = config.loadType.some((lt) => lt === types_1.LoadType.ASSORTMENT);
|
|
249
|
+
if (!isAssortmentLoad)
|
|
250
|
+
return [];
|
|
251
|
+
const issues = [];
|
|
252
|
+
const hasAssortmentId = rows.some((row) => row.assortmentId !== undefined);
|
|
253
|
+
const hasAssortmentIdentifier = rows.some((row) => row.assortmentIdentifier !== undefined);
|
|
254
|
+
const assortmentIdFromConditions = (config.conditionalColumns || []).some((col) => (col.toProperty === 'assortmentId' || col.toProperty === 'assortmentIdentifier') && col.conditions?.length);
|
|
255
|
+
if (!hasAssortmentId && !hasAssortmentIdentifier) {
|
|
256
|
+
issues.push({
|
|
257
|
+
severity: types_1.PreValidationSeverity.ERROR,
|
|
258
|
+
category: CONFIG_CATEGORY,
|
|
259
|
+
columnName: 'assortmentId / assortmentIdentifier',
|
|
260
|
+
propertySlug: '',
|
|
261
|
+
propertyType: '',
|
|
262
|
+
message: 'ASSORTMENT loads require an "assortmentId" or "assortmentIdentifier" column (or a config-level assortmentId/assortmentIdentifier) to identify the target assortment.',
|
|
263
|
+
affectedRows: [],
|
|
264
|
+
});
|
|
265
|
+
return issues;
|
|
266
|
+
}
|
|
267
|
+
const bothRows = [];
|
|
268
|
+
forEachRow(rows, (row, rowNumber) => {
|
|
269
|
+
const hasId = !isEmpty(row.assortmentId);
|
|
270
|
+
const hasIdent = !isEmpty(row.assortmentIdentifier);
|
|
271
|
+
if (hasId && hasIdent) {
|
|
272
|
+
bothRows.push({ row: rowNumber, value: `${row.assortmentId} / ${row.assortmentIdentifier}` });
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
if (bothRows.length > 0) {
|
|
276
|
+
issues.push({
|
|
277
|
+
severity: types_1.PreValidationSeverity.ERROR,
|
|
278
|
+
category: CONFIG_CATEGORY,
|
|
279
|
+
columnName: 'assortmentId / assortmentIdentifier',
|
|
280
|
+
propertySlug: '',
|
|
281
|
+
propertyType: '',
|
|
282
|
+
message: `${bothRows.length} row(s) have both "assortmentId" and "assortmentIdentifier". Each row must use one or the other.`,
|
|
283
|
+
affectedRows: bothRows,
|
|
284
|
+
});
|
|
285
|
+
return issues;
|
|
286
|
+
}
|
|
287
|
+
const emptyIdRows = [];
|
|
288
|
+
const emptyIdentRows = [];
|
|
289
|
+
forEachRow(rows, (row, rowNumber) => {
|
|
290
|
+
if (hasAssortmentId && row.assortmentId !== undefined) {
|
|
291
|
+
if (isEmpty(row.assortmentId)) {
|
|
292
|
+
emptyIdRows.push({ row: rowNumber, value: '' });
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
else if (hasAssortmentId && row.assortmentId === undefined && !assortmentIdFromConditions) {
|
|
296
|
+
emptyIdRows.push({ row: rowNumber, value: '' });
|
|
297
|
+
}
|
|
298
|
+
if (hasAssortmentIdentifier && row.assortmentIdentifier !== undefined) {
|
|
299
|
+
if (isEmpty(row.assortmentIdentifier)) {
|
|
300
|
+
emptyIdentRows.push({ row: rowNumber, value: '' });
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
if (emptyIdRows.length > 0) {
|
|
305
|
+
issues.push({
|
|
306
|
+
severity: types_1.PreValidationSeverity.ERROR,
|
|
307
|
+
category: MISSING_REQUIRED_FIELD_CATEGORY,
|
|
308
|
+
columnName: 'assortmentId',
|
|
309
|
+
propertySlug: 'assortmentId',
|
|
310
|
+
propertyType: 'string',
|
|
311
|
+
message: `"assortmentId" cannot be empty for assortment rows. ${emptyIdRows.length} row(s) have an empty value.`,
|
|
312
|
+
affectedRows: emptyIdRows,
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
if (emptyIdentRows.length > 0) {
|
|
316
|
+
issues.push({
|
|
317
|
+
severity: types_1.PreValidationSeverity.ERROR,
|
|
318
|
+
category: MISSING_REQUIRED_FIELD_CATEGORY,
|
|
319
|
+
columnName: 'assortmentIdentifier',
|
|
320
|
+
propertySlug: 'assortmentIdentifier',
|
|
321
|
+
propertyType: 'string',
|
|
322
|
+
message: `"assortmentIdentifier" cannot be empty for assortment rows. ${emptyIdentRows.length} row(s) have an empty value.`,
|
|
323
|
+
affectedRows: emptyIdentRows,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
return issues;
|
|
327
|
+
}
|
|
328
|
+
async function validateAndResolveAssortmentIdentifiers(rows, config, client) {
|
|
329
|
+
const isAssortmentLoad = config.loadType.some((lt) => lt === types_1.LoadType.ASSORTMENT);
|
|
330
|
+
if (!isAssortmentLoad)
|
|
331
|
+
return [];
|
|
332
|
+
const hasAssortmentIdentifier = rows.some((row) => row.assortmentIdentifier);
|
|
333
|
+
if (hasAssortmentIdentifier) {
|
|
334
|
+
return resolveAssortmentIdentifiers(rows, client);
|
|
335
|
+
}
|
|
336
|
+
return validateAssortmentIds(rows, client);
|
|
337
|
+
}
|
|
338
|
+
async function resolveAssortmentIdentifiers(rows, client) {
|
|
339
|
+
const issues = [];
|
|
340
|
+
const cache = {};
|
|
341
|
+
const uniqueIdentifiers = new Set();
|
|
342
|
+
for (const row of rows) {
|
|
343
|
+
if (row.assortmentIdentifier)
|
|
344
|
+
uniqueIdentifiers.add(row.assortmentIdentifier);
|
|
345
|
+
}
|
|
346
|
+
for (const identifier of uniqueIdentifiers) {
|
|
347
|
+
try {
|
|
348
|
+
const assortments = await (0, entities_helper_1.retryOnTimeout)(client, entities_helper_1.EntitiesLambdaCallType.GET, {
|
|
349
|
+
entityName: 'assortment',
|
|
350
|
+
criteria: { identifier },
|
|
351
|
+
skipHydrateRelationships: true,
|
|
352
|
+
});
|
|
353
|
+
if (!assortments?.length) {
|
|
354
|
+
cache[identifier] = null;
|
|
355
|
+
const affectedRows = [];
|
|
356
|
+
rows.forEach((row, index) => {
|
|
357
|
+
if (row.assortmentIdentifier === identifier) {
|
|
358
|
+
affectedRows.push({ row: index + 2, value: identifier });
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
issues.push({
|
|
362
|
+
severity: types_1.PreValidationSeverity.ERROR,
|
|
363
|
+
category: REFERENCE_NOT_FOUND_CATEGORY,
|
|
364
|
+
columnName: 'assortmentIdentifier',
|
|
365
|
+
propertySlug: 'assortmentIdentifier',
|
|
366
|
+
propertyType: 'string',
|
|
367
|
+
message: `Assortment with identifier "${identifier}" not found. Verify the identifier is correct and the assortment exists.`,
|
|
368
|
+
affectedRows,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
else {
|
|
372
|
+
const assortment = assortments[0];
|
|
373
|
+
cache[identifier] = {
|
|
374
|
+
id: assortment.id,
|
|
375
|
+
name: assortment.name || assortment.id,
|
|
376
|
+
workspaceId: assortment.workspaceId,
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
catch (e) {
|
|
381
|
+
issues.push({
|
|
382
|
+
severity: types_1.PreValidationSeverity.ERROR,
|
|
383
|
+
category: REFERENCE_NOT_FOUND_CATEGORY,
|
|
384
|
+
columnName: 'assortmentIdentifier',
|
|
385
|
+
propertySlug: 'assortmentIdentifier',
|
|
386
|
+
propertyType: 'string',
|
|
387
|
+
message: `Failed to resolve assortment identifier "${identifier}": ${e.message}`,
|
|
388
|
+
affectedRows: [],
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
if (issues.length === 0) {
|
|
393
|
+
for (const row of rows) {
|
|
394
|
+
if (row.assortmentIdentifier) {
|
|
395
|
+
const assortment = cache[row.assortmentIdentifier];
|
|
396
|
+
row.assortmentId = assortment.id;
|
|
397
|
+
row.assortmentName = assortment.name;
|
|
398
|
+
if (assortment.workspaceId) {
|
|
399
|
+
row.assortmentWorkspaceId = assortment.workspaceId;
|
|
400
|
+
}
|
|
401
|
+
delete row.assortmentIdentifier;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return issues;
|
|
406
|
+
}
|
|
407
|
+
async function validateAssortmentIds(rows, client) {
|
|
408
|
+
const issues = [];
|
|
409
|
+
const nameCache = {};
|
|
410
|
+
const workspaceCache = {};
|
|
411
|
+
const uniqueIds = new Set();
|
|
412
|
+
for (const row of rows) {
|
|
413
|
+
if (row.assortmentId)
|
|
414
|
+
uniqueIds.add(row.assortmentId);
|
|
415
|
+
}
|
|
416
|
+
if (uniqueIds.size === 0)
|
|
417
|
+
return [];
|
|
418
|
+
for (const assortmentId of uniqueIds) {
|
|
419
|
+
try {
|
|
420
|
+
const assortment = await (0, entities_helper_1.retryOnTimeout)(client, entities_helper_1.EntitiesLambdaCallType.GET, {
|
|
421
|
+
entityName: 'assortment',
|
|
422
|
+
id: assortmentId,
|
|
423
|
+
skipHydrateRelationships: true,
|
|
424
|
+
});
|
|
425
|
+
if (assortment) {
|
|
426
|
+
nameCache[assortmentId] = assortment.name || assortmentId;
|
|
427
|
+
workspaceCache[assortmentId] = assortment.workspaceId;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
catch (e) {
|
|
431
|
+
const affectedRows = [];
|
|
432
|
+
rows.forEach((row, index) => {
|
|
433
|
+
if (row.assortmentId === assortmentId) {
|
|
434
|
+
affectedRows.push({ row: index + 2, value: assortmentId });
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
const parsedDetails = typeof e?.details === 'string' ? JSON.parse(e.details).statusCode : e?.details?.statusCode;
|
|
438
|
+
const message = parsedDetails === 404
|
|
439
|
+
? `Assortment "${assortmentId}" not found. Verify the assortment ID is correct and the assortment exists.`
|
|
440
|
+
: `Failed to validate assortment "${assortmentId}": ${e.message}`;
|
|
441
|
+
issues.push({
|
|
442
|
+
severity: types_1.PreValidationSeverity.ERROR,
|
|
443
|
+
category: REFERENCE_NOT_FOUND_CATEGORY,
|
|
444
|
+
columnName: 'assortmentId',
|
|
445
|
+
propertySlug: 'assortmentId',
|
|
446
|
+
propertyType: 'string',
|
|
447
|
+
message,
|
|
448
|
+
affectedRows,
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
if (issues.length === 0) {
|
|
453
|
+
for (const row of rows) {
|
|
454
|
+
if (row.assortmentId) {
|
|
455
|
+
if (nameCache[row.assortmentId]) {
|
|
456
|
+
row.assortmentName = nameCache[row.assortmentId];
|
|
457
|
+
}
|
|
458
|
+
if (workspaceCache[row.assortmentId]) {
|
|
459
|
+
row.assortmentWorkspaceId = workspaceCache[row.assortmentId];
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return issues;
|
|
465
|
+
}
|
|
466
|
+
async function stampProjectIdsFromAssortmentWorkspaces(rows, client) {
|
|
467
|
+
const issues = [];
|
|
468
|
+
const assortmentToWorkspace = new Map();
|
|
469
|
+
const assortmentsMissingWorkspace = new Set();
|
|
470
|
+
for (const row of rows) {
|
|
471
|
+
const assortmentId = row.assortmentId;
|
|
472
|
+
if (!assortmentId)
|
|
473
|
+
continue;
|
|
474
|
+
const workspaceId = row.assortmentWorkspaceId;
|
|
475
|
+
if (!workspaceId) {
|
|
476
|
+
assortmentsMissingWorkspace.add(assortmentId);
|
|
477
|
+
}
|
|
478
|
+
else {
|
|
479
|
+
assortmentToWorkspace.set(assortmentId, workspaceId);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
for (const assortmentId of assortmentsMissingWorkspace) {
|
|
483
|
+
const affectedRows = [];
|
|
484
|
+
rows.forEach((row, index) => {
|
|
485
|
+
if (row.assortmentId === assortmentId && !row.assortmentWorkspaceId) {
|
|
486
|
+
affectedRows.push({ row: index + 2, value: assortmentId });
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
issues.push({
|
|
490
|
+
severity: types_1.PreValidationSeverity.ERROR,
|
|
491
|
+
category: REFERENCE_NOT_FOUND_CATEGORY,
|
|
492
|
+
columnName: 'assortmentId',
|
|
493
|
+
propertySlug: 'assortmentId',
|
|
494
|
+
propertyType: 'string',
|
|
495
|
+
message: `Assortment "${assortmentId}" has no workspaceId. Cannot derive a project for project-item rows from this assortment.`,
|
|
496
|
+
affectedRows,
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
if (issues.length > 0) {
|
|
500
|
+
return issues;
|
|
501
|
+
}
|
|
502
|
+
const projectByWorkspaceId = {};
|
|
503
|
+
const uniqueWorkspaceIds = new Set(assortmentToWorkspace.values());
|
|
504
|
+
for (const workspaceId of uniqueWorkspaceIds) {
|
|
505
|
+
try {
|
|
506
|
+
const workspace = await (0, entities_helper_1.retryOnTimeout)(client, entities_helper_1.EntitiesLambdaCallType.GET, {
|
|
507
|
+
entityName: 'workspace',
|
|
508
|
+
id: workspaceId,
|
|
509
|
+
relations: ['project'],
|
|
510
|
+
skipHydrateRelationships: true,
|
|
511
|
+
});
|
|
512
|
+
const project = workspace?.project;
|
|
513
|
+
if (!project?.id) {
|
|
514
|
+
const affectedAssortmentIds = [...assortmentToWorkspace.entries()]
|
|
515
|
+
.filter(([, wsId]) => wsId === workspaceId)
|
|
516
|
+
.map(([aId]) => aId);
|
|
517
|
+
const affectedRows = [];
|
|
518
|
+
rows.forEach((row, index) => {
|
|
519
|
+
if (row.assortmentId && affectedAssortmentIds.includes(row.assortmentId)) {
|
|
520
|
+
affectedRows.push({ row: index + 2, value: row.assortmentId });
|
|
521
|
+
}
|
|
522
|
+
});
|
|
523
|
+
issues.push({
|
|
524
|
+
severity: types_1.PreValidationSeverity.ERROR,
|
|
525
|
+
category: REFERENCE_NOT_FOUND_CATEGORY,
|
|
526
|
+
columnName: 'assortmentId',
|
|
527
|
+
propertySlug: 'assortmentId',
|
|
528
|
+
propertyType: 'string',
|
|
529
|
+
message: `Assortment workspace "${workspaceId}" has no associated project. Cannot derive a project for project-item rows. Verify the assortment lives in a PROJECT-type workspace.`,
|
|
530
|
+
affectedRows,
|
|
531
|
+
});
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
projectByWorkspaceId[workspaceId] = { id: project.id, name: project.name || project.id };
|
|
535
|
+
}
|
|
536
|
+
catch (e) {
|
|
537
|
+
issues.push({
|
|
538
|
+
severity: types_1.PreValidationSeverity.ERROR,
|
|
539
|
+
category: REFERENCE_NOT_FOUND_CATEGORY,
|
|
540
|
+
columnName: 'assortmentId',
|
|
541
|
+
propertySlug: 'assortmentId',
|
|
542
|
+
propertyType: 'string',
|
|
543
|
+
message: `Failed to resolve workspace "${workspaceId}" for assortment-derived project: ${e.message}`,
|
|
544
|
+
affectedRows: [],
|
|
545
|
+
});
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
if (issues.length > 0) {
|
|
549
|
+
return issues;
|
|
550
|
+
}
|
|
551
|
+
for (const row of rows) {
|
|
552
|
+
const workspaceId = row.assortmentWorkspaceId;
|
|
553
|
+
if (!workspaceId)
|
|
554
|
+
continue;
|
|
555
|
+
const project = projectByWorkspaceId[workspaceId];
|
|
556
|
+
if (!project)
|
|
557
|
+
continue;
|
|
558
|
+
row.projectId = project.id;
|
|
559
|
+
row.projectName = project.name;
|
|
560
|
+
}
|
|
561
|
+
return issues;
|
|
562
|
+
}
|