cassproject 5.0.7 → 5.0.8
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
CHANGED
|
@@ -50,6 +50,10 @@ Development unit tests presume you have a CaSS Repository running on `localhost:
|
|
|
50
50
|
|
|
51
51
|
# Changelog
|
|
52
52
|
|
|
53
|
+
## 5.0.8
|
|
54
|
+
* Security updates
|
|
55
|
+
* CTDL-ASN import fixes
|
|
56
|
+
|
|
53
57
|
## 5.0.7
|
|
54
58
|
* Security updates
|
|
55
59
|
* Assertions with the same registration will no longer propagate to the same inferred assertions.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cassproject",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.8",
|
|
4
4
|
"description": "Competency and Skills Service",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -193,18 +193,18 @@
|
|
|
193
193
|
"chai": "4.5.0",
|
|
194
194
|
"concurrently": "^9.2.1",
|
|
195
195
|
"convert-hrtime": "^5.0.0",
|
|
196
|
-
"cypress": "^15.
|
|
196
|
+
"cypress": "^15.8.1",
|
|
197
197
|
"cypress-fail-fast": "^7.1.1",
|
|
198
|
-
"eslint": "^9.39.
|
|
198
|
+
"eslint": "^9.39.2",
|
|
199
199
|
"fake-indexeddb": "^6.2.5",
|
|
200
200
|
"mocha": "^11.7.5",
|
|
201
201
|
"node-polyfill-webpack-plugin": "^4.1.0",
|
|
202
202
|
"nodemon": "^3.1.11",
|
|
203
203
|
"nyc": "^17.1.0",
|
|
204
|
-
"sinon": "^21.0.
|
|
204
|
+
"sinon": "^21.0.1",
|
|
205
205
|
"url-polyfill": "^1.1.14",
|
|
206
206
|
"wait-on": "^9.0.3",
|
|
207
|
-
"webpack": "^5.
|
|
207
|
+
"webpack": "^5.104.1",
|
|
208
208
|
"webpack-cli": "^6.0.1"
|
|
209
209
|
},
|
|
210
210
|
"packageManager": "npm@11.3.0+sha512.96eb611483f49c55f7fa74df61b588de9e213f80a256728e6798ddc67176c7b07e4a1cfc7de8922422cbce02543714367037536955221fa451b0c4fefaf20c66"
|
|
@@ -131,6 +131,7 @@ module.exports = class CTDLASNCSVConceptImport {
|
|
|
131
131
|
encoding: "UTF-8",
|
|
132
132
|
complete: async function(results) {
|
|
133
133
|
let tabularData = results["data"];
|
|
134
|
+
let errors = []; // Collect all errors to display at once
|
|
134
135
|
try {
|
|
135
136
|
for (let data of tabularData) {
|
|
136
137
|
for (let [key, value] of Object.entries(data)) {
|
|
@@ -148,30 +149,37 @@ module.exports = class CTDLASNCSVConceptImport {
|
|
|
148
149
|
validationRules.hierarchyRules
|
|
149
150
|
);
|
|
150
151
|
if (hierarchyError) {
|
|
151
|
-
|
|
152
|
-
return;
|
|
152
|
+
errors.push(hierarchyError);
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
const terms = JSON.parse(JSON.stringify((await EcRemote.getExpectingObject("https://schema.cassproject.org/0.4/jsonld1.1/ceasn2cassConceptsTerms"))));
|
|
156
156
|
let schemeArray = [];
|
|
157
157
|
let concepts = [];
|
|
158
|
+
let rowsWithErrors = new Set(); // Track rows that have errors
|
|
158
159
|
for (let each = 0; each < tabularData.length; each++) {
|
|
159
160
|
let pretranslatedE = tabularData[each];
|
|
160
161
|
// Skip extra lines if found in file
|
|
161
162
|
if (!pretranslatedE) {
|
|
162
163
|
continue;
|
|
163
164
|
}
|
|
165
|
+
|
|
166
|
+
// Validate basic required fields and collect errors
|
|
167
|
+
let rowHasError = false;
|
|
164
168
|
if (!pretranslatedE["@id"]) {
|
|
165
|
-
|
|
166
|
-
|
|
169
|
+
errors.push(`Row ${each + 2}: is missing an @id`);
|
|
170
|
+
rowHasError = true;
|
|
167
171
|
}
|
|
168
172
|
if (!pretranslatedE["@type"]) {
|
|
169
|
-
|
|
170
|
-
|
|
173
|
+
errors.push(`Row ${each + 2}: is missing a @type`);
|
|
174
|
+
rowHasError = true;
|
|
171
175
|
}
|
|
172
|
-
if (!pretranslatedE["@id"].startsWith('http') && !pretranslatedE["@id"].startsWith('ce-')) {
|
|
173
|
-
|
|
174
|
-
|
|
176
|
+
if (pretranslatedE["@id"] && !pretranslatedE["@id"].startsWith('http') && !pretranslatedE["@id"].startsWith('ce-')) {
|
|
177
|
+
errors.push(`Row ${each + 2}: @id must be a valid URI or start with 'ce-'`);
|
|
178
|
+
rowHasError = true;
|
|
179
|
+
}
|
|
180
|
+
if (rowHasError) {
|
|
181
|
+
rowsWithErrors.add(each);
|
|
182
|
+
continue;
|
|
175
183
|
}
|
|
176
184
|
if (
|
|
177
185
|
pretranslatedE["@type"].toLowerCase().startsWith('sample') || pretranslatedE["@type"].toLowerCase().startsWith('instruction')
|
|
@@ -181,15 +189,20 @@ module.exports = class CTDLASNCSVConceptImport {
|
|
|
181
189
|
if (pretranslatedE["@type"] == "skos:ConceptScheme") {
|
|
182
190
|
// Validate required properties (only if validation rules provided)
|
|
183
191
|
if (validationRules && validationRules.requiredProps) {
|
|
184
|
-
const
|
|
192
|
+
const validationErrors = CTDLASNCSVConceptImport.validateRequiredProperties(
|
|
185
193
|
pretranslatedE,
|
|
186
194
|
"skos:ConceptScheme",
|
|
187
195
|
each + 2,
|
|
188
196
|
validationRules.requiredProps
|
|
189
197
|
);
|
|
190
|
-
if (
|
|
191
|
-
|
|
192
|
-
|
|
198
|
+
if (validationErrors) {
|
|
199
|
+
if (Array.isArray(validationErrors)) {
|
|
200
|
+
errors.push(...validationErrors);
|
|
201
|
+
} else {
|
|
202
|
+
errors.push(validationErrors);
|
|
203
|
+
}
|
|
204
|
+
rowsWithErrors.add(each);
|
|
205
|
+
continue;
|
|
193
206
|
}
|
|
194
207
|
}
|
|
195
208
|
let translator = new EcLinkedData(null, null);
|
|
@@ -263,15 +276,20 @@ module.exports = class CTDLASNCSVConceptImport {
|
|
|
263
276
|
} else if (pretranslatedE["@type"] == "skos:Concept") {
|
|
264
277
|
// Validate required properties (only if validation rules provided)
|
|
265
278
|
if (validationRules && validationRules.requiredProps) {
|
|
266
|
-
const
|
|
279
|
+
const validationErrors = CTDLASNCSVConceptImport.validateRequiredProperties(
|
|
267
280
|
pretranslatedE,
|
|
268
281
|
"skos:Concept",
|
|
269
282
|
each + 2,
|
|
270
283
|
validationRules.requiredProps
|
|
271
284
|
);
|
|
272
|
-
if (
|
|
273
|
-
|
|
274
|
-
|
|
285
|
+
if (validationErrors) {
|
|
286
|
+
if (Array.isArray(validationErrors)) {
|
|
287
|
+
errors.push(...validationErrors);
|
|
288
|
+
} else {
|
|
289
|
+
errors.push(validationErrors);
|
|
290
|
+
}
|
|
291
|
+
rowsWithErrors.add(each);
|
|
292
|
+
continue;
|
|
275
293
|
}
|
|
276
294
|
}
|
|
277
295
|
let translator = new EcLinkedData(null, null);
|
|
@@ -302,8 +320,9 @@ module.exports = class CTDLASNCSVConceptImport {
|
|
|
302
320
|
let f = new EcConcept();
|
|
303
321
|
f.copyFrom(e);
|
|
304
322
|
if (e["id"] == null) {
|
|
305
|
-
|
|
306
|
-
|
|
323
|
+
errors.push(`Row ${each + 2}: Concept is missing an id`);
|
|
324
|
+
rowsWithErrors.add(each);
|
|
325
|
+
continue;
|
|
307
326
|
}
|
|
308
327
|
if (
|
|
309
328
|
EcConcept.template != null &&
|
|
@@ -446,15 +465,22 @@ module.exports = class CTDLASNCSVConceptImport {
|
|
|
446
465
|
pretranslatedE["@type"] == null ||
|
|
447
466
|
pretranslatedE["@type"] == ""
|
|
448
467
|
) {
|
|
449
|
-
|
|
450
|
-
|
|
468
|
+
errors.push(`Row ${each + 2}: Missing or empty @type`);
|
|
469
|
+
rowsWithErrors.add(each);
|
|
470
|
+
continue;
|
|
451
471
|
} else {
|
|
452
|
-
|
|
472
|
+
errors.push(
|
|
453
473
|
`Row ${each + 2}: Found unknown type: ` + pretranslatedE["@type"]
|
|
454
474
|
);
|
|
455
|
-
|
|
475
|
+
rowsWithErrors.add(each);
|
|
476
|
+
continue;
|
|
456
477
|
}
|
|
457
478
|
}
|
|
479
|
+
// If there are any errors, report them all at once
|
|
480
|
+
if (errors.length > 0) {
|
|
481
|
+
failure(errors);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
458
484
|
success(schemeArray, concepts);
|
|
459
485
|
},
|
|
460
486
|
error: failure
|
|
@@ -475,6 +501,7 @@ module.exports = class CTDLASNCSVConceptImport {
|
|
|
475
501
|
encoding: "UTF-8",
|
|
476
502
|
complete: async function(results) {
|
|
477
503
|
let tabularData = results["data"];
|
|
504
|
+
let errors = []; // Collect all errors to display at once
|
|
478
505
|
try {
|
|
479
506
|
for (let data of tabularData) {
|
|
480
507
|
for (let [key, value] of Object.entries(data)) {
|
|
@@ -492,26 +519,33 @@ module.exports = class CTDLASNCSVConceptImport {
|
|
|
492
519
|
validationRules.hierarchyRules
|
|
493
520
|
);
|
|
494
521
|
if (hierarchyError) {
|
|
495
|
-
|
|
496
|
-
return;
|
|
522
|
+
errors.push(hierarchyError);
|
|
497
523
|
}
|
|
498
524
|
}
|
|
499
525
|
const terms = JSON.parse(JSON.stringify((await EcRemote.getExpectingObject("https://schema.cassproject.org/0.4/jsonld1.1/ceasn2cassConceptsTerms"))));
|
|
500
526
|
let schemeArray = [];
|
|
501
527
|
let concepts = [];
|
|
528
|
+
let rowsWithErrors = new Set(); // Track rows that have errors
|
|
502
529
|
for (let each = 0; each < tabularData.length; each++) {
|
|
503
530
|
let pretranslatedE = tabularData[each];
|
|
504
531
|
// Skip extra lines if found in file
|
|
505
532
|
if (!pretranslatedE) {
|
|
506
533
|
continue;
|
|
507
534
|
}
|
|
535
|
+
|
|
536
|
+
// Validate basic required fields and collect errors
|
|
537
|
+
let rowHasError = false;
|
|
508
538
|
if (!pretranslatedE["@id"]) {
|
|
509
|
-
|
|
510
|
-
|
|
539
|
+
errors.push(`Row ${each + 2}: is missing an @id`);
|
|
540
|
+
rowHasError = true;
|
|
511
541
|
}
|
|
512
542
|
if (!pretranslatedE["@type"]) {
|
|
513
|
-
|
|
514
|
-
|
|
543
|
+
errors.push(`Row ${each + 2}: is missing a @type`);
|
|
544
|
+
rowHasError = true;
|
|
545
|
+
}
|
|
546
|
+
if (rowHasError) {
|
|
547
|
+
rowsWithErrors.add(each);
|
|
548
|
+
continue;
|
|
515
549
|
}
|
|
516
550
|
if (
|
|
517
551
|
pretranslatedE["@type"].toLowerCase().startsWith('sample') || pretranslatedE["@type"].toLowerCase().startsWith('instruction')
|
|
@@ -521,15 +555,20 @@ module.exports = class CTDLASNCSVConceptImport {
|
|
|
521
555
|
if (pretranslatedE["@type"] == "asn:ProgressionModel") {
|
|
522
556
|
// Validate required properties (only if validation rules provided)
|
|
523
557
|
if (validationRules && validationRules.requiredProps) {
|
|
524
|
-
const
|
|
558
|
+
const validationErrors = CTDLASNCSVConceptImport.validateRequiredProperties(
|
|
525
559
|
pretranslatedE,
|
|
526
560
|
"asn:ProgressionModel",
|
|
527
561
|
each + 2,
|
|
528
562
|
validationRules.requiredProps
|
|
529
563
|
);
|
|
530
|
-
if (
|
|
531
|
-
|
|
532
|
-
|
|
564
|
+
if (validationErrors) {
|
|
565
|
+
if (Array.isArray(validationErrors)) {
|
|
566
|
+
errors.push(...validationErrors);
|
|
567
|
+
} else {
|
|
568
|
+
errors.push(validationErrors);
|
|
569
|
+
}
|
|
570
|
+
rowsWithErrors.add(each);
|
|
571
|
+
continue;
|
|
533
572
|
}
|
|
534
573
|
}
|
|
535
574
|
let translator = new EcLinkedData(null, null);
|
|
@@ -599,15 +638,20 @@ module.exports = class CTDLASNCSVConceptImport {
|
|
|
599
638
|
} else if (pretranslatedE["@type"] == "asn:ProgressionLevel") {
|
|
600
639
|
// Validate required properties (only if validation rules provided)
|
|
601
640
|
if (validationRules && validationRules.requiredProps) {
|
|
602
|
-
const
|
|
641
|
+
const validationErrors = CTDLASNCSVConceptImport.validateRequiredProperties(
|
|
603
642
|
pretranslatedE,
|
|
604
643
|
"asn:ProgressionLevel",
|
|
605
644
|
each + 2,
|
|
606
645
|
validationRules.requiredProps
|
|
607
646
|
);
|
|
608
|
-
if (
|
|
609
|
-
|
|
610
|
-
|
|
647
|
+
if (validationErrors) {
|
|
648
|
+
if (Array.isArray(validationErrors)) {
|
|
649
|
+
errors.push(...validationErrors);
|
|
650
|
+
} else {
|
|
651
|
+
errors.push(validationErrors);
|
|
652
|
+
}
|
|
653
|
+
rowsWithErrors.add(each);
|
|
654
|
+
continue;
|
|
611
655
|
}
|
|
612
656
|
}
|
|
613
657
|
let translator = new EcLinkedData(null, null);
|
|
@@ -638,8 +682,9 @@ module.exports = class CTDLASNCSVConceptImport {
|
|
|
638
682
|
let f = new EcConcept();
|
|
639
683
|
f.copyFrom(e);
|
|
640
684
|
if (e["id"] == null) {
|
|
641
|
-
|
|
642
|
-
|
|
685
|
+
errors.push(`Row ${each + 2}: Concept is missing an id`);
|
|
686
|
+
rowsWithErrors.add(each);
|
|
687
|
+
continue;
|
|
643
688
|
}
|
|
644
689
|
if (
|
|
645
690
|
EcConcept.template != null &&
|
|
@@ -747,15 +792,22 @@ module.exports = class CTDLASNCSVConceptImport {
|
|
|
747
792
|
pretranslatedE["@type"] == null ||
|
|
748
793
|
pretranslatedE["@type"] == ""
|
|
749
794
|
) {
|
|
750
|
-
|
|
751
|
-
|
|
795
|
+
errors.push(`Row ${each + 2}: Missing or empty @type`);
|
|
796
|
+
rowsWithErrors.add(each);
|
|
797
|
+
continue;
|
|
752
798
|
} else {
|
|
753
|
-
|
|
799
|
+
errors.push(
|
|
754
800
|
`Row ${each + 2}: Found unknown type: ` + pretranslatedE["@type"]
|
|
755
801
|
);
|
|
756
|
-
|
|
802
|
+
rowsWithErrors.add(each);
|
|
803
|
+
continue;
|
|
757
804
|
}
|
|
758
805
|
}
|
|
806
|
+
// If there are any errors, report them all at once
|
|
807
|
+
if (errors.length > 0) {
|
|
808
|
+
failure(errors);
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
759
811
|
success(schemeArray, concepts);
|
|
760
812
|
},
|
|
761
813
|
error: failure
|
|
@@ -155,6 +155,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
155
155
|
encoding: "UTF-8",
|
|
156
156
|
complete: async function(results) {
|
|
157
157
|
let tabularData = results["data"];
|
|
158
|
+
let errors = []; // Collect all errors to display at once
|
|
158
159
|
try {
|
|
159
160
|
for (let data of tabularData) {
|
|
160
161
|
for (let [key, value] of Object.entries(data)) {
|
|
@@ -172,8 +173,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
172
173
|
validationRules.hierarchyRules
|
|
173
174
|
);
|
|
174
175
|
if (hierarchyError) {
|
|
175
|
-
|
|
176
|
-
return;
|
|
176
|
+
errors.push(hierarchyError);
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
|
|
@@ -185,6 +185,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
185
185
|
let competencyRows = {};
|
|
186
186
|
let relations = [];
|
|
187
187
|
let relationById = {};
|
|
188
|
+
let rowsWithErrors = new Set(); // Track rows that have errors to avoid duplicate processing
|
|
188
189
|
for (let i = 0; i < tabularData.length; i++) {
|
|
189
190
|
if (!tabularData[i]) {
|
|
190
191
|
continue;
|
|
@@ -194,7 +195,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
194
195
|
if (!pretranslatedE) {
|
|
195
196
|
continue;
|
|
196
197
|
}
|
|
197
|
-
if (pretranslatedE["@type"].toLowerCase() &&
|
|
198
|
+
if (pretranslatedE["@type"] && pretranslatedE["@type"].toLowerCase() &&
|
|
198
199
|
(pretranslatedE["@type"].toLowerCase().startsWith('sample') || pretranslatedE["@type"].toLowerCase().startsWith('instruction'))
|
|
199
200
|
) {
|
|
200
201
|
continue;
|
|
@@ -211,17 +212,24 @@ module.exports = class CTDLASNCSVImport {
|
|
|
211
212
|
continue;
|
|
212
213
|
}
|
|
213
214
|
}
|
|
215
|
+
|
|
216
|
+
// Validate basic required fields and collect errors
|
|
217
|
+
let rowHasError = false;
|
|
214
218
|
if (!pretranslatedE["@id"]) {
|
|
215
|
-
|
|
216
|
-
|
|
219
|
+
errors.push(`Row ${i + 2}: is missing an @id`);
|
|
220
|
+
rowHasError = true;
|
|
217
221
|
}
|
|
218
222
|
if (!pretranslatedE["@type"]) {
|
|
219
|
-
|
|
220
|
-
|
|
223
|
+
errors.push(`Row ${i + 2}: is missing a @type`);
|
|
224
|
+
rowHasError = true;
|
|
225
|
+
}
|
|
226
|
+
if (pretranslatedE["@id"] && !pretranslatedE["@id"].startsWith('http') && !pretranslatedE["@id"].startsWith('ce-')) {
|
|
227
|
+
errors.push(`Row ${i + 2}: @id must be a valid URI or start with 'ce-'`);
|
|
228
|
+
rowHasError = true;
|
|
221
229
|
}
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
|
|
230
|
+
if (rowHasError) {
|
|
231
|
+
rowsWithErrors.add(i);
|
|
232
|
+
continue;
|
|
225
233
|
}
|
|
226
234
|
if (
|
|
227
235
|
pretranslatedE["@type"] ==
|
|
@@ -229,15 +237,20 @@ module.exports = class CTDLASNCSVImport {
|
|
|
229
237
|
) {
|
|
230
238
|
// Validate required properties (only if validation rules provided)
|
|
231
239
|
if (validationRules && validationRules.requiredProps) {
|
|
232
|
-
const
|
|
240
|
+
const validationErrors = CTDLASNCSVImport.validateRequiredProperties(
|
|
233
241
|
pretranslatedE,
|
|
234
242
|
"ceasn:CompetencyFramework",
|
|
235
243
|
i + 2,
|
|
236
244
|
validationRules.requiredProps
|
|
237
245
|
);
|
|
238
|
-
if (
|
|
239
|
-
|
|
240
|
-
|
|
246
|
+
if (validationErrors) {
|
|
247
|
+
if (Array.isArray(validationErrors)) {
|
|
248
|
+
errors.push(...validationErrors);
|
|
249
|
+
} else {
|
|
250
|
+
errors.push(validationErrors);
|
|
251
|
+
}
|
|
252
|
+
rowsWithErrors.add(i);
|
|
253
|
+
continue;
|
|
241
254
|
}
|
|
242
255
|
}
|
|
243
256
|
|
|
@@ -251,7 +264,9 @@ module.exports = class CTDLASNCSVImport {
|
|
|
251
264
|
try {
|
|
252
265
|
let existing = await EcRepository.get(translator.id);
|
|
253
266
|
if (existing && existing.type !== 'Framework') {
|
|
254
|
-
|
|
267
|
+
errors.push(`Row ${i + 2}: ${translator.id} already exists as a ${existing.type}`);
|
|
268
|
+
rowsWithErrors.add(i);
|
|
269
|
+
continue;
|
|
255
270
|
}
|
|
256
271
|
} catch (e) {
|
|
257
272
|
console.error(e);
|
|
@@ -371,15 +386,20 @@ module.exports = class CTDLASNCSVImport {
|
|
|
371
386
|
) {
|
|
372
387
|
// Validate required properties (only if validation rules provided)
|
|
373
388
|
if (validationRules && validationRules.requiredProps) {
|
|
374
|
-
const
|
|
389
|
+
const validationErrors = CTDLASNCSVImport.validateRequiredProperties(
|
|
375
390
|
pretranslatedE,
|
|
376
391
|
"ceasn:Competency",
|
|
377
392
|
i + 2,
|
|
378
393
|
validationRules.requiredProps
|
|
379
394
|
);
|
|
380
|
-
if (
|
|
381
|
-
|
|
382
|
-
|
|
395
|
+
if (validationErrors) {
|
|
396
|
+
if (Array.isArray(validationErrors)) {
|
|
397
|
+
errors.push(...validationErrors);
|
|
398
|
+
} else {
|
|
399
|
+
errors.push(validationErrors);
|
|
400
|
+
}
|
|
401
|
+
rowsWithErrors.add(i);
|
|
402
|
+
continue;
|
|
383
403
|
}
|
|
384
404
|
}
|
|
385
405
|
|
|
@@ -418,8 +438,9 @@ module.exports = class CTDLASNCSVImport {
|
|
|
418
438
|
let f = new EcCompetency();
|
|
419
439
|
f.copyFrom(e);
|
|
420
440
|
if (e["id"] == null) {
|
|
421
|
-
|
|
422
|
-
|
|
441
|
+
errors.push(`Row ${i+2}: Competency is missing an id`);
|
|
442
|
+
rowsWithErrors.add(i);
|
|
443
|
+
continue;
|
|
423
444
|
}
|
|
424
445
|
if (e["ceasn:isPartOf"] != null) {
|
|
425
446
|
let shortId = EcRemoteLinkedData.trimVersionFromUrl(e["ceasn:isPartOf"]);
|
|
@@ -461,10 +482,11 @@ module.exports = class CTDLASNCSVImport {
|
|
|
461
482
|
}
|
|
462
483
|
}
|
|
463
484
|
if (!done) {
|
|
464
|
-
|
|
485
|
+
errors.push(
|
|
465
486
|
`Row ${i+2}: Could not find framework:${e["type"]} (Possibly missing ceasn:isPartOf or ceasn:isChildOf)`
|
|
466
487
|
);
|
|
467
|
-
|
|
488
|
+
rowsWithErrors.add(i);
|
|
489
|
+
continue;
|
|
468
490
|
}
|
|
469
491
|
if (parent != null) {
|
|
470
492
|
if (parent["type"] == "Framework") {
|
|
@@ -479,18 +501,20 @@ module.exports = class CTDLASNCSVImport {
|
|
|
479
501
|
)
|
|
480
502
|
].competency.push(f.shortId());
|
|
481
503
|
} else {
|
|
482
|
-
|
|
504
|
+
errors.push(
|
|
483
505
|
`Row ${i+2}: Object cannot trace to framework:` +
|
|
484
506
|
e["type"]
|
|
485
507
|
);
|
|
486
|
-
|
|
508
|
+
rowsWithErrors.add(i);
|
|
509
|
+
continue;
|
|
487
510
|
}
|
|
488
511
|
} else {
|
|
489
|
-
|
|
512
|
+
errors.push(
|
|
490
513
|
`Row ${i+2}: Object has no framework:` +
|
|
491
514
|
e["type"]
|
|
492
515
|
);
|
|
493
|
-
|
|
516
|
+
rowsWithErrors.add(i);
|
|
517
|
+
continue;
|
|
494
518
|
}
|
|
495
519
|
}
|
|
496
520
|
if (
|
|
@@ -682,15 +706,22 @@ module.exports = class CTDLASNCSVImport {
|
|
|
682
706
|
pretranslatedE["@type"] == null ||
|
|
683
707
|
pretranslatedE["@type"] == ""
|
|
684
708
|
) {
|
|
685
|
-
|
|
686
|
-
|
|
709
|
+
errors.push(`Row ${i+2}: Missing or empty @type`);
|
|
710
|
+
rowsWithErrors.add(i);
|
|
711
|
+
continue;
|
|
687
712
|
} else {
|
|
688
|
-
|
|
713
|
+
errors.push(
|
|
689
714
|
`Row ${i+2}: Found unknown type:` + pretranslatedE["@type"]
|
|
690
715
|
);
|
|
691
|
-
|
|
716
|
+
rowsWithErrors.add(i);
|
|
717
|
+
continue;
|
|
692
718
|
}
|
|
693
719
|
}
|
|
720
|
+
// If there are any errors, report them all at once
|
|
721
|
+
if (errors.length > 0) {
|
|
722
|
+
failure(errors);
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
694
725
|
success(frameworkArray, competencies, relations);
|
|
695
726
|
},
|
|
696
727
|
error: failure
|
|
@@ -712,6 +743,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
712
743
|
encoding: "UTF-8",
|
|
713
744
|
complete: async function(results) {
|
|
714
745
|
let tabularData = results["data"];
|
|
746
|
+
let errors = []; // Collect all errors to display at once
|
|
715
747
|
try {
|
|
716
748
|
for (let data of tabularData) {
|
|
717
749
|
for (let [key, value] of Object.entries(data)) {
|
|
@@ -729,8 +761,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
729
761
|
validationRules.hierarchyRules
|
|
730
762
|
);
|
|
731
763
|
if (hierarchyError) {
|
|
732
|
-
|
|
733
|
-
return;
|
|
764
|
+
errors.push(hierarchyError);
|
|
734
765
|
}
|
|
735
766
|
}
|
|
736
767
|
|
|
@@ -742,23 +773,31 @@ module.exports = class CTDLASNCSVImport {
|
|
|
742
773
|
let competencyRows = {};
|
|
743
774
|
let relations = [];
|
|
744
775
|
let relationById = {};
|
|
776
|
+
let rowsWithErrors = new Set(); // Track rows that have errors
|
|
745
777
|
for (let i = 0; i < tabularData.length; i++) {
|
|
746
778
|
let pretranslatedE = tabularData[i];
|
|
747
779
|
// Skip extra lines if found in file
|
|
748
780
|
if (!pretranslatedE) {
|
|
749
781
|
continue;
|
|
750
782
|
}
|
|
783
|
+
|
|
784
|
+
// Validate basic required fields and collect errors
|
|
785
|
+
let rowHasError = false;
|
|
751
786
|
if (!pretranslatedE["@id"]) {
|
|
752
|
-
|
|
753
|
-
|
|
787
|
+
errors.push(`Row ${i + 2}: is missing an @id`);
|
|
788
|
+
rowHasError = true;
|
|
754
789
|
}
|
|
755
|
-
if (!pretranslatedE["@id"].startsWith('http') && !pretranslatedE["@id"].startsWith('ce-')) {
|
|
756
|
-
|
|
757
|
-
|
|
790
|
+
if (pretranslatedE["@id"] && !pretranslatedE["@id"].startsWith('http') && !pretranslatedE["@id"].startsWith('ce-')) {
|
|
791
|
+
errors.push(`Row ${i + 2}: @id must be a valid URI or start with 'ce-'`);
|
|
792
|
+
rowHasError = true;
|
|
758
793
|
}
|
|
759
794
|
if (!pretranslatedE["@type"]) {
|
|
760
|
-
|
|
761
|
-
|
|
795
|
+
errors.push(`Row ${i + 2}: is missing a @type`);
|
|
796
|
+
rowHasError = true;
|
|
797
|
+
}
|
|
798
|
+
if (rowHasError) {
|
|
799
|
+
rowsWithErrors.add(i);
|
|
800
|
+
continue;
|
|
762
801
|
}
|
|
763
802
|
if (
|
|
764
803
|
pretranslatedE["@type"].toLowerCase().startsWith('sample') || pretranslatedE["@type"].toLowerCase().startsWith('instruction')
|
|
@@ -783,15 +822,20 @@ module.exports = class CTDLASNCSVImport {
|
|
|
783
822
|
) {
|
|
784
823
|
// Validate required properties (only if validation rules provided)
|
|
785
824
|
if (validationRules && validationRules.requiredProps) {
|
|
786
|
-
const
|
|
825
|
+
const validationErrors = CTDLASNCSVImport.validateRequiredProperties(
|
|
787
826
|
pretranslatedE,
|
|
788
827
|
"ceterms:Collection",
|
|
789
828
|
i + 2,
|
|
790
829
|
validationRules.requiredProps
|
|
791
830
|
);
|
|
792
|
-
if (
|
|
793
|
-
|
|
794
|
-
|
|
831
|
+
if (validationErrors) {
|
|
832
|
+
if (Array.isArray(validationErrors)) {
|
|
833
|
+
errors.push(...validationErrors);
|
|
834
|
+
} else {
|
|
835
|
+
errors.push(validationErrors);
|
|
836
|
+
}
|
|
837
|
+
rowsWithErrors.add(i);
|
|
838
|
+
continue;
|
|
795
839
|
}
|
|
796
840
|
}
|
|
797
841
|
let translator = new EcLinkedData(null, null);
|
|
@@ -867,15 +911,20 @@ module.exports = class CTDLASNCSVImport {
|
|
|
867
911
|
) {
|
|
868
912
|
// Validate required properties (only if validation rules provided)
|
|
869
913
|
if (validationRules && validationRules.requiredProps) {
|
|
870
|
-
const
|
|
914
|
+
const validationErrors = CTDLASNCSVImport.validateRequiredProperties(
|
|
871
915
|
pretranslatedE,
|
|
872
916
|
"ceterms:Collection:Competency",
|
|
873
917
|
i + 2,
|
|
874
918
|
validationRules.requiredProps
|
|
875
919
|
);
|
|
876
|
-
if (
|
|
877
|
-
|
|
878
|
-
|
|
920
|
+
if (validationErrors) {
|
|
921
|
+
if (Array.isArray(validationErrors)) {
|
|
922
|
+
errors.push(...validationErrors);
|
|
923
|
+
} else {
|
|
924
|
+
errors.push(validationErrors);
|
|
925
|
+
}
|
|
926
|
+
rowsWithErrors.add(i);
|
|
927
|
+
continue;
|
|
879
928
|
}
|
|
880
929
|
}
|
|
881
930
|
let translator = new EcLinkedData(null, null);
|
|
@@ -901,8 +950,9 @@ module.exports = class CTDLASNCSVImport {
|
|
|
901
950
|
let f = new EcCompetency();
|
|
902
951
|
f.copyFrom(e);
|
|
903
952
|
if (e["id"] == null) {
|
|
904
|
-
|
|
905
|
-
|
|
953
|
+
errors.push(`Row ${i+2}: Competency is missing an id`);
|
|
954
|
+
rowsWithErrors.add(i);
|
|
955
|
+
continue;
|
|
906
956
|
}
|
|
907
957
|
if (e["ceterms:isMemberOf"] != null) {
|
|
908
958
|
if (!EcArray.isArray(e["ceterms:isMemberOf"])) {
|
|
@@ -1095,15 +1145,22 @@ module.exports = class CTDLASNCSVImport {
|
|
|
1095
1145
|
pretranslatedE["@type"] == null ||
|
|
1096
1146
|
pretranslatedE["@type"] == ""
|
|
1097
1147
|
) {
|
|
1098
|
-
|
|
1099
|
-
|
|
1148
|
+
errors.push(`Row ${i+2}: Missing or empty @type`);
|
|
1149
|
+
rowsWithErrors.add(i);
|
|
1150
|
+
continue;
|
|
1100
1151
|
} else {
|
|
1101
|
-
|
|
1152
|
+
errors.push(
|
|
1102
1153
|
`Row ${i+2}: Found unknown type:` + pretranslatedE["@type"]
|
|
1103
1154
|
);
|
|
1104
|
-
|
|
1155
|
+
rowsWithErrors.add(i);
|
|
1156
|
+
continue;
|
|
1105
1157
|
}
|
|
1106
1158
|
}
|
|
1159
|
+
// If there are any errors, report them all at once
|
|
1160
|
+
if (errors.length > 0) {
|
|
1161
|
+
failure(errors);
|
|
1162
|
+
return;
|
|
1163
|
+
}
|
|
1107
1164
|
success(frameworkArray, competencies, relations);
|
|
1108
1165
|
},
|
|
1109
1166
|
error: failure
|