cassproject 5.0.20 → 5.0.23
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/package.json +49 -52
- package/src/com/eduworks/ec/array/EcArray.js +1 -1
- package/src/com/eduworks/ec/array/EcAsyncHelper.js +1 -1
- package/src/com/eduworks/ec/array/EcObject.js +1 -1
- package/src/com/eduworks/ec/crypto/EcAes.js +1 -1
- package/src/com/eduworks/ec/crypto/EcAesCtrAsync.js +20 -22
- package/src/com/eduworks/ec/crypto/EcCrypto.js +8 -4
- package/src/com/eduworks/ec/crypto/EcPk.js +7 -7
- package/src/com/eduworks/ec/crypto/EcPpk.js +7 -7
- package/src/com/eduworks/ec/crypto/EcRsaOaepAsync.js +20 -19
- package/src/com/eduworks/ec/crypto/EcRsaOaepAsyncWorker.js +7 -6
- package/src/com/eduworks/ec/crypto/forgeAsync.js +1 -9
- package/src/com/eduworks/ec/crypto/forgeAsyncNode.js +8 -8
- package/src/com/eduworks/ec/graph/EcDirectedGraph.js +1 -2
- package/src/com/eduworks/ec/graph/EcFrameworkGraph.js +18 -24
- package/src/com/eduworks/ec/promises/helpers.js +1 -3
- package/src/com/eduworks/ec/remote/EcRemote.js +9 -9
- package/src/com/eduworks/ec/task/EcAsyncTask.js +5 -20
- package/src/com/eduworks/ec/task/EcAsyncTaskManager.js +3 -4
- package/src/com/eduworks/ec/task/EcAsyncTaskWorker.js +9 -11
- package/src/com/eduworks/schema/cfd/competency/CfdFramework.js +36 -37
- package/src/com/eduworks/schema/ebac/EbacContact.js +3 -9
- package/src/com/eduworks/schema/ebac/EbacContactGrant.js +3 -9
- package/src/com/eduworks/schema/ebac/EbacCredential.js +3 -9
- package/src/com/eduworks/schema/ebac/EbacCredentialCommit.js +3 -9
- package/src/com/eduworks/schema/ebac/EbacCredentialRequest.js +3 -9
- package/src/com/eduworks/schema/ebac/EbacCredentials.js +3 -9
- package/src/com/eduworks/schema/ebac/EbacEncryptedSecret.js +3 -9
- package/src/com/eduworks/schema/ebac/EbacEncryptedValue.js +5 -12
- package/src/com/eduworks/schema/ebac/EbacSignature.js +1 -6
- package/src/index/env.js +6 -3
- package/src/index/polyfills.js +13 -27
- package/src/org/angles/schema/angles/VideoStory.js +1 -1
- package/src/org/cass/competency/EcCompetency.js +1 -2
- package/src/org/cass/competency/EcDirectory.js +1 -2
- package/src/org/cass/competency/EcFramework.js +9 -12
- package/src/org/cass/exporter/CSVExport.js +12 -13
- package/src/org/cass/importer/ASNImport.js +3 -6
- package/src/org/cass/importer/CSVImport.js +9 -17
- package/src/org/cass/importer/CTDLASNCSVConceptImport.js +25 -121
- package/src/org/cass/importer/CTDLASNCSVImport.js +88 -133
- package/src/org/cass/importer/FrameworkImport.js +16 -30
- package/src/org/cass/importer/MedbiqImport.js +3 -4
- package/src/org/cass/importer/TabStructuredImport.js +6 -6
- package/src/org/cass/profile/EcAssertion.js +40 -51
- package/src/org/cassproject/ebac/identity/EcContactGrant.js +2 -2
- package/src/org/cassproject/ebac/identity/EcIdentityManager.js +56 -62
- package/src/org/cassproject/ebac/identity/EcRekeyRequest.js +3 -2
- package/src/org/cassproject/ebac/identity/remote/EcRemoteIdentityManager.js +51 -62
- package/src/org/cassproject/ebac/identity/remote/OAuth2FileBasedRemoteIdentityManager.js +56 -70
- package/src/org/cassproject/ebac/identity/remote/RemoteIdentityManagerInterface.js +3 -3
- package/src/org/cassproject/ebac/repository/EcEncryptedValue.js +51 -44
- package/src/org/cassproject/ebac/repository/EcRepository.js +228 -301
- package/src/org/cassproject/general/repository/GeneralFile.js +8 -9
- package/src/org/cassproject/schema/cass/competency/Competency.js +10 -11
- package/src/org/cassproject/schema/cass/competency/Framework.js +10 -11
- package/src/org/cassproject/schema/cass/competency/Level.js +10 -11
- package/src/org/cassproject/schema/cass/competency/Relation.js +10 -11
- package/src/org/cassproject/schema/cass/competency/RollupRule.js +7 -7
- package/src/org/cassproject/schema/cass/profile/Assertion.js +20 -27
- package/src/org/cassproject/schema/cass/profile/AssertionEnvelope.js +3 -5
- package/src/org/cassproject/schema/general/EcRemoteLinkedData.js +41 -44
- package/src/org/json/ld/EcLinkedData.js +10 -12
- package/src/org/schema/EcOrganization.js +10 -10
- package/src/org/schema/EcQuestion.js +8 -8
- package/src/org/w3/skos/Concept.js +6 -6
- package/src/org/w3/skos/ConceptScheme.js +6 -6
- package/src/org/w3/skos/EcConcept.js +1 -2
- package/src/org/w3/skos/EcConceptScheme.js +1 -2
- package/src/test/0.start.test.js +1 -1
- package/src/test/0.testLibraries.test.js +172 -0
- package/src/test/1.ASNImport.test.js +122 -0
- package/src/test/1.CSVExport.test.js +57 -0
- package/src/test/1.CSVImport.test.js +185 -0
- package/src/test/1.CTDLASNCSVConceptImport.test.js +348 -0
- package/src/test/1.CTDLASNCSVImport.import.test.js +497 -0
- package/src/test/1.Ebac.schema.test.js +107 -0
- package/src/test/1.EcAssertion.decrypt.test.js +98 -0
- package/src/test/1.EcAssertion.pinned.test.js +160 -0
- package/src/test/1.EcCrypto.formats.test.js +293 -0
- package/src/test/1.EcCrypto.generateUUID.test.js +47 -0
- package/src/test/1.EcEncryptedValue.helpers.test.js +236 -0
- package/src/test/1.EcHelpers.misc.test.js +259 -0
- package/src/test/1.EcIdentityManager.keys.test.js +439 -0
- package/src/test/1.EcOrganization.rekey.test.js +68 -0
- package/src/test/1.EcRemote.responses.test.js +155 -0
- package/src/test/1.EcRemoteIdentityManager.flows.test.js +326 -0
- package/src/test/1.EcRemoteLinkedData.pinned.test.js +473 -0
- package/src/test/1.EcRepository.l0.test.js +14 -5
- package/src/test/1.EcRepository.l1.test.js +16 -7
- package/src/test/1.EcRepository.l2.test.js +20 -11
- package/src/test/1.EcRepository.unit.test.js +961 -0
- package/src/test/1.EcRsaOaep.test.js +2 -12
- package/src/test/1.EcRsaOaepAsync.test.js +2 -12
- package/src/test/1.EcRsaOaepAsyncWorker.test.js +2 -12
- package/src/test/1.EcRsaOaepAsyncWorker.workers.test.js +147 -0
- package/src/test/1.FrameworkImport.test.js +170 -0
- package/src/test/1.MedbiqImport.test.js +92 -0
- package/src/test/1.OAuth2FileBasedRemoteIdentityManager.test.js +399 -0
- package/src/test/1.TabStructuredImport.test.js +61 -0
- package/src/test/1.polyfills.test.js +89 -0
- package/src/test/1.schema.competency.pinned.test.js +384 -0
- package/src/test/3.EcFrameworkGraph.test.js +3 -2
- package/src/test/4.schema.test.js +33 -28
- package/src/test/4.skyId.test.js +4 -2
- package/src/test/cypressValue.js +9 -0
- package/src/test/2.EcCompetency.test.js +0 -17
|
@@ -18,13 +18,61 @@ function setVersionIdentifier(f) {
|
|
|
18
18
|
},
|
|
19
19
|
"ceterms:identifierType": parts.length > 2 ? parts[2] : undefined
|
|
20
20
|
};
|
|
21
|
-
} catch (e) {
|
|
21
|
+
} catch (e) { // NOSONAR - A value that cannot be split (not a string) is dropped from the list.
|
|
22
22
|
delete f["ceterms:versionIdentifier"][i];
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
const CTID_PATTERN = /^ce-[0-9a-fA-F-]+$/;
|
|
29
|
+
|
|
30
|
+
function isCtidOrAllowedRegistryUrl(value, allowedOrigins) {
|
|
31
|
+
if (value === "" || CTID_PATTERN.test(value)) return true;
|
|
32
|
+
let origin = null;
|
|
33
|
+
try {
|
|
34
|
+
origin = new URL(value).origin;
|
|
35
|
+
} catch (e) {
|
|
36
|
+
// Not parseable as a URL; reported as an error by the caller.
|
|
37
|
+
}
|
|
38
|
+
return origin != null && allowedOrigins.includes(origin);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function registryUrlFieldErrors(raw, field, rowIndex, allowedOrigins) {
|
|
42
|
+
if (raw == null) return [];
|
|
43
|
+
const errors = [];
|
|
44
|
+
const values = Array.isArray(raw) ? raw : String(raw).split("|");
|
|
45
|
+
for (let value of values) {
|
|
46
|
+
value = String(value).trim();
|
|
47
|
+
if (!isCtidOrAllowedRegistryUrl(value, allowedOrigins)) {
|
|
48
|
+
errors.push(`Row ${rowIndex}: ${field} must be a CTID or a credential registry URL for this environment (got: ${value})`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return errors;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function rowHasAnyProperty(row, props) {
|
|
55
|
+
for (const prop of props) {
|
|
56
|
+
if (row[prop]) return true;
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// True when a row of the given type has one of the required properties, or any typed row has a child property.
|
|
62
|
+
function hasHierarchyProperty(tabularData, type, rules) {
|
|
63
|
+
let hasRequiredProp = false;
|
|
64
|
+
let hasChildProp = false;
|
|
65
|
+
for (const row of tabularData) {
|
|
66
|
+
if (!row?.["@type"]) continue;
|
|
67
|
+
if (row["@type"] === type && rules.requiredProperties && rowHasAnyProperty(row, rules.requiredProperties))
|
|
68
|
+
hasRequiredProp = true;
|
|
69
|
+
if (rules.childProperties && rowHasAnyProperty(row, rules.childProperties))
|
|
70
|
+
hasChildProp = true;
|
|
71
|
+
if (hasRequiredProp && hasChildProp) break;
|
|
72
|
+
}
|
|
73
|
+
return hasRequiredProp || hasChildProp;
|
|
74
|
+
}
|
|
75
|
+
|
|
28
76
|
module.exports = class CTDLASNCSVImport {
|
|
29
77
|
static analyzeFile(file, success, failure) {
|
|
30
78
|
if (file == null) {
|
|
@@ -74,7 +122,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
74
122
|
line: i
|
|
75
123
|
});
|
|
76
124
|
} else {
|
|
77
|
-
const originalAlreadyAdded = duplicates.
|
|
125
|
+
const originalAlreadyAdded = duplicates.some((duplicate) => duplicate.line === uniqueRows[existing].line);
|
|
78
126
|
if (!originalAlreadyAdded) {
|
|
79
127
|
duplicates.push(uniqueRows[existing]);
|
|
80
128
|
}
|
|
@@ -91,20 +139,11 @@ module.exports = class CTDLASNCSVImport {
|
|
|
91
139
|
for (let i = 0; i < tabularData.length; i++) {
|
|
92
140
|
if (i == 0) continue;
|
|
93
141
|
let col = tabularData[i];
|
|
94
|
-
if (
|
|
95
|
-
col[typeCol] != null &&
|
|
96
|
-
col[typeCol].trim() == "ceasn:CompetencyFramework"
|
|
97
|
-
)
|
|
142
|
+
if (col[typeCol]?.trim() == "ceasn:CompetencyFramework")
|
|
98
143
|
frameworkCounter++;
|
|
99
|
-
else if (
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
)
|
|
103
|
-
collectionCounter++;
|
|
104
|
-
else if (
|
|
105
|
-
col[typeCol] != null &&
|
|
106
|
-
col[typeCol].trim() == "ceasn:Competency"
|
|
107
|
-
) {
|
|
144
|
+
else if (col[typeCol]?.trim() == "ceterms:Collection")
|
|
145
|
+
collectionCounter++;
|
|
146
|
+
else if (col[typeCol]?.trim() == "ceasn:Competency") {
|
|
108
147
|
competencyCounter++;
|
|
109
148
|
// if (colCompetencyText && !col[colCompetencyText].trim()) {
|
|
110
149
|
// this.error('CTDLASN Parse Error: Competency text is empty on line ' + (i+1));
|
|
@@ -178,7 +217,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
178
217
|
}
|
|
179
218
|
}
|
|
180
219
|
|
|
181
|
-
const terms =
|
|
220
|
+
const terms = structuredClone(await EcRemote.getExpectingObject("https://schema.cassproject.org/0.4/jsonld1.1/ceasn2cassTerms"));
|
|
182
221
|
let frameworks = {};
|
|
183
222
|
let frameworkArray = [];
|
|
184
223
|
let frameworkRows = {};
|
|
@@ -187,7 +226,6 @@ module.exports = class CTDLASNCSVImport {
|
|
|
187
226
|
let relations = [];
|
|
188
227
|
let relationById = {};
|
|
189
228
|
let competencyCtids = {};
|
|
190
|
-
let rowsWithErrors = new Set(); // Track rows that have errors to avoid duplicate processing
|
|
191
229
|
for (let i = 0; i < tabularData.length; i++) {
|
|
192
230
|
if (!tabularData[i]) {
|
|
193
231
|
continue;
|
|
@@ -197,7 +235,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
197
235
|
if (!pretranslatedE) {
|
|
198
236
|
continue;
|
|
199
237
|
}
|
|
200
|
-
if (pretranslatedE["@type"]
|
|
238
|
+
if (pretranslatedE["@type"]?.toLowerCase() &&
|
|
201
239
|
(pretranslatedE["@type"].toLowerCase().startsWith('sample') || pretranslatedE["@type"].toLowerCase().startsWith('instruction'))
|
|
202
240
|
) {
|
|
203
241
|
continue;
|
|
@@ -210,7 +248,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
210
248
|
}
|
|
211
249
|
// Skip competency if ctid is specified
|
|
212
250
|
if (skip && Array.isArray(skip) && skip.length > 0) {
|
|
213
|
-
if (skip.find((element) => element.ctid ? element.ctid.includes(pretranslatedE["ceterms:ctid"]) : element === pretranslatedE["ceterms:ctid"])) {
|
|
251
|
+
if (skip.find((element) => element.ctid ? element.ctid.includes(pretranslatedE["ceterms:ctid"]) : element === pretranslatedE["ceterms:ctid"])) { // NOSONAR - some() would also match a falsy skip entry, find() does not
|
|
214
252
|
continue;
|
|
215
253
|
}
|
|
216
254
|
}
|
|
@@ -230,7 +268,6 @@ module.exports = class CTDLASNCSVImport {
|
|
|
230
268
|
rowHasError = true;
|
|
231
269
|
}
|
|
232
270
|
if (rowHasError) {
|
|
233
|
-
rowsWithErrors.add(i);
|
|
234
271
|
continue;
|
|
235
272
|
}
|
|
236
273
|
if (
|
|
@@ -238,7 +275,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
238
275
|
"ceasn:CompetencyFramework"
|
|
239
276
|
) {
|
|
240
277
|
// Validate required properties (only if validation rules provided)
|
|
241
|
-
if (validationRules
|
|
278
|
+
if (validationRules?.requiredProps) {
|
|
242
279
|
const validationErrors = CTDLASNCSVImport.validateRequiredProperties(
|
|
243
280
|
pretranslatedE,
|
|
244
281
|
"ceasn:CompetencyFramework",
|
|
@@ -251,12 +288,11 @@ module.exports = class CTDLASNCSVImport {
|
|
|
251
288
|
} else {
|
|
252
289
|
errors.push(validationErrors);
|
|
253
290
|
}
|
|
254
|
-
rowsWithErrors.add(i);
|
|
255
291
|
continue;
|
|
256
292
|
}
|
|
257
293
|
}
|
|
258
294
|
// Validate registry URL fields (only if url rules provided)
|
|
259
|
-
if (validationRules
|
|
295
|
+
if (validationRules?.urlRules) {
|
|
260
296
|
const urlErrors = CTDLASNCSVImport.validateRegistryUrls(
|
|
261
297
|
pretranslatedE,
|
|
262
298
|
"ceasn:CompetencyFramework",
|
|
@@ -265,7 +301,6 @@ module.exports = class CTDLASNCSVImport {
|
|
|
265
301
|
);
|
|
266
302
|
if (urlErrors) {
|
|
267
303
|
errors.push(...urlErrors);
|
|
268
|
-
rowsWithErrors.add(i);
|
|
269
304
|
continue;
|
|
270
305
|
}
|
|
271
306
|
}
|
|
@@ -281,7 +316,6 @@ module.exports = class CTDLASNCSVImport {
|
|
|
281
316
|
let existing = await EcRepository.get(translator.id);
|
|
282
317
|
if (existing && existing.type !== 'Framework') {
|
|
283
318
|
errors.push(`Row ${i + 2}: ${translator.id} already exists as a ${existing.type}`);
|
|
284
|
-
rowsWithErrors.add(i);
|
|
285
319
|
continue;
|
|
286
320
|
}
|
|
287
321
|
} catch (e) {
|
|
@@ -335,10 +369,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
335
369
|
}
|
|
336
370
|
if (ceo != null) f.addOwner(ceo.ppk.toPk());
|
|
337
371
|
if (
|
|
338
|
-
EcFramework.template != null
|
|
339
|
-
EcFramework.template[
|
|
340
|
-
"schema:dateCreated"
|
|
341
|
-
] != null
|
|
372
|
+
EcFramework.template?.["schema:dateCreated"] != null
|
|
342
373
|
) {
|
|
343
374
|
CTDLASNCSVImport.setDateCreated(e, f);
|
|
344
375
|
}
|
|
@@ -369,7 +400,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
369
400
|
for (let i = val.length-1; i >=0; i--) {
|
|
370
401
|
if (!val[i].startsWith('http')) {
|
|
371
402
|
if (val[i] === "Deprecated" || val[i] === "Published" || val[i] === "Draft" ) {
|
|
372
|
-
val[i] = "http://credreg.net/ctdlasn/vocabs/publicationStatus/" + val[i];
|
|
403
|
+
val[i] = "http://credreg.net/ctdlasn/vocabs/publicationStatus/" + val[i]; // NOSONAR - JSON-LD identifier, never fetched
|
|
373
404
|
} else {
|
|
374
405
|
val.splice(i, 1);
|
|
375
406
|
}
|
|
@@ -401,7 +432,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
401
432
|
pretranslatedE["@type"] == "ceasn:Competency"
|
|
402
433
|
) {
|
|
403
434
|
// Validate required properties (only if validation rules provided)
|
|
404
|
-
if (validationRules
|
|
435
|
+
if (validationRules?.requiredProps) {
|
|
405
436
|
const validationErrors = CTDLASNCSVImport.validateRequiredProperties(
|
|
406
437
|
pretranslatedE,
|
|
407
438
|
"ceasn:Competency",
|
|
@@ -414,12 +445,11 @@ module.exports = class CTDLASNCSVImport {
|
|
|
414
445
|
} else {
|
|
415
446
|
errors.push(validationErrors);
|
|
416
447
|
}
|
|
417
|
-
rowsWithErrors.add(i);
|
|
418
448
|
continue;
|
|
419
449
|
}
|
|
420
450
|
}
|
|
421
451
|
// Validate registry URL fields (only if url rules provided)
|
|
422
|
-
if (validationRules
|
|
452
|
+
if (validationRules?.urlRules) {
|
|
423
453
|
const urlErrors = CTDLASNCSVImport.validateRegistryUrls(
|
|
424
454
|
pretranslatedE,
|
|
425
455
|
"ceasn:Competency",
|
|
@@ -428,7 +458,6 @@ module.exports = class CTDLASNCSVImport {
|
|
|
428
458
|
);
|
|
429
459
|
if (urlErrors) {
|
|
430
460
|
errors.push(...urlErrors);
|
|
431
|
-
rowsWithErrors.add(i);
|
|
432
461
|
continue;
|
|
433
462
|
}
|
|
434
463
|
}
|
|
@@ -469,7 +498,6 @@ module.exports = class CTDLASNCSVImport {
|
|
|
469
498
|
f.copyFrom(e);
|
|
470
499
|
if (e["id"] == null) {
|
|
471
500
|
errors.push(`Row ${i+2}: Competency is missing an id`);
|
|
472
|
-
rowsWithErrors.add(i);
|
|
473
501
|
continue;
|
|
474
502
|
}
|
|
475
503
|
if (e["ceasn:isPartOf"] != null) {
|
|
@@ -515,7 +543,6 @@ module.exports = class CTDLASNCSVImport {
|
|
|
515
543
|
errors.push(
|
|
516
544
|
`Row ${i+2}: Could not find framework:${e["type"]} (Possibly missing ceasn:isPartOf or ceasn:isChildOf)`
|
|
517
545
|
);
|
|
518
|
-
rowsWithErrors.add(i);
|
|
519
546
|
continue;
|
|
520
547
|
}
|
|
521
548
|
if (parent != null) {
|
|
@@ -535,7 +562,6 @@ module.exports = class CTDLASNCSVImport {
|
|
|
535
562
|
`Row ${i+2}: Object cannot trace to framework:` +
|
|
536
563
|
e["type"]
|
|
537
564
|
);
|
|
538
|
-
rowsWithErrors.add(i);
|
|
539
565
|
continue;
|
|
540
566
|
}
|
|
541
567
|
} else {
|
|
@@ -543,13 +569,11 @@ module.exports = class CTDLASNCSVImport {
|
|
|
543
569
|
`Row ${i+2}: Object has no framework:` +
|
|
544
570
|
e["type"]
|
|
545
571
|
);
|
|
546
|
-
rowsWithErrors.add(i);
|
|
547
572
|
continue;
|
|
548
573
|
}
|
|
549
574
|
}
|
|
550
575
|
if (
|
|
551
|
-
EcCompetency.template != null
|
|
552
|
-
EcCompetency.template["@owner"] != null
|
|
576
|
+
EcCompetency.template?.["@owner"] != null
|
|
553
577
|
) {
|
|
554
578
|
f["owner"] =
|
|
555
579
|
EcCompetency.template["@owner"];
|
|
@@ -576,10 +600,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
576
600
|
}
|
|
577
601
|
if (ceo != null) f.addOwner(ceo.ppk.toPk());
|
|
578
602
|
if (
|
|
579
|
-
EcCompetency.template != null
|
|
580
|
-
EcCompetency.template[
|
|
581
|
-
"schema:dateCreated"
|
|
582
|
-
] != null
|
|
603
|
+
EcCompetency.template?.["schema:dateCreated"] != null
|
|
583
604
|
) {
|
|
584
605
|
CTDLASNCSVImport.setDateCreated(e, f);
|
|
585
606
|
}
|
|
@@ -610,7 +631,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
610
631
|
for (let i = val.length-1; i >=0; i--) {
|
|
611
632
|
if (!val[i].startsWith('http')) {
|
|
612
633
|
if (val[i] === "Deprecated" || val[i] === "Published" || val[i] === "Draft" ) {
|
|
613
|
-
val[i] = "http://credreg.net/ctdlasn/vocabs/publicationStatus/" + val[i];
|
|
634
|
+
val[i] = "http://credreg.net/ctdlasn/vocabs/publicationStatus/" + val[i]; // NOSONAR - JSON-LD identifier, never fetched
|
|
614
635
|
} else {
|
|
615
636
|
val.splice(i, 1);
|
|
616
637
|
}
|
|
@@ -738,13 +759,11 @@ module.exports = class CTDLASNCSVImport {
|
|
|
738
759
|
pretranslatedE["@type"] == ""
|
|
739
760
|
) {
|
|
740
761
|
errors.push(`Row ${i+2}: Missing or empty @type`);
|
|
741
|
-
rowsWithErrors.add(i);
|
|
742
762
|
continue;
|
|
743
763
|
} else {
|
|
744
764
|
errors.push(
|
|
745
765
|
`Row ${i+2}: Found unknown type:` + pretranslatedE["@type"]
|
|
746
766
|
);
|
|
747
|
-
rowsWithErrors.add(i);
|
|
748
767
|
continue;
|
|
749
768
|
}
|
|
750
769
|
}
|
|
@@ -804,7 +823,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
804
823
|
}
|
|
805
824
|
}
|
|
806
825
|
|
|
807
|
-
const terms =
|
|
826
|
+
const terms = structuredClone(await EcRemote.getExpectingObject("https://schema.cassproject.org/0.4/jsonld1.1/ceasn2cassTerms"));
|
|
808
827
|
let frameworks = {};
|
|
809
828
|
let frameworkArray = [];
|
|
810
829
|
let frameworkRows = {};
|
|
@@ -813,7 +832,6 @@ module.exports = class CTDLASNCSVImport {
|
|
|
813
832
|
let relations = [];
|
|
814
833
|
let relationById = {};
|
|
815
834
|
let competencyCtids = {};
|
|
816
|
-
let rowsWithErrors = new Set(); // Track rows that have errors
|
|
817
835
|
for (let i = 0; i < tabularData.length; i++) {
|
|
818
836
|
let pretranslatedE = tabularData[i];
|
|
819
837
|
// Skip extra lines if found in file
|
|
@@ -836,7 +854,6 @@ module.exports = class CTDLASNCSVImport {
|
|
|
836
854
|
rowHasError = true;
|
|
837
855
|
}
|
|
838
856
|
if (rowHasError) {
|
|
839
|
-
rowsWithErrors.add(i);
|
|
840
857
|
continue;
|
|
841
858
|
}
|
|
842
859
|
if (
|
|
@@ -852,7 +869,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
852
869
|
}
|
|
853
870
|
// Skip competency if ctid is specified
|
|
854
871
|
if (skip && Array.isArray(skip) && skip.length > 0) {
|
|
855
|
-
if (skip.find((element) => element.ctid ? element.ctid.includes(pretranslatedE["ceterms:ctid"]) : element === pretranslatedE["ceterms:ctid"])) {
|
|
872
|
+
if (skip.find((element) => element.ctid ? element.ctid.includes(pretranslatedE["ceterms:ctid"]) : element === pretranslatedE["ceterms:ctid"])) { // NOSONAR - some() would also match a falsy skip entry, find() does not
|
|
856
873
|
continue;
|
|
857
874
|
}
|
|
858
875
|
}
|
|
@@ -861,7 +878,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
861
878
|
"ceterms:Collection"
|
|
862
879
|
) {
|
|
863
880
|
// Validate required properties (only if validation rules provided)
|
|
864
|
-
if (validationRules
|
|
881
|
+
if (validationRules?.requiredProps) {
|
|
865
882
|
const validationErrors = CTDLASNCSVImport.validateRequiredProperties(
|
|
866
883
|
pretranslatedE,
|
|
867
884
|
"ceterms:Collection",
|
|
@@ -874,12 +891,11 @@ module.exports = class CTDLASNCSVImport {
|
|
|
874
891
|
} else {
|
|
875
892
|
errors.push(validationErrors);
|
|
876
893
|
}
|
|
877
|
-
rowsWithErrors.add(i);
|
|
878
894
|
continue;
|
|
879
895
|
}
|
|
880
896
|
}
|
|
881
897
|
// Validate registry URL fields (only if url rules provided)
|
|
882
|
-
if (validationRules
|
|
898
|
+
if (validationRules?.urlRules) {
|
|
883
899
|
const urlErrors = CTDLASNCSVImport.validateRegistryUrls(
|
|
884
900
|
pretranslatedE,
|
|
885
901
|
"ceterms:Collection",
|
|
@@ -888,7 +904,6 @@ module.exports = class CTDLASNCSVImport {
|
|
|
888
904
|
);
|
|
889
905
|
if (urlErrors) {
|
|
890
906
|
errors.push(...urlErrors);
|
|
891
|
-
rowsWithErrors.add(i);
|
|
892
907
|
continue;
|
|
893
908
|
}
|
|
894
909
|
}
|
|
@@ -935,10 +950,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
935
950
|
}
|
|
936
951
|
if (ceo != null) f.addOwner(ceo.ppk.toPk());
|
|
937
952
|
if (
|
|
938
|
-
EcFramework.template != null
|
|
939
|
-
EcFramework.template[
|
|
940
|
-
"schema:dateCreated"
|
|
941
|
-
] != null
|
|
953
|
+
EcFramework.template?.["schema:dateCreated"] != null
|
|
942
954
|
) {
|
|
943
955
|
CTDLASNCSVImport.setDateCreated(e, f);
|
|
944
956
|
}
|
|
@@ -964,7 +976,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
964
976
|
pretranslatedE["@type"] == "ceasn:Competency"
|
|
965
977
|
) {
|
|
966
978
|
// Validate required properties (only if validation rules provided)
|
|
967
|
-
if (validationRules
|
|
979
|
+
if (validationRules?.requiredProps) {
|
|
968
980
|
const validationErrors = CTDLASNCSVImport.validateRequiredProperties(
|
|
969
981
|
pretranslatedE,
|
|
970
982
|
"ceterms:Collection:Competency",
|
|
@@ -977,12 +989,11 @@ module.exports = class CTDLASNCSVImport {
|
|
|
977
989
|
} else {
|
|
978
990
|
errors.push(validationErrors);
|
|
979
991
|
}
|
|
980
|
-
rowsWithErrors.add(i);
|
|
981
992
|
continue;
|
|
982
993
|
}
|
|
983
994
|
}
|
|
984
995
|
// Validate registry URL fields (only if url rules provided)
|
|
985
|
-
if (validationRules
|
|
996
|
+
if (validationRules?.urlRules) {
|
|
986
997
|
const urlErrors = CTDLASNCSVImport.validateRegistryUrls(
|
|
987
998
|
pretranslatedE,
|
|
988
999
|
"ceasn:Competency",
|
|
@@ -991,7 +1002,6 @@ module.exports = class CTDLASNCSVImport {
|
|
|
991
1002
|
);
|
|
992
1003
|
if (urlErrors) {
|
|
993
1004
|
errors.push(...urlErrors);
|
|
994
|
-
rowsWithErrors.add(i);
|
|
995
1005
|
continue;
|
|
996
1006
|
}
|
|
997
1007
|
}
|
|
@@ -1019,7 +1029,6 @@ module.exports = class CTDLASNCSVImport {
|
|
|
1019
1029
|
f.copyFrom(e);
|
|
1020
1030
|
if (e["id"] == null) {
|
|
1021
1031
|
errors.push(`Row ${i+2}: Competency is missing an id`);
|
|
1022
|
-
rowsWithErrors.add(i);
|
|
1023
1032
|
continue;
|
|
1024
1033
|
}
|
|
1025
1034
|
if (e["ceterms:isMemberOf"] != null) {
|
|
@@ -1034,16 +1043,14 @@ module.exports = class CTDLASNCSVImport {
|
|
|
1034
1043
|
}
|
|
1035
1044
|
}
|
|
1036
1045
|
if (
|
|
1037
|
-
EcCompetency.template != null
|
|
1038
|
-
EcCompetency.template["@owner"] != null
|
|
1046
|
+
EcCompetency.template?.["@owner"] != null
|
|
1039
1047
|
) {
|
|
1040
1048
|
f["owner"] =
|
|
1041
1049
|
EcCompetency.template["@owner"];
|
|
1042
1050
|
}
|
|
1043
1051
|
if (e["owner"] == null) {
|
|
1044
1052
|
if (
|
|
1045
|
-
frameworkRows[EcRemoteLinkedData.trimVersionFromUrl(e["ceterms:isMemberOf"][0])]
|
|
1046
|
-
frameworkRows[EcRemoteLinkedData.trimVersionFromUrl(e["ceterms:isMemberOf"][0])]["owner"] != null
|
|
1053
|
+
frameworkRows[EcRemoteLinkedData.trimVersionFromUrl(e["ceterms:isMemberOf"][0])]?.["owner"] != null
|
|
1047
1054
|
)
|
|
1048
1055
|
e["owner"] =
|
|
1049
1056
|
frameworkRows[EcRemoteLinkedData.trimVersionFromUrl(
|
|
@@ -1061,10 +1068,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
1061
1068
|
}
|
|
1062
1069
|
if (ceo != null) f.addOwner(ceo.ppk.toPk());
|
|
1063
1070
|
if (
|
|
1064
|
-
EcCompetency.template != null
|
|
1065
|
-
EcCompetency.template[
|
|
1066
|
-
"schema:dateCreated"
|
|
1067
|
-
] != null
|
|
1071
|
+
EcCompetency.template?.["schema:dateCreated"] != null
|
|
1068
1072
|
) {
|
|
1069
1073
|
CTDLASNCSVImport.setDateCreated(e, f);
|
|
1070
1074
|
}
|
|
@@ -1095,7 +1099,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
1095
1099
|
for (let i = val.length-1; i >=0; i--) {
|
|
1096
1100
|
if (!val[i].startsWith('http')) {
|
|
1097
1101
|
if (val[i] === "Deprecated" || val[i] === "Published" || val[i] === "Draft" ) {
|
|
1098
|
-
val[i] = "http://credreg.net/ctdlasn/vocabs/publicationStatus/" + val[i];
|
|
1102
|
+
val[i] = "http://credreg.net/ctdlasn/vocabs/publicationStatus/" + val[i]; // NOSONAR - JSON-LD identifier, never fetched
|
|
1099
1103
|
} else {
|
|
1100
1104
|
val.splice(i, 1);
|
|
1101
1105
|
}
|
|
@@ -1215,13 +1219,11 @@ module.exports = class CTDLASNCSVImport {
|
|
|
1215
1219
|
pretranslatedE["@type"] == ""
|
|
1216
1220
|
) {
|
|
1217
1221
|
errors.push(`Row ${i+2}: Missing or empty @type`);
|
|
1218
|
-
rowsWithErrors.add(i);
|
|
1219
1222
|
continue;
|
|
1220
1223
|
} else {
|
|
1221
1224
|
errors.push(
|
|
1222
1225
|
`Row ${i+2}: Found unknown type:` + pretranslatedE["@type"]
|
|
1223
1226
|
);
|
|
1224
|
-
rowsWithErrors.add(i);
|
|
1225
1227
|
continue;
|
|
1226
1228
|
}
|
|
1227
1229
|
}
|
|
@@ -1250,16 +1252,14 @@ module.exports = class CTDLASNCSVImport {
|
|
|
1250
1252
|
} else if (type == "Concept") {
|
|
1251
1253
|
type = "ConceptScheme";
|
|
1252
1254
|
}
|
|
1253
|
-
} else {
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
type = "Concept";
|
|
1258
|
-
}
|
|
1255
|
+
} else if (type == "Framework") {
|
|
1256
|
+
type = "Competency";
|
|
1257
|
+
} else if (type == "ConceptScheme") {
|
|
1258
|
+
type = "Concept";
|
|
1259
1259
|
}
|
|
1260
1260
|
}
|
|
1261
1261
|
if (endpoint != null) {
|
|
1262
|
-
if (endpoint.
|
|
1262
|
+
if (endpoint.includes("ce-")) {
|
|
1263
1263
|
ctid = ctid.substring(3);
|
|
1264
1264
|
}
|
|
1265
1265
|
return endpoint + ctid;
|
|
@@ -1298,7 +1298,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
1298
1298
|
if (typeof thisKey == "string") {
|
|
1299
1299
|
if (translator[key].trim().length == 0) {
|
|
1300
1300
|
translator[key] = null;
|
|
1301
|
-
} else if (thisKey.
|
|
1301
|
+
} else if (thisKey.includes("|")) {
|
|
1302
1302
|
thisKey = thisKey.split("|");
|
|
1303
1303
|
translator[key] = thisKey;
|
|
1304
1304
|
for (let i = 0; i < thisKey.length; i++) {
|
|
@@ -1378,7 +1378,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
1378
1378
|
collectionFlag
|
|
1379
1379
|
) {
|
|
1380
1380
|
if (!EcArray.isArray(e[field])) {
|
|
1381
|
-
let makeArray = Array(e[field]);
|
|
1381
|
+
let makeArray = new Array(e[field]);
|
|
1382
1382
|
e[field] = makeArray;
|
|
1383
1383
|
}
|
|
1384
1384
|
for (let i = 0; i < e[field].length; i++) {
|
|
@@ -1522,25 +1522,9 @@ module.exports = class CTDLASNCSVImport {
|
|
|
1522
1522
|
const allowedOrigins = urlRules.allowedOrigins;
|
|
1523
1523
|
if (!fields || !allowedOrigins || allowedOrigins.length === 0) return null;
|
|
1524
1524
|
|
|
1525
|
-
const ctidPattern = /^ce-[0-9a-fA-F-]+$/;
|
|
1526
1525
|
const errors = [];
|
|
1527
1526
|
for (const field of fields) {
|
|
1528
|
-
|
|
1529
|
-
if (raw == null) continue;
|
|
1530
|
-
const values = Array.isArray(raw) ? raw : String(raw).split("|");
|
|
1531
|
-
for (let value of values) {
|
|
1532
|
-
value = String(value).trim();
|
|
1533
|
-
if (value === "" || ctidPattern.test(value)) continue;
|
|
1534
|
-
let origin = null;
|
|
1535
|
-
try {
|
|
1536
|
-
origin = new URL(value).origin;
|
|
1537
|
-
} catch (e) {
|
|
1538
|
-
// Not parseable as a URL; falls through to the error below.
|
|
1539
|
-
}
|
|
1540
|
-
if (origin == null || !allowedOrigins.includes(origin)) {
|
|
1541
|
-
errors.push(`Row ${rowIndex}: ${field} must be a CTID or a credential registry URL for this environment (got: ${value})`);
|
|
1542
|
-
}
|
|
1543
|
-
}
|
|
1527
|
+
errors.push(...registryUrlFieldErrors(obj[field], field, rowIndex, allowedOrigins));
|
|
1544
1528
|
}
|
|
1545
1529
|
return errors.length > 0 ? errors : null;
|
|
1546
1530
|
}
|
|
@@ -1553,40 +1537,11 @@ module.exports = class CTDLASNCSVImport {
|
|
|
1553
1537
|
|
|
1554
1538
|
// Check each type defined in hierarchy rules
|
|
1555
1539
|
for (const [type, rules] of Object.entries(hierarchyRules)) {
|
|
1556
|
-
const hasType = tabularData.some(r => r
|
|
1540
|
+
const hasType = tabularData.some(r => r?.["@type"] === type);
|
|
1557
1541
|
if (!hasType) continue;
|
|
1558
1542
|
|
|
1559
|
-
let hasRequiredProp = false;
|
|
1560
|
-
let hasChildProp = false;
|
|
1561
|
-
|
|
1562
|
-
for (const row of tabularData) {
|
|
1563
|
-
if (!row || !row["@type"]) continue;
|
|
1564
|
-
|
|
1565
|
-
// Check if this row has any of the required properties
|
|
1566
|
-
if (row["@type"] === type && rules.requiredProperties) {
|
|
1567
|
-
for (const prop of rules.requiredProperties) {
|
|
1568
|
-
if (row[prop]) {
|
|
1569
|
-
hasRequiredProp = true;
|
|
1570
|
-
break;
|
|
1571
|
-
}
|
|
1572
|
-
}
|
|
1573
|
-
}
|
|
1574
|
-
|
|
1575
|
-
// Check if any row has child properties
|
|
1576
|
-
if (rules.childProperties) {
|
|
1577
|
-
for (const prop of rules.childProperties) {
|
|
1578
|
-
if (row[prop]) {
|
|
1579
|
-
hasChildProp = true;
|
|
1580
|
-
break;
|
|
1581
|
-
}
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
|
-
if (hasRequiredProp && hasChildProp) break;
|
|
1586
|
-
}
|
|
1587
|
-
|
|
1588
1543
|
// Validate that at least one of the hierarchy properties exists
|
|
1589
|
-
if (!
|
|
1544
|
+
if (!hasHierarchyProperty(tabularData, type, rules)) {
|
|
1590
1545
|
return rules.errorMessage || `CSV must contain hierarchy properties for ${type}`;
|
|
1591
1546
|
}
|
|
1592
1547
|
}
|
|
@@ -1602,7 +1557,7 @@ module.exports = class CTDLASNCSVImport {
|
|
|
1602
1557
|
let timestamp = object.getTimestamp();
|
|
1603
1558
|
let date;
|
|
1604
1559
|
if (timestamp != null) {
|
|
1605
|
-
date = new Date(parseInt(timestamp)).toISOString();
|
|
1560
|
+
date = new Date(Number.parseInt(timestamp)).toISOString();
|
|
1606
1561
|
} else {
|
|
1607
1562
|
date = new Date().toISOString();
|
|
1608
1563
|
}
|
|
@@ -67,17 +67,13 @@ module.exports = class FrameworkImport {
|
|
|
67
67
|
FrameworkImport.compMap = {};
|
|
68
68
|
FrameworkImport.savedComp = 0;
|
|
69
69
|
FrameworkImport.savedRel = 0;
|
|
70
|
-
for (let
|
|
71
|
-
let id = source.competency[i];
|
|
70
|
+
for (let sourceCompetencyId of source.competency) {
|
|
72
71
|
EcCompetency.get(
|
|
73
|
-
|
|
72
|
+
sourceCompetencyId,
|
|
74
73
|
function (comp) {
|
|
75
74
|
let competency = new EcCompetency();
|
|
76
75
|
competency.copyFrom(comp);
|
|
77
|
-
if (
|
|
78
|
-
repo == null ||
|
|
79
|
-
repo.selectedServer.indexOf(serverUrl) != -1
|
|
80
|
-
)
|
|
76
|
+
if (repo?.selectedServer.includes(serverUrl) ?? true)
|
|
81
77
|
competency.generateId(serverUrl);
|
|
82
78
|
else competency.generateShortId(serverUrl);
|
|
83
79
|
FrameworkImport.compMap[
|
|
@@ -100,25 +96,15 @@ module.exports = class FrameworkImport {
|
|
|
100
96
|
) {
|
|
101
97
|
FrameworkImport.targetUsable.save(
|
|
102
98
|
function (p1) {
|
|
103
|
-
for (
|
|
104
|
-
let i = 0;
|
|
105
|
-
i < source.relation.length;
|
|
106
|
-
i++
|
|
107
|
-
) {
|
|
108
|
-
let id = source.relation[i];
|
|
99
|
+
for (let sourceRelationId of source.relation) {
|
|
109
100
|
EcAlignment.get(
|
|
110
|
-
|
|
101
|
+
sourceRelationId,
|
|
111
102
|
function (rel) {
|
|
112
103
|
let relation = new EcAlignment();
|
|
113
104
|
relation.copyFrom(
|
|
114
105
|
rel
|
|
115
106
|
);
|
|
116
|
-
if (
|
|
117
|
-
repo == null ||
|
|
118
|
-
repo.selectedServer.indexOf(
|
|
119
|
-
serverUrl
|
|
120
|
-
) != -1
|
|
121
|
-
)
|
|
107
|
+
if (repo?.selectedServer.includes(serverUrl) ?? true)
|
|
122
108
|
relation.generateId(
|
|
123
109
|
serverUrl
|
|
124
110
|
);
|
|
@@ -223,18 +209,18 @@ module.exports = class FrameworkImport {
|
|
|
223
209
|
);
|
|
224
210
|
}
|
|
225
211
|
} else {
|
|
226
|
-
for (let
|
|
212
|
+
for (let sourceCompetencyId of source.competency) {
|
|
227
213
|
if (
|
|
228
214
|
target.competency == null ||
|
|
229
|
-
target.competency.
|
|
230
|
-
target.competency.
|
|
215
|
+
!target.competency.includes(sourceCompetencyId) &&
|
|
216
|
+
!target.competency.includes(
|
|
231
217
|
EcRemoteLinkedData.trimVersionFromUrl(
|
|
232
|
-
|
|
218
|
+
sourceCompetencyId
|
|
233
219
|
)
|
|
234
|
-
)
|
|
220
|
+
)
|
|
235
221
|
) {
|
|
236
222
|
EcCompetency.get(
|
|
237
|
-
|
|
223
|
+
sourceCompetencyId,
|
|
238
224
|
function (comp) {
|
|
239
225
|
FrameworkImport.competencies.push(comp);
|
|
240
226
|
FrameworkImport.targetUsable.addCompetency(comp.id);
|
|
@@ -254,14 +240,14 @@ module.exports = class FrameworkImport {
|
|
|
254
240
|
) {
|
|
255
241
|
if (
|
|
256
242
|
target.relation == null ||
|
|
257
|
-
target.relation.
|
|
243
|
+
!target.relation.includes(
|
|
258
244
|
source.relation[i]
|
|
259
|
-
)
|
|
260
|
-
target.relation.
|
|
245
|
+
) &&
|
|
246
|
+
!target.relation.includes(
|
|
261
247
|
EcRemoteLinkedData.trimVersionFromUrl(
|
|
262
248
|
source.competency[i]
|
|
263
249
|
)
|
|
264
|
-
)
|
|
250
|
+
)
|
|
265
251
|
) {
|
|
266
252
|
EcAlignment.get(
|
|
267
253
|
source.relation[i],
|
|
@@ -49,7 +49,7 @@ module.exports = class MedbiqImport extends Importer {
|
|
|
49
49
|
}
|
|
50
50
|
} else {
|
|
51
51
|
let newCompetency = new EcCompetency();
|
|
52
|
-
if (obj["lom"]
|
|
52
|
+
if (obj["lom"]?.["general"] != null) {
|
|
53
53
|
newCompetency.name = obj["lom"]["general"]["title"][
|
|
54
54
|
"string"
|
|
55
55
|
].toString();
|
|
@@ -135,9 +135,8 @@ module.exports = class MedbiqImport extends Importer {
|
|
|
135
135
|
) {
|
|
136
136
|
MedbiqImport.progressObject = null;
|
|
137
137
|
MedbiqImport.saved = 0;
|
|
138
|
-
for (let
|
|
139
|
-
|
|
140
|
-
if (repo == null || repo.selectedServer.indexOf(serverUrl) != -1)
|
|
138
|
+
for (let comp of MedbiqImport.medbiqXmlCompetencies) {
|
|
139
|
+
if (repo?.selectedServer.includes(serverUrl) ?? true)
|
|
141
140
|
comp.generateId(serverUrl);
|
|
142
141
|
else comp.generateShortId(serverUrl);
|
|
143
142
|
if (owner != null) comp.addOwner(owner.ppk.toPk());
|