fhirsmith 0.4.2 → 0.5.0
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/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/library/cron-utilities.js +136 -0
- package/library/html-server.js +13 -29
- package/library/html.js +3 -8
- package/library/languages.js +160 -37
- package/library/package-manager.js +48 -1
- package/library/utilities.js +100 -19
- package/package.json +2 -2
- package/packages/package-crawler.js +6 -1
- package/packages/packages.js +38 -54
- package/publisher/publisher.js +19 -27
- package/registry/api.js +11 -10
- package/registry/crawler.js +31 -29
- package/registry/model.js +5 -26
- package/registry/registry.js +32 -41
- package/server.js +53 -5
- package/shl/shl.js +0 -18
- package/static/assets/js/statuspage.js +1 -9
- package/stats.js +39 -1
- package/token/token.js +14 -9
- package/translations/Messages.properties +2 -1
- package/tx/README.md +17 -6
- package/tx/cs/cs-api.js +19 -1
- package/tx/cs/cs-base.js +77 -0
- package/tx/cs/cs-country.js +46 -0
- package/tx/cs/cs-cpt.js +9 -5
- package/tx/cs/cs-cs.js +27 -13
- package/tx/cs/cs-lang.js +60 -22
- package/tx/cs/cs-loinc.js +69 -98
- package/tx/cs/cs-mimetypes.js +4 -0
- package/tx/cs/cs-ndc.js +6 -0
- package/tx/cs/cs-omop.js +16 -15
- package/tx/cs/cs-rxnorm.js +23 -1
- package/tx/cs/cs-snomed.js +283 -40
- package/tx/cs/cs-ucum.js +90 -70
- package/tx/importers/import-sct.module.js +371 -35
- package/tx/importers/readme.md +117 -7
- package/tx/library/bundle.js +5 -0
- package/tx/library/capabilitystatement.js +3 -142
- package/tx/library/codesystem.js +19 -173
- package/tx/library/conceptmap.js +4 -218
- package/tx/library/designations.js +14 -1
- package/tx/library/extensions.js +7 -0
- package/tx/library/namingsystem.js +3 -89
- package/tx/library/operation-outcome.js +8 -3
- package/tx/library/parameters.js +3 -2
- package/tx/library/renderer.js +10 -6
- package/tx/library/terminologycapabilities.js +3 -243
- package/tx/library/valueset.js +3 -235
- package/tx/library.js +100 -13
- package/tx/operation-context.js +23 -4
- package/tx/params.js +35 -38
- package/tx/provider.js +6 -5
- package/tx/sct/expressions.js +12 -3
- package/tx/tx-html.js +80 -89
- package/tx/tx.fhir.org.yml +6 -5
- package/tx/tx.js +163 -13
- package/tx/vs/vs-database.js +56 -39
- package/tx/vs/vs-package.js +21 -2
- package/tx/vs/vs-vsac.js +175 -39
- package/tx/workers/batch-validate.js +2 -0
- package/tx/workers/batch.js +2 -0
- package/tx/workers/expand.js +132 -112
- package/tx/workers/lookup.js +33 -14
- package/tx/workers/metadata.js +2 -2
- package/tx/workers/read.js +3 -2
- package/tx/workers/related.js +574 -0
- package/tx/workers/search.js +46 -9
- package/tx/workers/subsumes.js +13 -3
- package/tx/workers/translate.js +7 -3
- package/tx/workers/validate.js +258 -285
- package/tx/workers/worker.js +43 -39
- package/tx/xml/bundle-xml.js +237 -0
- package/tx/xml/xml-base.js +215 -64
- package/tx/xversion/xv-bundle.js +71 -0
- package/tx/xversion/xv-capabiliityStatement.js +137 -0
- package/tx/xversion/xv-codesystem.js +169 -0
- package/tx/xversion/xv-conceptmap.js +224 -0
- package/tx/xversion/xv-namingsystem.js +88 -0
- package/tx/xversion/xv-operationoutcome.js +27 -0
- package/tx/xversion/xv-parameters.js +87 -0
- package/tx/xversion/xv-resource.js +45 -0
- package/tx/xversion/xv-terminologyCapabilities.js +214 -0
- package/tx/xversion/xv-valueset.js +234 -0
- package/utilities/dev-proxy-server.js +126 -0
- package/utilities/explode-results.js +58 -0
- package/utilities/split-by-system.js +198 -0
- package/utilities/vsac-cs-fetcher.js +0 -0
- package/{windows-install.js → utilities/windows-install.js} +2 -0
- package/vcl/vcl.js +0 -18
- package/xig/xig.js +108 -99
package/tx/workers/validate.js
CHANGED
|
@@ -43,7 +43,6 @@ class ValueSetChecker {
|
|
|
43
43
|
worker;
|
|
44
44
|
valueSet;
|
|
45
45
|
params;
|
|
46
|
-
requiredSupplements = [];
|
|
47
46
|
others = new Map();
|
|
48
47
|
|
|
49
48
|
constructor(worker, valueSet, params) {
|
|
@@ -74,20 +73,20 @@ class ValueSetChecker {
|
|
|
74
73
|
checkCanonicalStatusFull(path, op, rtype, vurl, pid, status, standardsStatus, experimental, source) {
|
|
75
74
|
if (op !== null) {
|
|
76
75
|
if (standardsStatus === 'deprecated') {
|
|
77
|
-
op.
|
|
76
|
+
op.addIssueIfNew(new Issue('information', 'business-rule', '', 'MSG_DEPRECATED', this.worker.i18n.translate('MSG_DEPRECATED', this.params.HTTPLanguages, [vurl, '', rtype]), 'status-check'), false);
|
|
78
77
|
} else if (standardsStatus === 'withdrawn') {
|
|
79
|
-
op.
|
|
78
|
+
op.addIssueIfNew(new Issue('information', 'business-rule', '', 'MSG_WITHDRAWN', this.worker.i18n.translate('MSG_WITHDRAWN', this.params.HTTPLanguages, [vurl, '', rtype]), 'status-check'), false);
|
|
80
79
|
} else if (status === 'retired') {
|
|
81
|
-
op.
|
|
80
|
+
op.addIssueIfNew(new Issue('information', 'business-rule', '', 'MSG_RETIRED', this.worker.i18n.translate('MSG_RETIRED', this.params.HTTPLanguages, [vurl, '', rtype]), 'status-check'), false);
|
|
82
81
|
} else if (source !== null) {
|
|
83
82
|
if (experimental && !source.experimental) {
|
|
84
|
-
op.
|
|
83
|
+
op.addIssueIfNew(new Issue('information', 'business-rule', '', 'MSG_EXPERIMENTAL', this.worker.i18n.translate('MSG_EXPERIMENTAL', this.params.HTTPLanguages, [vurl, '', rtype]), 'status-check'), false);
|
|
85
84
|
} else if ((status === 'draft' || standardsStatus === 'draft') &&
|
|
86
85
|
!((source.status === 'draft') || (Extensions.readString(source, 'http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status') === 'draft'))) {
|
|
87
86
|
if (pid) {
|
|
88
|
-
op.
|
|
87
|
+
op.addIssueIfNew(new Issue('information', 'business-rule', '', 'MSG_DRAFT', this.worker.i18n.translate('MSG_DRAFT_SRC', this.params.HTTPLanguages, [vurl, pid, rtype]), 'status-check'), false);
|
|
89
88
|
} else {
|
|
90
|
-
op.
|
|
89
|
+
op.addIssueIfNew(new Issue('information', 'business-rule', '', 'MSG_DRAFT', this.worker.i18n.translate('MSG_DRAFT', this.params.HTTPLanguages, [vurl, '', rtype]), 'status-check'), false);
|
|
91
90
|
}
|
|
92
91
|
}
|
|
93
92
|
}
|
|
@@ -121,9 +120,10 @@ class ValueSetChecker {
|
|
|
121
120
|
}
|
|
122
121
|
}
|
|
123
122
|
}
|
|
124
|
-
} catch (
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
} catch (error) {
|
|
124
|
+
this.log.error(error);
|
|
125
|
+
this.debugLog(error);
|
|
126
|
+
throw new Error('Exception expanding value set in order to infer system: ' + error.message);
|
|
127
127
|
}
|
|
128
128
|
return result;
|
|
129
129
|
}
|
|
@@ -151,6 +151,7 @@ class ValueSetChecker {
|
|
|
151
151
|
async determineSystem(opContext, code, systems, op) {
|
|
152
152
|
let result = '';
|
|
153
153
|
let needDoExpansion = false;
|
|
154
|
+
let mayNeedExpansion = false;
|
|
154
155
|
|
|
155
156
|
for (let vsi of this.valueSet.jsonObj.compose.exclude || []) {
|
|
156
157
|
if (vsi.valueSet || !vsi.system || vsi.filter) {
|
|
@@ -158,9 +159,12 @@ class ValueSetChecker {
|
|
|
158
159
|
}
|
|
159
160
|
}
|
|
160
161
|
for (let vsi of this.valueSet.jsonObj.compose.include || []) {
|
|
161
|
-
if (vsi.valueSet || !vsi.system
|
|
162
|
+
if (vsi.valueSet || !vsi.system) {
|
|
162
163
|
needDoExpansion = true;
|
|
163
164
|
}
|
|
165
|
+
if (vsi.filter) {
|
|
166
|
+
mayNeedExpansion = true;
|
|
167
|
+
}
|
|
164
168
|
}
|
|
165
169
|
|
|
166
170
|
if (needDoExpansion) {
|
|
@@ -168,7 +172,7 @@ class ValueSetChecker {
|
|
|
168
172
|
} else {
|
|
169
173
|
for (let vsi of this.valueSet.jsonObj.compose.include) {
|
|
170
174
|
this.worker.deadCheck('determineSystem');
|
|
171
|
-
let cs = await this.worker.findCodeSystem(vsi.system, '', null, ['complete', 'fragment'], op,true);
|
|
175
|
+
let cs = await this.worker.findCodeSystem(vsi.system, '', null, ['complete', 'fragment'], op,true, false, false, this.worker.requiredSupplements);
|
|
172
176
|
if (cs === null) {
|
|
173
177
|
return '';
|
|
174
178
|
}
|
|
@@ -192,7 +196,11 @@ class ValueSetChecker {
|
|
|
192
196
|
if (!result) {
|
|
193
197
|
result = vsi.system;
|
|
194
198
|
} else if (result !== vsi.system) {
|
|
195
|
-
|
|
199
|
+
if (mayNeedExpansion) {
|
|
200
|
+
result = await this.determineSystemFromExpansion(code, systems);
|
|
201
|
+
} else {
|
|
202
|
+
return '';
|
|
203
|
+
}
|
|
196
204
|
}
|
|
197
205
|
}
|
|
198
206
|
}
|
|
@@ -212,7 +220,7 @@ class ValueSetChecker {
|
|
|
212
220
|
|
|
213
221
|
|
|
214
222
|
let result;
|
|
215
|
-
let csa = await this.worker.findCodeSystem(system, '', this.params, "*", op,true, false, true);
|
|
223
|
+
let csa = await this.worker.findCodeSystem(system, '', this.params, "*", op,true, false, true, this.worker.requiredSupplements);
|
|
216
224
|
|
|
217
225
|
result = this.worker.determineVersionBase(system, versionVS, this.params);
|
|
218
226
|
|
|
@@ -231,7 +239,7 @@ class ValueSetChecker {
|
|
|
231
239
|
}
|
|
232
240
|
}
|
|
233
241
|
|
|
234
|
-
let cs = await this.worker.findCodeSystem(system, result, this.params, ['complete', 'fragment'], op,true, false, false);
|
|
242
|
+
let cs = await this.worker.findCodeSystem(system, result, this.params, ['complete', 'fragment'], op,true, false, false, this.worker.requiredSupplements);
|
|
235
243
|
if (cs !== null && cs.version() !== versionCoding && !cs.versionIsMoreDetailed(versionCoding, cs.version())) {
|
|
236
244
|
let errLvl = 'error';
|
|
237
245
|
let msg, mid;
|
|
@@ -252,7 +260,7 @@ class ValueSetChecker {
|
|
|
252
260
|
if (errLvl === 'error') {
|
|
253
261
|
messages.push(msg);
|
|
254
262
|
}
|
|
255
|
-
let cs2 = await this.worker.findCodeSystem(system, versionCoding, this.params, ['complete', 'fragment'], op, true, false, true);
|
|
263
|
+
let cs2 = await this.worker.findCodeSystem(system, versionCoding, this.params, ['complete', 'fragment'], op, true, false, true, this.worker.requiredSupplements);
|
|
256
264
|
if (cs2 !== null) {
|
|
257
265
|
cs2 = null;
|
|
258
266
|
} else {
|
|
@@ -289,7 +297,7 @@ class ValueSetChecker {
|
|
|
289
297
|
|
|
290
298
|
async prepare() {
|
|
291
299
|
if (this.valueSet === null) {
|
|
292
|
-
throw new Issue('error', 'not-found', null, null, 'Error Error: vs = nil');
|
|
300
|
+
throw new Issue('error', 'not-found', null, null, 'Error Error: vs = nil', null, 422);
|
|
293
301
|
} else {
|
|
294
302
|
this.seeValueSet();
|
|
295
303
|
this.worker.opContext.addNote(this.valueSet, 'Analysing ' + this.valueSet.vurl + ' for validation purposes', this.indentCount);
|
|
@@ -300,11 +308,7 @@ class ValueSetChecker {
|
|
|
300
308
|
this.worker.opContext.addNote(this.valueSet, 'Version Rules: ' + vrs, this.indentCount);
|
|
301
309
|
}
|
|
302
310
|
}
|
|
303
|
-
this.requiredSupplements
|
|
304
|
-
for (let ext of Extensions.list(this.valueSet.jsonObj, 'http://hl7.org/fhir/StructureDefinition/valueset-supplement')) {
|
|
305
|
-
this.requiredSupplements.push(getValuePrimitive(ext));
|
|
306
|
-
}
|
|
307
|
-
if (this.requiredSupplements.length > 0) {
|
|
311
|
+
if (this.worker.requiredSupplements.size > 0) {
|
|
308
312
|
await this.checkSupplementsExist(this.valueSet);
|
|
309
313
|
}
|
|
310
314
|
|
|
@@ -315,6 +319,7 @@ class ValueSetChecker {
|
|
|
315
319
|
|
|
316
320
|
if (this.valueSet.jsonObj.compose) {
|
|
317
321
|
Extensions.checkNoModifiers(this.valueSet.jsonObj.compose, 'ValueSetChecker.prepare', 'ValueSet.compose');
|
|
322
|
+
this.worker.checkNoLockedDate(this.valueSet.url, this.valueSet.jsonObj.compose)
|
|
318
323
|
let i = 0;
|
|
319
324
|
for (let cc of this.valueSet.jsonObj.compose.include || []) {
|
|
320
325
|
await this.prepareConceptSet('include['+i+']', cc);
|
|
@@ -327,8 +332,10 @@ class ValueSetChecker {
|
|
|
327
332
|
}
|
|
328
333
|
}
|
|
329
334
|
}
|
|
330
|
-
|
|
331
|
-
|
|
335
|
+
|
|
336
|
+
const unused = new Set([...this.worker.requiredSupplements].filter(s => !this.worker.usedSupplements.has(s)));
|
|
337
|
+
if (unused.size > 0) {
|
|
338
|
+
throw new Issue('error', 'not-found', null, 'VALUESET_SUPPLEMENT_MISSING', this.worker.i18n.translatePlural(unused.size, 'VALUESET_SUPPLEMENT_MISSING', this.params.HTTPLanguages, [[...unused].join(',')]), 'not-found').handleAsOO(422);
|
|
332
339
|
}
|
|
333
340
|
}
|
|
334
341
|
|
|
@@ -343,7 +350,7 @@ class ValueSetChecker {
|
|
|
343
350
|
if (!this.others.has(s)) {
|
|
344
351
|
let other = await this.worker.findValueSet(s, '');
|
|
345
352
|
if (other === null) {
|
|
346
|
-
throw new Issue('error', 'not-found', null, 'Unable_to_resolve_value_Set_', this.worker.i18n.translate('Unable_to_resolve_value_Set_', this.params.HTTPLanguages, [s]), 'not-found');
|
|
353
|
+
throw new Issue('error', 'not-found', null, 'Unable_to_resolve_value_Set_', this.worker.i18n.translate('Unable_to_resolve_value_Set_', this.params.HTTPLanguages, [s]), 'not-found', 422);
|
|
347
354
|
}
|
|
348
355
|
let checker = new ValueSetChecker(this.worker, other, this.params);
|
|
349
356
|
checker.indentCount = this.indentCount + 1;
|
|
@@ -353,12 +360,12 @@ class ValueSetChecker {
|
|
|
353
360
|
}
|
|
354
361
|
}
|
|
355
362
|
let v = this.worker.determineVersionBase(cc.system, cc.version, this.params);
|
|
356
|
-
let cs = await this.worker.findCodeSystem(cc.system, v, this.params, ['complete', 'fragment'], null, true, false, false);
|
|
363
|
+
let cs = await this.worker.findCodeSystem(cc.system, v, this.params, ['complete', 'fragment'], null, true, false, false, this.worker.requiredSupplements);
|
|
357
364
|
if (cs !== null) {
|
|
358
365
|
this.worker.opContext.addNote(this.valueSet, 'CodeSystem found: "' + this.worker.renderer.displayCoded(cs) + '"', this.indentCount);
|
|
359
|
-
for (
|
|
360
|
-
if (cs.hasSupplement(
|
|
361
|
-
this.
|
|
366
|
+
for (const s of this.worker.requiredSupplements) {
|
|
367
|
+
if (cs.hasSupplement(s)) {
|
|
368
|
+
this.worker.usedSupplements.add(s);
|
|
362
369
|
}
|
|
363
370
|
}
|
|
364
371
|
let i = 0;
|
|
@@ -415,9 +422,9 @@ class ValueSetChecker {
|
|
|
415
422
|
}
|
|
416
423
|
}
|
|
417
424
|
|
|
418
|
-
async checkSimple(issuePath, system, version, code,
|
|
425
|
+
async checkSimple(issuePath, system, version, code, op) {
|
|
419
426
|
this.worker.opContext.clearContexts();
|
|
420
|
-
if (inferSystem) {
|
|
427
|
+
if (this.params.inferSystem) {
|
|
421
428
|
this.worker.opContext.addNote(this.valueSet, 'Validate "' + code + '" and infer system', this.indentCount);
|
|
422
429
|
} else {
|
|
423
430
|
this.worker.opContext.addNote(this.valueSet, 'Validate "' + this.worker.renderer.displayCoded(system, version, code) + '"', this.indentCount);
|
|
@@ -433,13 +440,14 @@ class ValueSetChecker {
|
|
|
433
440
|
let contentMode = {value: null};
|
|
434
441
|
let impliedSystem = {value: ''};
|
|
435
442
|
let defLang = {value: null};
|
|
436
|
-
return await this.check(issuePath, system, version, code,
|
|
443
|
+
return await this.check(issuePath, system, version, code, null, unknownSystems, ver, inactive, normalForm, vstatus, it, op, null, null, contentMode, impliedSystem, ts, msgs, defLang);
|
|
437
444
|
}
|
|
438
445
|
|
|
439
|
-
async check(path, system, version, code,
|
|
446
|
+
async check(path, system, version, code, displays, unknownSystems, ver, inactive, normalForm, vstatus, cause, op, vcc, params, contentMode, impliedSystem, unkCodes, messages, defLang) {
|
|
440
447
|
defLang.value = new Language('en');
|
|
441
448
|
this.worker.opContext.addNote(this.valueSet, 'Check "' + this.worker.renderer.displayCoded(system, version, code) + '"', this.indentCount);
|
|
442
|
-
|
|
449
|
+
|
|
450
|
+
if (!system && !this.params.inferSystem) {
|
|
443
451
|
let msg = this.worker.i18n.translate('Coding_has_no_system__cannot_validate', this.params.HTTPLanguages, []);
|
|
444
452
|
messages.push(msg);
|
|
445
453
|
op.addIssue(new Issue('warning', 'invalid', path, 'Coding_has_no_system__cannot_validate', msg, 'invalid-data'));
|
|
@@ -455,7 +463,7 @@ class ValueSetChecker {
|
|
|
455
463
|
op.addIssue(new Issue('warning', 'invalid', path, 'Coding_has_no_system__cannot_validate_NO_INFER', msg, 'invalid-data'));
|
|
456
464
|
return false;
|
|
457
465
|
}
|
|
458
|
-
let cs = await this.worker.findCodeSystem(system, version, this.params, ['complete', 'fragment'], op,true);
|
|
466
|
+
let cs = await this.worker.findCodeSystem(system, version, this.params, ['complete', 'fragment'], op,true, false, false, this.worker.requiredSupplements);
|
|
459
467
|
this.seeSourceProvider(cs, system);
|
|
460
468
|
if (cs === null) {
|
|
461
469
|
this.worker.opContext.addNote(this.valueSet, 'Didn\'t find CodeSystem "' + this.worker.renderer.displayCoded(system, version) + '"', this.indentCount);
|
|
@@ -469,7 +477,7 @@ class ValueSetChecker {
|
|
|
469
477
|
op.addIssue(new Issue('error', 'invalid', addToPath(path, 'system'), 'Terminology_TX_System_ValueSet2', msg, 'invalid-data'));
|
|
470
478
|
unknownSystems.add(system);
|
|
471
479
|
} else {
|
|
472
|
-
let css = await this.worker.findCodeSystem(system, version, this.params, ['supplement'], op,true);
|
|
480
|
+
let css = await this.worker.findCodeSystem(system, version, this.params, ['supplement'], op,true, false, false, this.worker.requiredSupplements);
|
|
473
481
|
if (css !== null) {
|
|
474
482
|
vss = null;
|
|
475
483
|
let msg = this.worker.i18n.translate('CODESYSTEM_CS_NO_SUPPLEMENT', this.params.HTTPLanguages, [this.canonical(css.system(), css.version())]);
|
|
@@ -519,7 +527,7 @@ class ValueSetChecker {
|
|
|
519
527
|
result = false;
|
|
520
528
|
cause.value = 'code-invalid';
|
|
521
529
|
this.worker.opContext.addNote(this.valueSet, 'Unknown code', this.indentCount);
|
|
522
|
-
let msg = this.worker.i18n.translate(Unknown_Code_in_VersionSCT(cs.system), this.params.HTTPLanguages, [code, cs.system(), cs.version(), SCTVersion(cs.system(), cs.version())]);
|
|
530
|
+
let msg = this.worker.i18n.translate(Unknown_Code_in_VersionSCT(cs.system, cs.version()), this.params.HTTPLanguages, [code, cs.system(), cs.version(), SCTVersion(cs.system(), cs.version())]);
|
|
523
531
|
messages.push(msg);
|
|
524
532
|
op.addIssue(new Issue('error', 'code-invalid', addToPath(path, 'code'), 'Unknown_Code_in_Version', msg, 'invalid-code'));
|
|
525
533
|
}
|
|
@@ -528,7 +536,7 @@ class ValueSetChecker {
|
|
|
528
536
|
vcc.addCoding(cs.system(), cs.version(), await cs.code(ctxt), cs.display(ctxt, this.params.workingLanguages()));
|
|
529
537
|
}
|
|
530
538
|
cause.value = 'null';
|
|
531
|
-
if (!(abstractOk || !(await cs.
|
|
539
|
+
if (!(this.params.abstractOk || !(await cs.isAbstract(ctxt)))) {
|
|
532
540
|
result = false;
|
|
533
541
|
this.worker.opContext.addNote(this.valueSet, 'Abstract code when not allowed', this.indentCount);
|
|
534
542
|
cause.value = 'business-rule';
|
|
@@ -565,7 +573,7 @@ class ValueSetChecker {
|
|
|
565
573
|
}
|
|
566
574
|
} else if (DEV_IGNORE_VALUESET) {
|
|
567
575
|
// anyhow, we ignore the value set (at least for now)
|
|
568
|
-
let cs = await this.worker.findCodeSystem(system, version, this.params, ['complete', 'fragment'], op, true, true, false);
|
|
576
|
+
let cs = await this.worker.findCodeSystem(system, version, this.params, ['complete', 'fragment'], op, true, true, false, this.worker.requiredSupplements);
|
|
569
577
|
if (cs === null) {
|
|
570
578
|
result = null;
|
|
571
579
|
cause.value = 'not-found';
|
|
@@ -596,11 +604,11 @@ class ValueSetChecker {
|
|
|
596
604
|
defLang.value = cs.defLang();
|
|
597
605
|
this.checkCanonicalStatus(path, op, cs, this.valueSet);
|
|
598
606
|
ver.value = cs.version();
|
|
599
|
-
contentMode.value = cs.contentMode;
|
|
607
|
+
contentMode.value = cs.contentMode();
|
|
600
608
|
let ctxt = cs.locate(code);
|
|
601
609
|
if (ctxt.context === null) {
|
|
602
610
|
unkCodes.push(system + '|' + version + '#' + code);
|
|
603
|
-
if (cs.contentMode !== 'complete') {
|
|
611
|
+
if (cs.contentMode() !== 'complete') {
|
|
604
612
|
result = true;
|
|
605
613
|
cause.value = 'code-invalid';
|
|
606
614
|
this.worker.opContext.addNote(this.valueSet, 'Not found in Incomplete Code System', this.indentCount);
|
|
@@ -611,14 +619,14 @@ class ValueSetChecker {
|
|
|
611
619
|
result = false;
|
|
612
620
|
cause.value = 'code-invalid';
|
|
613
621
|
this.worker.opContext.addNote(this.valueSet, 'Unknown code', this.indentCount);
|
|
614
|
-
let msg = this.worker.i18n.translate(Unknown_Code_in_VersionSCT(system), this.params.HTTPLanguages, [code, system, version, SCTVersion(system, version)]);
|
|
622
|
+
let msg = this.worker.i18n.translate(Unknown_Code_in_VersionSCT(system, version), this.params.HTTPLanguages, [code, system, version, SCTVersion(system, version)]);
|
|
615
623
|
messages.push(msg);
|
|
616
624
|
op.addIssue(new Issue('warning', 'code-invalid', addToPath(path, 'code'), 'Unknown_Code_in_Version', msg, 'invalid-code'));
|
|
617
625
|
}
|
|
618
626
|
} else {
|
|
619
627
|
ctxt = ctxt.context;
|
|
620
628
|
cause.value = 'null';
|
|
621
|
-
if (!(abstractOk || !cs.
|
|
629
|
+
if (!(this.params.abstractOk || !(await cs.isAbstract(ctxt)))) {
|
|
622
630
|
result = false;
|
|
623
631
|
this.worker.opContext.addNote(this.valueSet, 'Abstract code when not allowed', this.indentCount);
|
|
624
632
|
cause.value = 'business-rule';
|
|
@@ -640,7 +648,7 @@ class ValueSetChecker {
|
|
|
640
648
|
}
|
|
641
649
|
}
|
|
642
650
|
} else {
|
|
643
|
-
if (!system && inferSystem) {
|
|
651
|
+
if (!system && this.params.inferSystem) {
|
|
644
652
|
let systems = new Set();
|
|
645
653
|
system = await this.determineSystem(this.worker.opContext, code, systems, op);
|
|
646
654
|
if (!system) {
|
|
@@ -661,8 +669,9 @@ class ValueSetChecker {
|
|
|
661
669
|
}
|
|
662
670
|
}
|
|
663
671
|
|
|
664
|
-
|
|
665
|
-
|
|
672
|
+
const unused = new Set([...this.worker.requiredSupplements].filter(s => !this.worker.usedSupplements.has(s)));
|
|
673
|
+
if (unused.size > 0) {
|
|
674
|
+
throw new Issue('error', 'not-found', null, 'VALUESET_SUPPLEMENT_MISSING', this.worker.i18n.translatePlural(unused.size, 'VALUESET_SUPPLEMENT_MISSING', this.params.HTTPLanguages, [[...unused].join(',')]), 'not-found').handleAsOO(422);
|
|
666
675
|
}
|
|
667
676
|
|
|
668
677
|
if (Extensions.checkNoModifiers(this.valueSet.jsonObj.compose, 'ValueSetChecker.prepare', 'ValueSet.compose')) {
|
|
@@ -673,7 +682,7 @@ class ValueSetChecker {
|
|
|
673
682
|
result = true;
|
|
674
683
|
} else if (cc.system === system || system === '%%null%%') {
|
|
675
684
|
let v = await this.determineVersion(path, cc.system, cc.version, version, op, unknownSystems, messages);
|
|
676
|
-
let cs = await this.worker.findCodeSystem(system, v, this.params, ["complete", "fragment"], op,true, true, false);
|
|
685
|
+
let cs = await this.worker.findCodeSystem(system, v, this.params, ["complete", "fragment"], op,true, true, false, this.worker.requiredSupplements);
|
|
677
686
|
if (cs === null) {
|
|
678
687
|
this.worker.opContext.addNote(this.valueSet, 'CodeSystem not found: ' + this.worker.renderer.displayCoded(cc.system, v), this.indentCount);
|
|
679
688
|
if (!this.params.membershipOnly) {
|
|
@@ -711,11 +720,11 @@ class ValueSetChecker {
|
|
|
711
720
|
this.worker.opContext.addNote(this.valueSet, 'CodeSystem found: ' + this.worker.renderer.displayCoded(cs) + ' for ' + this.worker.renderer.displayCoded(cc.system, v), this.indentCount);
|
|
712
721
|
await this.checkCanonicalStatusCS(path, op, cs, this.valueSet);
|
|
713
722
|
ver.value = cs.version();
|
|
714
|
-
this.worker.checkSupplements(cs, cc, this.requiredSupplements);
|
|
723
|
+
this.worker.checkSupplements(cs, cc, this.worker.requiredSupplements, this.worker.usedSupplements);
|
|
715
724
|
contentMode.value = cs.contentMode();
|
|
716
725
|
|
|
717
726
|
let msg = '';
|
|
718
|
-
if ((system === '%%null%%' || cs.system() === system) && await this.checkConceptSet(path, 'in', cs, cc, code,
|
|
727
|
+
if ((system === '%%null%%' || cs.system() === system) && await this.checkConceptSet(path, 'in', cs, cc, code, displays, this.valueSet, msg, inactive, normalForm, vstatus, op, vcc, messages)) {
|
|
719
728
|
result = true;
|
|
720
729
|
} else {
|
|
721
730
|
result = false;
|
|
@@ -736,7 +745,7 @@ class ValueSetChecker {
|
|
|
736
745
|
}
|
|
737
746
|
this.checkCanonicalStatus(path, op, checker.valueSet, this.valueSet);
|
|
738
747
|
if (result === true) {
|
|
739
|
-
result = await checker.check(path, system, version, code,
|
|
748
|
+
result = await checker.check(path, system, version, code, displays, unknownSystems, ver, inactive, normalForm, vstatus, cause, op, null, params, contentMode, impliedSystem, unkCodes, messages, defLang);
|
|
740
749
|
}
|
|
741
750
|
}
|
|
742
751
|
if (result === true) {
|
|
@@ -747,24 +756,24 @@ class ValueSetChecker {
|
|
|
747
756
|
for (let cc of this.valueSet.jsonObj.compose.exclude || []) {
|
|
748
757
|
this.worker.deadCheck('check#4');
|
|
749
758
|
let excluded;
|
|
750
|
-
if (cc.system) {
|
|
759
|
+
if (!cc.system) {
|
|
751
760
|
excluded = true;
|
|
752
761
|
} else {
|
|
753
|
-
let cs = await this.worker.findCodeSystem(cc.system, cc.version, this.params, ['complete', 'fragment'], op,true, true, false);
|
|
762
|
+
let cs = await this.worker.findCodeSystem(cc.system, cc.version, this.params, ['complete', 'fragment'], op,true, true, false, this.worker.requiredSupplements);
|
|
754
763
|
if (cs === null) {
|
|
755
764
|
throw new Issue('error', 'unknown', null, null, 'No Match for ' + cc.system + '|' + cc.version);
|
|
756
765
|
}
|
|
757
766
|
await this.checkCanonicalStatus(path, op, cs, this.valueSet);
|
|
758
|
-
this.worker.checkSupplements(cs, cc, this.requiredSupplements);
|
|
767
|
+
this.worker.checkSupplements(cs, cc, this.worker.requiredSupplements, this.worker.usedSupplements);
|
|
759
768
|
ver.value = cs.version();
|
|
760
769
|
contentMode.value = cs.contentMode();
|
|
761
770
|
let msg = '';
|
|
762
|
-
excluded = (system === '%%null%%' || cs.system() === system) && await this.checkConceptSet(path, 'not in', cs, cc, code,
|
|
771
|
+
excluded = (system === '%%null%%' || cs.system() === system) && await this.checkConceptSet(path, 'not in', cs, cc, code, displays, this.valueSet, msg, inactive, normalForm, vstatus, op, vcc);
|
|
763
772
|
if (msg) {
|
|
764
773
|
messages.push(msg);
|
|
765
774
|
}
|
|
766
775
|
}
|
|
767
|
-
for (let u of cc.
|
|
776
|
+
for (let u of cc.valueSet || []) {
|
|
768
777
|
this.worker.deadCheck('check#5');
|
|
769
778
|
let s = this.worker.pinValueSet(u);
|
|
770
779
|
let checker = this.others.get(s);
|
|
@@ -772,7 +781,7 @@ class ValueSetChecker {
|
|
|
772
781
|
throw new Issue('error', 'unknown', null, null, 'No Match for ' + cc.system + '|' + cc.version + ' in ' + Array.from(this.others.keys()).join(','));
|
|
773
782
|
}
|
|
774
783
|
this.checkCanonicalStatus(path, op, checker.valueSet, this.valueSet);
|
|
775
|
-
excluded = excluded && (await checker.check(path, system, version, code,
|
|
784
|
+
excluded = excluded && (await checker.check(path, system, version, code, displays, unknownSystems, ver, inactive, normalForm, vstatus, cause, op, null, params, contentMode, impliedSystem, unkCodes, messages, defLang) === true);
|
|
776
785
|
}
|
|
777
786
|
if (excluded) {
|
|
778
787
|
return false;
|
|
@@ -799,7 +808,7 @@ class ValueSetChecker {
|
|
|
799
808
|
op.addIssueNoId('error', 'not-found', addToPath(path, 'version'), msg, 'vs-invalid');
|
|
800
809
|
return false;
|
|
801
810
|
}
|
|
802
|
-
let cs = await this.worker.findCodeSystem(system, v, this.params, ['complete', 'fragment'], op, true, true, false);
|
|
811
|
+
let cs = await this.worker.findCodeSystem(system, v, this.params, ['complete', 'fragment'], op, true, true, false, this.worker.requiredSupplements);
|
|
803
812
|
if (cs === null) {
|
|
804
813
|
if (!this.params.membershipOnly) {
|
|
805
814
|
let bAdd = true;
|
|
@@ -837,7 +846,7 @@ class ValueSetChecker {
|
|
|
837
846
|
ver.value = cs.version();
|
|
838
847
|
contentMode.value = cs.contentMode();
|
|
839
848
|
let msg = '';
|
|
840
|
-
if ((system === '%%null%%' || cs.system() === system) && await this.checkExpansion(path, cs, ccc, code,
|
|
849
|
+
if ((system === '%%null%%' || cs.system() === system) && await this.checkExpansion(path, cs, ccc, code, displays, this.valueSet, msg, inactive, vstatus, op)) {
|
|
841
850
|
result = true;
|
|
842
851
|
} else {
|
|
843
852
|
result = false;
|
|
@@ -854,7 +863,7 @@ class ValueSetChecker {
|
|
|
854
863
|
return result;
|
|
855
864
|
}
|
|
856
865
|
|
|
857
|
-
async checkCoding(issuePath, coding
|
|
866
|
+
async checkCoding(issuePath, coding) {
|
|
858
867
|
let inactive = false;
|
|
859
868
|
let path = issuePath;
|
|
860
869
|
let unknownSystems = new Set();
|
|
@@ -863,7 +872,7 @@ class ValueSetChecker {
|
|
|
863
872
|
let result = new Parameters();
|
|
864
873
|
|
|
865
874
|
this.worker.opContext.clearContexts();
|
|
866
|
-
if (inferSystem) {
|
|
875
|
+
if (this.params.inferSystem) {
|
|
867
876
|
this.worker.opContext.addNote(this.valueSet, 'Validate "' + this.worker.renderer.displayCoded(coding) + '" and infer system', this.indentCount);
|
|
868
877
|
} else {
|
|
869
878
|
this.worker.opContext.addNote(this.valueSet, 'Validate "' + this.worker.renderer.displayCoded(coding) + '"', this.indentCount);
|
|
@@ -881,7 +890,7 @@ class ValueSetChecker {
|
|
|
881
890
|
let impliedSystem = {value: ''};
|
|
882
891
|
let defLang = {value: null};
|
|
883
892
|
|
|
884
|
-
let ok = await this.check(path, coding.system, coding.version, coding.code,
|
|
893
|
+
let ok = await this.check(path, coding.system, coding.version, coding.code, list, unknownSystems, ver, inactive, normalForm, vstatus, cause, op, null, result, contentMode, impliedSystem, unkCodes, messages, defLang);
|
|
885
894
|
if (ok === true) {
|
|
886
895
|
result.AddParamBool('result', true);
|
|
887
896
|
if ((cause.value === 'not-found' && contentMode.value !== 'complete') || contentMode.value === 'example') {
|
|
@@ -962,17 +971,17 @@ class ValueSetChecker {
|
|
|
962
971
|
async checkSupplementsExist(vs) {
|
|
963
972
|
for (let inc of vs.jsonObj.compose.include) {
|
|
964
973
|
if (inc.system) {
|
|
965
|
-
let cs = await this.worker.findCodeSystem(inc.system, inc.version, this.params, ['complete', 'fragment'], null,true);
|
|
974
|
+
let cs = await this.worker.findCodeSystem(inc.system, inc.version, this.params, ['complete', 'fragment'], null,true, false, false, this.worker.requiredSupplements);
|
|
966
975
|
if (cs !== null) {
|
|
967
|
-
await this.worker.checkSupplements(cs, null, this.requiredSupplements);
|
|
976
|
+
await this.worker.checkSupplements(cs, null, this.worker.requiredSupplements, this.worker.usedSupplements);
|
|
968
977
|
}
|
|
969
978
|
}
|
|
970
979
|
}
|
|
971
980
|
}
|
|
972
981
|
|
|
973
|
-
async checkCodeableConcept(issuePath, code,
|
|
982
|
+
async checkCodeableConcept(issuePath, code, mode) {
|
|
974
983
|
this.worker.opContext.clearContexts();
|
|
975
|
-
if (inferSystem) {
|
|
984
|
+
if (this.params.inferSystem) {
|
|
976
985
|
this.worker.opContext.addNote(this.valueSet, 'Validate "' + this.worker.renderer.displayCoded(code) + '" and infer system', this.indentCount);
|
|
977
986
|
} else {
|
|
978
987
|
this.worker.opContext.addNote(this.valueSet, 'Validate "' + this.worker.renderer.displayCoded(code) + '"', this.indentCount);
|
|
@@ -996,7 +1005,7 @@ class ValueSetChecker {
|
|
|
996
1005
|
let tsys = '';
|
|
997
1006
|
let tcode = '';
|
|
998
1007
|
let tver = '';
|
|
999
|
-
let vcc = {};
|
|
1008
|
+
let vcc = {}; // todo: VCC is an appendage, and useless. remove it
|
|
1000
1009
|
if (code.text) {
|
|
1001
1010
|
vcc.text = code.text;
|
|
1002
1011
|
}
|
|
@@ -1023,10 +1032,9 @@ class ValueSetChecker {
|
|
|
1023
1032
|
mt = [];
|
|
1024
1033
|
let i = 0;
|
|
1025
1034
|
let impliedSystem = { value: '' };
|
|
1026
|
-
for (let c of code.coding) {
|
|
1027
|
-
const csd = await this.worker.findCodeSystem(c.system, null, this.params, ['complete', 'fragment'], false, true);
|
|
1035
|
+
for (let c of code.coding || []) {
|
|
1036
|
+
const csd = await this.worker.findCodeSystem(c.system, null, this.params, ['complete', 'fragment'], false, true, false, false, this.worker.requiredSupplements);
|
|
1028
1037
|
this.worker.seeSourceProvider(csd, c.system);
|
|
1029
|
-
|
|
1030
1038
|
this.worker.deadCheck('check-b#1');
|
|
1031
1039
|
let path;
|
|
1032
1040
|
if (issuePath === 'CodeableConcept') {
|
|
@@ -1034,11 +1042,16 @@ class ValueSetChecker {
|
|
|
1034
1042
|
} else {
|
|
1035
1043
|
path = issuePath;
|
|
1036
1044
|
}
|
|
1045
|
+
if (!c.code) {
|
|
1046
|
+
let msg = `Coding has no code for system ${c.system} and cannot be validated`;
|
|
1047
|
+
op.addIssue(new Issue('error', 'invalid', path, null, msg, 'invalid-data'));
|
|
1048
|
+
break;
|
|
1049
|
+
}
|
|
1037
1050
|
list.clear();
|
|
1038
1051
|
let ver = { value: '' };
|
|
1039
1052
|
let contentMode = { value: null };
|
|
1040
1053
|
let defLang = { value: null };
|
|
1041
|
-
let v = await this.check(path, c.system, c.version, c.code,
|
|
1054
|
+
let v = await this.check(path, c.system, c.version, c.code, list, unknownSystems, ver, inactive, normalForm, vstatus, cause, op, vcc, result, contentMode, impliedSystem, ts, mt, defLang);
|
|
1042
1055
|
if (v === false) {
|
|
1043
1056
|
cause.value = 'code-invalid';
|
|
1044
1057
|
}
|
|
@@ -1053,6 +1066,9 @@ class ValueSetChecker {
|
|
|
1053
1066
|
tcode = c.code;
|
|
1054
1067
|
tver = c.version;
|
|
1055
1068
|
}
|
|
1069
|
+
if (!tver && ver.value) {
|
|
1070
|
+
tver = ver.value;
|
|
1071
|
+
}
|
|
1056
1072
|
let cc;
|
|
1057
1073
|
if (!ws) {
|
|
1058
1074
|
ws = "";
|
|
@@ -1113,7 +1129,7 @@ class ValueSetChecker {
|
|
|
1113
1129
|
mt.push(m);
|
|
1114
1130
|
op.addIssue(new Issue('error', 'invalid', p, 'Terminology_TX_System_Relative', m, 'invalid-data'));
|
|
1115
1131
|
}
|
|
1116
|
-
let prov = await this.worker.findCodeSystem(ws, c.version, this.params, ['complete', 'fragment'], op,true, true, false);
|
|
1132
|
+
let prov = await this.worker.findCodeSystem(ws, c.version, this.params, ['complete', 'fragment'], op,true, true, false, this.worker.requiredSupplements);
|
|
1117
1133
|
if (prov === null) {
|
|
1118
1134
|
let vss = await this.worker.findValueSet(ws, '');
|
|
1119
1135
|
if (vss !== null) {
|
|
@@ -1123,7 +1139,7 @@ class ValueSetChecker {
|
|
|
1123
1139
|
op.addIssue(new Issue('error', 'invalid', addToPath(path, 'system'), 'Terminology_TX_System_ValueSet2', m, 'invalid-data'));
|
|
1124
1140
|
cause.value = 'invalid';
|
|
1125
1141
|
} else {
|
|
1126
|
-
let provS = await this.worker.findCodeSystem(ws, c.version, this.params, ['supplement'], op,true, true, false);
|
|
1142
|
+
let provS = await this.worker.findCodeSystem(ws, c.version, this.params, ['supplement'], op,true, true, false, this.worker.requiredSupplements);
|
|
1127
1143
|
if (provS !== null) {
|
|
1128
1144
|
vss = null;
|
|
1129
1145
|
let m = this.worker.i18n.translate('CODESYSTEM_CS_NO_SUPPLEMENT', this.params.HTTPLanguages, [provS.vurl()]);
|
|
@@ -1131,7 +1147,7 @@ class ValueSetChecker {
|
|
|
1131
1147
|
op.addIssue(new Issue('error', 'invalid', addToPath(path, 'system'), 'CODESYSTEM_CS_NO_SUPPLEMENT', m, 'invalid-data'));
|
|
1132
1148
|
cause.value = 'invalid';
|
|
1133
1149
|
} else {
|
|
1134
|
-
let prov2 = await this.worker.findCodeSystem(ws, '', this.params, ['complete', 'fragment'], op,true, true, false);
|
|
1150
|
+
let prov2 = await this.worker.findCodeSystem(ws, '', this.params, ['complete', 'fragment'], op,true, true, false, this.worker.requiredSupplements);
|
|
1135
1151
|
let bAdd = true;
|
|
1136
1152
|
let m, mid, vn;
|
|
1137
1153
|
if (prov2 === null && !c.version) {
|
|
@@ -1184,7 +1200,7 @@ class ValueSetChecker {
|
|
|
1184
1200
|
ts.push(vs);
|
|
1185
1201
|
let m;
|
|
1186
1202
|
if (prov.contentMode() === 'complete') {
|
|
1187
|
-
m = this.worker.i18n.translate(Unknown_Code_in_VersionSCT(ws), this.params.HTTPLanguages, [c.code, ws, prov.version(), SCTVersion(ws, prov.version())]);
|
|
1203
|
+
m = this.worker.i18n.translate(Unknown_Code_in_VersionSCT(ws, prov.version()), this.params.HTTPLanguages, [c.code, ws, prov.version(), SCTVersion(ws, prov.version())]);
|
|
1188
1204
|
cause.value = 'code-invalid';
|
|
1189
1205
|
msg(m);
|
|
1190
1206
|
op.addIssue(new Issue('error', 'code-invalid', addToPath(path, 'code'), 'Unknown_Code_in_Version', m, 'invalid-code'), true);
|
|
@@ -1196,6 +1212,9 @@ class ValueSetChecker {
|
|
|
1196
1212
|
}
|
|
1197
1213
|
}
|
|
1198
1214
|
} else {
|
|
1215
|
+
if (!c.version && mode == 'codeableConcept' && prov.version()) {
|
|
1216
|
+
c.version = prov.version();
|
|
1217
|
+
}
|
|
1199
1218
|
await this.worker.listDisplaysFromCodeSystem(list, prov, ctxt.context);
|
|
1200
1219
|
let pd = list.preferredDisplay(this.params.workingLanguages());
|
|
1201
1220
|
if (pd) {
|
|
@@ -1230,7 +1249,7 @@ class ValueSetChecker {
|
|
|
1230
1249
|
msg(m);
|
|
1231
1250
|
op.addIssue(new Issue(severity, 'invalid', addToPath(path, 'display'), baseMsg, m, 'invalid-display'));
|
|
1232
1251
|
}
|
|
1233
|
-
if (prov.version()) {
|
|
1252
|
+
if (prov.version() && mode != 'codeableConcept') {
|
|
1234
1253
|
result.addParamStr('version', prov.version());
|
|
1235
1254
|
}
|
|
1236
1255
|
}
|
|
@@ -1412,9 +1431,9 @@ class ValueSetChecker {
|
|
|
1412
1431
|
}
|
|
1413
1432
|
}
|
|
1414
1433
|
|
|
1415
|
-
async checkSystemCode(issuePath, system, version, code
|
|
1434
|
+
async checkSystemCode(issuePath, system, version, code) {
|
|
1416
1435
|
this.worker.opContext.clearContexts();
|
|
1417
|
-
if (inferSystem) {
|
|
1436
|
+
if (this.params.inferSystem) {
|
|
1418
1437
|
this.worker.opContext.addNote(this.valueSet, 'Validate "' + code + '" and infer system', this.indentCount);
|
|
1419
1438
|
} else {
|
|
1420
1439
|
this.worker.opContext.addNote(this.valueSet, 'Validate "' + this.worker.renderer.displayCoded(system, version, code) + '"', this.indentCount);
|
|
@@ -1435,7 +1454,7 @@ class ValueSetChecker {
|
|
|
1435
1454
|
let impliedSystem = {value: ''};
|
|
1436
1455
|
let defLang = {value: null};
|
|
1437
1456
|
|
|
1438
|
-
let ok = await this.check(issuePath, system, version, code, true,
|
|
1457
|
+
let ok = await this.check(issuePath, system, version, code, true, list, unknownSystems, ver, inactive, normalForm, vstatus, cause, op, null, result, contentMode, impliedSystem, unkCodes, messages, defLang);
|
|
1439
1458
|
if (ok === true) {
|
|
1440
1459
|
result.AddParamBool('result', true);
|
|
1441
1460
|
let pd = list.preferredDisplay(this.params.workingLanguages());
|
|
@@ -1484,7 +1503,7 @@ class ValueSetChecker {
|
|
|
1484
1503
|
return result;
|
|
1485
1504
|
}
|
|
1486
1505
|
|
|
1487
|
-
async checkConceptSet(path, role, cs, cset, code,
|
|
1506
|
+
async checkConceptSet(path, role, cs, cset, code, displays, vs, message, inactive, normalForm, vstatus, op, vcc, messages) {
|
|
1488
1507
|
this.worker.opContext.addNote(vs, 'check code ' + role + ' ' + this.worker.renderer.displayValueSetInclude(cset) + ' at ' + path, this.indentCount);
|
|
1489
1508
|
inactive.value = false;
|
|
1490
1509
|
let result = false;
|
|
@@ -1498,43 +1517,15 @@ class ValueSetChecker {
|
|
|
1498
1517
|
op.addIssue(new Issue('warning', 'code-invalid', addToPath(path, 'code'), 'UNKNOWN_CODE_IN_FRAGMENT', this.worker.i18n.translate('UNKNOWN_CODE_IN_FRAGMENT', this.params.HTTPLanguages, [code, cs.system(), cs.version()]), 'invalid-code'));
|
|
1499
1518
|
result = true;
|
|
1500
1519
|
} else {
|
|
1501
|
-
op.addIssue(new Issue('error', 'code-invalid', addToPath(path, 'code'), 'Unknown_Code_in_Version',
|
|
1502
|
-
this.worker.i18n.translate(Unknown_Code_in_VersionSCT(cs.system()), this.params.HTTPLanguages, [code, cs.system(), cs.version(), SCTVersion(cs.system(), cs.version())]), 'invalid-code'));
|
|
1520
|
+
op.addIssue(new Issue('error', 'code-invalid', addToPath(path, 'code'), cs.version() ? 'Unknown_Code_in_Version' : 'Unknown_Code_in',
|
|
1521
|
+
this.worker.i18n.translate(Unknown_Code_in_VersionSCT(cs.system(), cs.version()), this.params.HTTPLanguages, [code, cs.system(), cs.version(), SCTVersion(cs.system(), cs.version())]), 'invalid-code'));
|
|
1503
1522
|
}
|
|
1504
1523
|
}
|
|
1505
1524
|
if (loc.message && op) {
|
|
1506
1525
|
op.addIssue(new Issue('information', 'code-invalid', addToPath(path, 'code'), null, loc.message, 'invalid-code'));
|
|
1507
1526
|
}
|
|
1508
|
-
} else if (!(abstractOk || !cs.IsAbstract(loc.context))) {
|
|
1509
|
-
this.worker.opContext.addNote(this.valueSet, 'Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs) + ' but is abstract', this.indentCount);
|
|
1510
|
-
if (!this.params.membershipOnly) {
|
|
1511
|
-
op.addIssue(new Issue('error', 'business-rule', addToPath(path, 'code'), 'ABSTRACT_CODE_NOT_ALLOWED', this.worker.i18n.translate('ABSTRACT_CODE_NOT_ALLOWED', this.params.HTTPLanguages, [cs.system(), code]), 'code-rule'));
|
|
1512
|
-
}
|
|
1513
|
-
} else if (this.excludeInactives() && await cs.isInactive(loc.context)) {
|
|
1514
|
-
this.worker.opContext.addNote(this.valueSet, 'Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs) + ' but is inactive', this.indentCount);
|
|
1515
|
-
let msg = this.worker.i18n.translate('STATUS_CODE_WARNING_CODE', this.params.HTTPLanguages, ['not active', code]);
|
|
1516
|
-
op.addIssue(new Issue('error', 'business-rule', addToPath(path, 'code'), 'STATUS_CODE_WARNING_CODE', msg, 'code-rule'));
|
|
1517
|
-
result = false;
|
|
1518
|
-
messages.push(msg);
|
|
1519
|
-
if (!this.params.membershipOnly) {
|
|
1520
|
-
inactive.value = true;
|
|
1521
|
-
inactive.path = path;
|
|
1522
|
-
if (inactive.value) {
|
|
1523
|
-
vstatus.value = await cs.getStatus(loc.context);
|
|
1524
|
-
}
|
|
1525
|
-
}
|
|
1526
|
-
} else if (this.params.activeOnly && await cs.isInactive(loc.context)) {
|
|
1527
|
-
this.worker.opContext.addNote(this.valueSet, 'Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs) + ' but is inactive', this.indentCount);
|
|
1528
|
-
result = false;
|
|
1529
|
-
inactive.value = true;
|
|
1530
|
-
inactive.path = path;
|
|
1531
|
-
vstatus.value = await cs.getStatus(loc.context);
|
|
1532
|
-
let msg = this.worker.i18n.translate('STATUS_CODE_WARNING_CODE', this.params.HTTPLanguages, ['not active', code]);
|
|
1533
|
-
messages.push(msg);
|
|
1534
|
-
op.addIssue(new Issue('error', 'business-rule', addToPath(path, 'code'), 'STATUS_CODE_WARNING_CODE', msg, 'code-rule'));
|
|
1535
1527
|
} else {
|
|
1536
1528
|
this.worker.opContext.addNote(this.valueSet, 'Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs), this.indentCount);
|
|
1537
|
-
result = true;
|
|
1538
1529
|
if (await cs.code(loc.context) != code) {
|
|
1539
1530
|
let msg;
|
|
1540
1531
|
if (cs.version()) {
|
|
@@ -1550,22 +1541,53 @@ class ValueSetChecker {
|
|
|
1550
1541
|
op.addIssue(new Issue('information', 'informational', addToPath(path, 'code'), null, msg, 'process-note'));
|
|
1551
1542
|
}
|
|
1552
1543
|
await this.worker.listDisplaysFromCodeSystem(displays, cs, loc.context);
|
|
1553
|
-
inactive.value = await cs.isInactive(loc.context);
|
|
1554
|
-
inactive.path = path;
|
|
1555
|
-
vstatus.value = await cs.getStatus(loc.context);
|
|
1556
1544
|
|
|
1557
|
-
if (
|
|
1558
|
-
|
|
1559
|
-
|
|
1545
|
+
if (!(this.params.abstractOk || !(await cs.isAbstract(loc.context)))) {
|
|
1546
|
+
this.worker.opContext.addNote(this.valueSet, 'Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs) + ' but is abstract', this.indentCount);
|
|
1547
|
+
if (!this.params.membershipOnly) {
|
|
1548
|
+
op.addIssue(new Issue('error', 'business-rule', addToPath(path, 'code'), 'ABSTRACT_CODE_NOT_ALLOWED', this.worker.i18n.translate('ABSTRACT_CODE_NOT_ALLOWED', this.params.HTTPLanguages, [cs.system(), code]), 'code-rule'));
|
|
1549
|
+
}
|
|
1550
|
+
} else if (this.excludeInactives() && await cs.isInactive(loc.context)) {
|
|
1551
|
+
this.worker.opContext.addNote(this.valueSet, 'Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs) + ' but is inactive', this.indentCount);
|
|
1552
|
+
let msg = this.worker.i18n.translate('STATUS_CODE_WARNING_CODE', this.params.HTTPLanguages, ['not active', code]);
|
|
1553
|
+
op.addIssue(new Issue('error', 'business-rule', addToPath(path, 'code'), 'STATUS_CODE_WARNING_CODE', msg, 'code-rule'));
|
|
1554
|
+
result = false;
|
|
1555
|
+
messages.push(msg);
|
|
1556
|
+
if (!this.params.membershipOnly) {
|
|
1557
|
+
inactive.value = true;
|
|
1558
|
+
inactive.path = path;
|
|
1559
|
+
if (inactive.value) {
|
|
1560
|
+
vstatus.value = await cs.getStatus(loc.context);
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
} else if (this.params.activeOnly && await cs.isInactive(loc.context)) {
|
|
1564
|
+
this.worker.opContext.addNote(this.valueSet, 'Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs) + ' but is inactive', this.indentCount);
|
|
1565
|
+
result = false;
|
|
1566
|
+
inactive.value = true;
|
|
1567
|
+
inactive.path = path;
|
|
1568
|
+
vstatus.value = await cs.getStatus(loc.context);
|
|
1569
|
+
let msg = this.worker.i18n.translate('STATUS_CODE_WARNING_CODE', this.params.HTTPLanguages, ['not active', code]);
|
|
1570
|
+
messages.push(msg);
|
|
1571
|
+
op.addIssue(new Issue('error', 'business-rule', addToPath(path, 'code'), 'STATUS_CODE_WARNING_CODE', msg, 'code-rule'));
|
|
1572
|
+
} else {
|
|
1573
|
+
result = true;
|
|
1574
|
+
inactive.value = await cs.isInactive(loc.context);
|
|
1575
|
+
inactive.path = path;
|
|
1576
|
+
vstatus.value = await cs.getStatus(loc.context);
|
|
1577
|
+
|
|
1578
|
+
if (vcc !== null) {
|
|
1579
|
+
if (!vcc.coding) {
|
|
1580
|
+
vcc.coding = [];
|
|
1581
|
+
}
|
|
1582
|
+
vcc.coding.push({
|
|
1583
|
+
system: cs.system(),
|
|
1584
|
+
version: cs.version(),
|
|
1585
|
+
code: await cs.code(loc.context),
|
|
1586
|
+
display: displays.preferredDisplay(this.params.workingLanguages())
|
|
1587
|
+
});
|
|
1560
1588
|
}
|
|
1561
|
-
|
|
1562
|
-
system: cs.system(),
|
|
1563
|
-
version: cs.version(),
|
|
1564
|
-
code: await cs.code(loc.context),
|
|
1565
|
-
display: displays.preferredDisplay(this.params.workingLanguages())
|
|
1566
|
-
});
|
|
1589
|
+
return result;
|
|
1567
1590
|
}
|
|
1568
|
-
return result;
|
|
1569
1591
|
}
|
|
1570
1592
|
}
|
|
1571
1593
|
|
|
@@ -1579,7 +1601,7 @@ class ValueSetChecker {
|
|
|
1579
1601
|
this.worker.opContext.addNote(this.valueSet, 'Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs), this.indentCount);
|
|
1580
1602
|
await this.worker.listDisplaysFromCodeSystem(displays, cs, loc);
|
|
1581
1603
|
this.worker.listDisplaysFromIncludeConcept(displays, cc, vs);
|
|
1582
|
-
if (!(abstractOk || !cs.
|
|
1604
|
+
if (!(this.params.abstractOk || !(await cs.isAbstract(loc)))) {
|
|
1583
1605
|
if (!this.params.membershipOnly) {
|
|
1584
1606
|
op.addIssue(new Issue('error', 'business-rule', addToPath(path, 'code'), 'ABSTRACT_CODE_NOT_ALLOWED', this.worker.i18n.translate('ABSTRACT_CODE_NOT_ALLOWED', this.params.HTTPLanguages, [cs.system(), code]), 'code-rule'));
|
|
1585
1607
|
}
|
|
@@ -1627,149 +1649,62 @@ class ValueSetChecker {
|
|
|
1627
1649
|
// }
|
|
1628
1650
|
}
|
|
1629
1651
|
let filters = await cs.executeFilters(prep);
|
|
1630
|
-
if (filters) {
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
}
|
|
1640
|
-
} else if (this.excludeInactives() && await cs.isInactive(loc)) {
|
|
1641
|
-
result = false;
|
|
1642
|
-
this.worker.opContext.addNote(this.valueSet, 'Filter ' + ctxt.summary + ': Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs) + ' but is inactive', this.indentCount);
|
|
1643
|
-
if (!this.params.membershipOnly) {
|
|
1644
|
-
inactive.value = true;
|
|
1645
|
-
inactive.path = path;
|
|
1646
|
-
vstatus.value = await cs.getStatus(loc);
|
|
1647
|
-
}
|
|
1648
|
-
} else {
|
|
1649
|
-
this.worker.opContext.addNote(this.valueSet, 'Filter ' + ctxt.summary + ': Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs), this.indentCount);
|
|
1650
|
-
if (vcc !== null) {
|
|
1651
|
-
if (!vcc.coding) { vcc.coding = []}
|
|
1652
|
-
vcc.coding.push( { system : cs.system(), version: cs.version(), code: await cs.code(loc), display: displays.preferredDisplay(this.params.workingLanguages())});
|
|
1653
|
-
}
|
|
1654
|
-
result = true;
|
|
1655
|
-
return result;
|
|
1652
|
+
if (!filters || filters.length == 0) {
|
|
1653
|
+
throw new Error("executing filters failed for " + cs.name());
|
|
1654
|
+
}
|
|
1655
|
+
let loc = await cs.filterLocate(prep, filters[0], code);
|
|
1656
|
+
if (loc != null && !(typeof loc === 'string') && filters.length > 1) {
|
|
1657
|
+
for (let i = 1; i < filters.length; i++) {
|
|
1658
|
+
if (!(await cs.filterCheck(prep, filters[i], loc))) {
|
|
1659
|
+
loc = null;
|
|
1660
|
+
break;
|
|
1656
1661
|
}
|
|
1657
|
-
} else if (loc != null) {
|
|
1658
|
-
this.worker.opContext.addNote(this.valueSet, 'Filter ' + ctxt.summary + ': Code "' + code + '" not found in ' + this.worker.renderer.displayCoded(cs)+ ": "+loc, this.indentCount);
|
|
1659
|
-
messages.push(loc);
|
|
1660
|
-
} else {
|
|
1661
|
-
this.worker.opContext.addNote(this.valueSet, 'Filter ' + ctxt.summary + ': Code "' + code + '" not found in ' + this.worker.renderer.displayCoded(cs), this.indentCount);
|
|
1662
1662
|
}
|
|
1663
|
-
}
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
this.worker.
|
|
1668
|
-
if (
|
|
1669
|
-
|
|
1670
|
-
if (loc !== null) {
|
|
1671
|
-
await this.worker.listDisplaysFromCodeSystem(displays, cs, loc);
|
|
1672
|
-
if (!(abstractOk || !cs.IsAbstract(loc))) {
|
|
1673
|
-
this.worker.opContext.addNote(this.valueSet, 'Filter "' + fc.property + '' + fc.op + '' + fc.value + '": Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs) + ' but is abstract', this.indentCount);
|
|
1674
|
-
if (!this.params.membershipOnly) {
|
|
1675
|
-
op.addIssue(new Issue('error', 'business-rule', addToPath(path, 'code'), 'ABSTRACT_CODE_NOT_ALLOWED', this.worker.i18n.translate('ABSTRACT_CODE_NOT_ALLOWED', this.params.HTTPLanguages, [cs.system(), code]), 'code-rule'));
|
|
1676
|
-
}
|
|
1677
|
-
} else {
|
|
1678
|
-
this.worker.opContext.addNote(this.valueSet, 'Filter "' + fc.property + fc.op + fc.value + '": Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs), this.indentCount);
|
|
1679
|
-
if (vcc !== null) {
|
|
1680
|
-
vcc.addCoding(cs.system(), cs.version(), await cs.code(loc), displays.preferredDisplay(this.params.workingLanguages()));
|
|
1681
|
-
}
|
|
1682
|
-
result = true;
|
|
1683
|
-
return result;
|
|
1684
|
-
}
|
|
1685
|
-
} else {
|
|
1686
|
-
result = false;
|
|
1687
|
-
this.worker.opContext.addNote(this.valueSet, 'Filter "' + fc.property + fc.op + fc.value + '": Code "' + code + '" not found in ' + this.worker.renderer.displayCoded(cs), this.indentCount);
|
|
1688
|
-
}
|
|
1689
|
-
} else if (fc.property === 'concept' && fc.op === 'is-not-a') {
|
|
1690
|
-
let loc = await cs.locateIsA(code, fc.value);
|
|
1691
|
-
result = loc === null;
|
|
1692
|
-
if (result) {
|
|
1693
|
-
let msg;
|
|
1694
|
-
loc = await cs.locate(code, null, msg);
|
|
1695
|
-
if (loc !== null) {
|
|
1696
|
-
await this.worker.listDisplaysFromCodeSystem(displays, cs, loc);
|
|
1697
|
-
if (!(abstractOk || !cs.IsAbstract(loc))) {
|
|
1698
|
-
this.worker.opContext.addNote(this.valueSet, 'Filter ' + fc.property + fc.op + fc.value + ': Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs) + ' but is abstract', this.indentCount);
|
|
1699
|
-
if (!this.params.membershipOnly) {
|
|
1700
|
-
op.addIssue(new Issue('error', 'business-rule', addToPath(path, 'code'), 'ABSTRACT_CODE_NOT_ALLOWED', this.worker.i18n.translate('ABSTRACT_CODE_NOT_ALLOWED', this.params.HTTPLanguages, [cs.system(), code]), 'code-rule'));
|
|
1701
|
-
}
|
|
1702
|
-
} else if (this.excludeInactives() && await cs.isInactive(loc)) {
|
|
1703
|
-
this.worker.opContext.addNote(this.valueSet, 'Filter ' + fc.property + fc.op + fc.value + ': Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs) + ' but is inactive', this.indentCount);
|
|
1704
|
-
result = false;
|
|
1705
|
-
if (!this.params.membershipOnly) {
|
|
1706
|
-
inactive.value = true;
|
|
1707
|
-
inactive.path = path;
|
|
1708
|
-
vstatus.value = await cs.getStatus(loc);
|
|
1709
|
-
}
|
|
1710
|
-
} else {
|
|
1711
|
-
this.worker.opContext.addNote(this.valueSet, 'Filter ' + fc.property + fc.op + fc.value + ': Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs), this.indentCount);
|
|
1712
|
-
if (vcc !== null) {
|
|
1713
|
-
vcc.addCoding(cs.system(), cs.version(), await cs.code(loc), displays.preferredDisplay(this.params.workingLanguages()));
|
|
1714
|
-
}
|
|
1715
|
-
result = true;
|
|
1716
|
-
return result;
|
|
1717
|
-
}
|
|
1718
|
-
}
|
|
1719
|
-
} else {
|
|
1720
|
-
this.worker.opContext.addNote(this.valueSet, 'Filter ' + fc.property + fc.op + fc.value + ': Code "' + code + '" not found in ' + this.worker.renderer.displayCoded(cs), this.indentCount);
|
|
1721
|
-
}
|
|
1722
|
-
} else {
|
|
1723
|
-
let ctxt = filters[i];
|
|
1724
|
-
result = false;
|
|
1725
|
-
let loc = await cs.filterLocate(prep, ctxt, code);
|
|
1726
|
-
if (!(typeof loc === 'string')) {
|
|
1727
|
-
await this.worker.listDisplaysFromCodeSystem(displays, cs, loc);
|
|
1728
|
-
if (!(abstractOk || !cs.IsAbstract(loc))) {
|
|
1729
|
-
this.worker.opContext.addNote(this.valueSet, 'Filter ' + ctxt.summary + ': Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs) + ' but is abstract', this.indentCount);
|
|
1730
|
-
if (!this.params.membershipOnly) {
|
|
1731
|
-
op.addIssue(new Issue('error', 'business-rule', addToPath(path, 'code'), 'ABSTRACT_CODE_NOT_ALLOWED', this.worker.i18n.translate('ABSTRACT_CODE_NOT_ALLOWED', this.params.HTTPLanguages, [cs.system(), code]), 'code-rule'));
|
|
1732
|
-
}
|
|
1733
|
-
} else if (this.excludeInactives() && await cs.isInactive(loc)) {
|
|
1734
|
-
this.worker.opContext.addNote(this.valueSet, 'Filter ' + ctxt.summary + ': Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs) + ' but is inactive', this.indentCount);
|
|
1735
|
-
result = false;
|
|
1736
|
-
if (!this.params.membershipOnly) {
|
|
1737
|
-
inactive.value = true;
|
|
1738
|
-
inactive.path = path;
|
|
1739
|
-
vstatus.value = await cs.getStatus(loc);
|
|
1740
|
-
}
|
|
1741
|
-
} else {
|
|
1742
|
-
this.worker.opContext.addNote(this.valueSet, 'Filter ' + ctxt.summary + ': Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs), this.indentCount);
|
|
1743
|
-
if (vcc !== null) {
|
|
1744
|
-
vcc.addCoding(cs.system(), cs.version(), await cs.code(loc), displays.preferredDisplay(this.params.workingLanguages()));
|
|
1745
|
-
}
|
|
1746
|
-
result = true;
|
|
1747
|
-
return result;
|
|
1748
|
-
}
|
|
1749
|
-
} else {
|
|
1750
|
-
this.worker.opContext.addNote(this.valueSet, 'Filter ' + ctxt.summary + ': Code "' + code + '" not found in ' + this.worker.renderer.displayCoded(cs)+": "+loc, this.indentCount);
|
|
1751
|
-
}
|
|
1663
|
+
}
|
|
1664
|
+
if (loc != null && !(typeof loc === 'string')) {
|
|
1665
|
+
await this.worker.listDisplaysFromCodeSystem(displays, cs, loc);
|
|
1666
|
+
if (!(this.params.abstractOk || !(await cs.isAbstract(loc)))) {
|
|
1667
|
+
this.worker.opContext.addNote(this.valueSet, 'Filter ' + this.filterSummary(cset) + ': Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs) + ' but is abstract', this.indentCount);
|
|
1668
|
+
if (!this.params.membershipOnly) {
|
|
1669
|
+
op.addIssue(new Issue('error', 'business-rule', addToPath(path, 'code'), 'ABSTRACT_CODE_NOT_ALLOWED', this.worker.i18n.translate('ABSTRACT_CODE_NOT_ALLOWED', this.params.HTTPLanguages, [cs.system(), code]), 'code-rule'));
|
|
1752
1670
|
}
|
|
1753
|
-
|
|
1754
|
-
|
|
1671
|
+
} else if (this.excludeInactives() && await cs.isInactive(loc)) {
|
|
1672
|
+
result = false;
|
|
1673
|
+
this.worker.opContext.addNote(this.valueSet, 'Filter ' + this.filterSummary(cset) + ': Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs) + ' but is inactive', this.indentCount);
|
|
1674
|
+
if (!this.params.membershipOnly) {
|
|
1675
|
+
inactive.value = true;
|
|
1676
|
+
inactive.path = path;
|
|
1677
|
+
vstatus.value = await cs.getStatus(loc);
|
|
1755
1678
|
}
|
|
1756
|
-
|
|
1679
|
+
} else {
|
|
1680
|
+
this.worker.opContext.addNote(this.valueSet, 'Filter ' + this.filterSummary(cset) + ': Code "' + code + '" found in ' + this.worker.renderer.displayCoded(cs), this.indentCount);
|
|
1681
|
+
if (vcc !== null) {
|
|
1682
|
+
if (!vcc.coding) { vcc.coding = []}
|
|
1683
|
+
vcc.coding.push( { system : cs.system(), version: cs.version(), code: await cs.code(loc), display: displays.preferredDisplay(this.params.workingLanguages())});
|
|
1684
|
+
}
|
|
1685
|
+
result = true;
|
|
1686
|
+
return result;
|
|
1757
1687
|
}
|
|
1688
|
+
} else if (loc != null) {
|
|
1689
|
+
this.worker.opContext.addNote(this.valueSet, 'Filter ' + this.filterSummary(cset) + ': Code "' + code + '" not found in ' + this.worker.renderer.displayCoded(cs)+ ": "+loc, this.indentCount);
|
|
1690
|
+
messages.push(loc);
|
|
1691
|
+
} else {
|
|
1692
|
+
this.worker.opContext.addNote(this.valueSet, 'Filter ' + this.filterSummary(cset) + ': Code "' + code + '" not found in ' + this.worker.renderer.displayCoded(cs), this.indentCount);
|
|
1758
1693
|
}
|
|
1759
1694
|
}
|
|
1760
1695
|
return result;
|
|
1761
1696
|
}
|
|
1762
1697
|
|
|
1763
|
-
async checkExpansion(path, cs, cset, code,
|
|
1698
|
+
async checkExpansion(path, cs, cset, code, displays, vs, message, inactive, vstatus, op) {
|
|
1764
1699
|
let result = false;
|
|
1765
1700
|
let loc = await cs.locate(code, null, message);
|
|
1766
1701
|
result = false;
|
|
1767
1702
|
if (loc === null || loc.context == null) {
|
|
1768
1703
|
if (!this.params.membershipOnly) {
|
|
1769
1704
|
op.addIssue(new Issue('error', 'code-invalid', addToPath(path, 'code'), 'Unknown_Code_in_Version',
|
|
1770
|
-
this.worker.i18n.translate(Unknown_Code_in_VersionSCT(cs.system()), this.params.HTTPLanguages, [code, cs.system(), cs.version(), SCTVersion(cs.system(), cs.version())]), 'invalid-code'));
|
|
1705
|
+
this.worker.i18n.translate(Unknown_Code_in_VersionSCT(cs.system(), cs.version()), this.params.HTTPLanguages, [code, cs.system(), cs.version(), SCTVersion(cs.system(), cs.version())]), 'invalid-code'));
|
|
1771
1706
|
}
|
|
1772
|
-
} else if (!(abstractOk || !cs.
|
|
1707
|
+
} else if (!(this.params.abstractOk || !(await cs.isAbstract(loc.context)))) {
|
|
1773
1708
|
if (!this.params.membershipOnly) {
|
|
1774
1709
|
op.addIssue(new Issue('error', 'business-rule', addToPath(path, 'code'), 'ABSTRACT_CODE_NOT_ALLOWED', this.worker.i18n.translate('ABSTRACT_CODE_NOT_ALLOWED', this.params.HTTPLanguages, [cs.system(), code]), 'code-rule'));
|
|
1775
1710
|
}
|
|
@@ -1789,6 +1724,17 @@ class ValueSetChecker {
|
|
|
1789
1724
|
return this.valueSet.jsonObj.compose && this.valueSet.jsonObj.compose.inactive != undefined && !this.valueSet.jsonObj.compose.inactive;
|
|
1790
1725
|
}
|
|
1791
1726
|
|
|
1727
|
+
filterSummary(cset) {
|
|
1728
|
+
let list = [];
|
|
1729
|
+
for (let filter of cset.filter) {
|
|
1730
|
+
let s = cset.filter.length > 1 ? "(" : "";
|
|
1731
|
+
s = filter.prop+" "+filter.op+" "+filter.value;
|
|
1732
|
+
s = s + (cset.filter.length > 1 ? ")" : "");
|
|
1733
|
+
list.push(s)
|
|
1734
|
+
}
|
|
1735
|
+
return list.join(",");
|
|
1736
|
+
}
|
|
1737
|
+
|
|
1792
1738
|
}
|
|
1793
1739
|
|
|
1794
1740
|
function addToPath(path, name) {
|
|
@@ -1800,11 +1746,13 @@ function addToPath(path, name) {
|
|
|
1800
1746
|
}
|
|
1801
1747
|
|
|
1802
1748
|
|
|
1803
|
-
function Unknown_Code_in_VersionSCT(url) {
|
|
1749
|
+
function Unknown_Code_in_VersionSCT(url, version) {
|
|
1804
1750
|
if (url === 'http://snomed.info/sct') {
|
|
1805
1751
|
return 'Unknown_Code_in_Version_SCT';
|
|
1806
|
-
} else {
|
|
1752
|
+
} else if (version) {
|
|
1807
1753
|
return 'Unknown_Code_in_Version';
|
|
1754
|
+
} else {
|
|
1755
|
+
return 'Unknown_Code_in';
|
|
1808
1756
|
}
|
|
1809
1757
|
}
|
|
1810
1758
|
|
|
@@ -1864,6 +1812,8 @@ function toText(st, sep) {
|
|
|
1864
1812
|
|
|
1865
1813
|
class ValidateWorker extends TerminologyWorker {
|
|
1866
1814
|
|
|
1815
|
+
requiredSupplements = new Set();
|
|
1816
|
+
usedSupplements = new Set();
|
|
1867
1817
|
/**
|
|
1868
1818
|
* @param {OperationContext} opContext - Operation context
|
|
1869
1819
|
* @param {Logger} log - Logger instance
|
|
@@ -1901,7 +1851,7 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
1901
1851
|
|
|
1902
1852
|
} catch (error) {
|
|
1903
1853
|
this.log.error(error);
|
|
1904
|
-
|
|
1854
|
+
this.debugLog(error);
|
|
1905
1855
|
if (error instanceof Issue) {
|
|
1906
1856
|
if (error.isHandleAsOO()) {
|
|
1907
1857
|
let oo = new OperationOutcome();
|
|
@@ -1922,27 +1872,34 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
1922
1872
|
let coded;
|
|
1923
1873
|
let mode;
|
|
1924
1874
|
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
this.setupAdditionalResources(params);
|
|
1875
|
+
// Handle tx-resource and cache-id parameters
|
|
1876
|
+
this.setupAdditionalResources(params);
|
|
1928
1877
|
|
|
1929
|
-
|
|
1930
|
-
|
|
1878
|
+
let txp = new TxParameters(this.languages, this.i18n, true);
|
|
1879
|
+
txp.readParams(params);
|
|
1880
|
+
for (const item of txp.supplements) this.requiredSupplements.add(item);
|
|
1881
|
+
|
|
1882
|
+
try {
|
|
1931
1883
|
|
|
1932
1884
|
// Extract coded value
|
|
1933
1885
|
mode = {mode: null};
|
|
1934
1886
|
coded = this.extractCodedValue(params, true, mode);
|
|
1935
1887
|
if (!coded) {
|
|
1936
|
-
throw new Issue('error', 'invalid', null, null, 'Unable to find code to validate (looked for coding | codeableConcept | code in parameters
|
|
1888
|
+
throw new Issue('error', 'invalid', null, null, 'Unable to find code to validate (looked for coding | codeableConcept | code in parameters)', null, 400).handleAsOO(400);
|
|
1937
1889
|
}
|
|
1938
1890
|
|
|
1939
1891
|
// Get the CodeSystem - from parameter or by url
|
|
1940
1892
|
const codeSystem = await this.resolveCodeSystem(params, txp, coded?.coding?.[0] ?? null, mode);
|
|
1941
1893
|
if (!codeSystem) {
|
|
1942
|
-
|
|
1894
|
+
if (!coded?.coding?.[0].system) {
|
|
1895
|
+
let msg = this.i18n.translate('Coding_has_no_system__cannot_validate', txp.HTTPLanguages, []);
|
|
1896
|
+
throw new Issue('warning', 'invalid', mode.issuePath, 'Coding_has_no_system__cannot_validate', msg, 'invalid-data');
|
|
1897
|
+
} else {
|
|
1898
|
+
throw new Issue('error', 'invalid', null, null, 'No CodeSystem specified - provide url parameter or codeSystem resource', null, 400);
|
|
1899
|
+
}
|
|
1943
1900
|
}
|
|
1944
1901
|
if (codeSystem.contentMode() == 'supplement') {
|
|
1945
|
-
throw new Issue('error', 'invalid', this.systemPath(mode), 'CODESYSTEM_CS_NO_SUPPLEMENT', this.opContext.i18n.translate('CODESYSTEM_CS_NO_SUPPLEMENT', txp.HTTPLanguages, [codeSystem.vurl()]), "invalid-data");
|
|
1902
|
+
throw new Issue('error', 'invalid', this.systemPath(mode), 'CODESYSTEM_CS_NO_SUPPLEMENT', this.opContext.i18n.translate('CODESYSTEM_CS_NO_SUPPLEMENT', txp.HTTPLanguages, [codeSystem.vurl()]), "invalid-data", 400);
|
|
1946
1903
|
}
|
|
1947
1904
|
|
|
1948
1905
|
// Perform validation
|
|
@@ -1953,8 +1910,9 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
1953
1910
|
return result;
|
|
1954
1911
|
} catch (error) {
|
|
1955
1912
|
this.log.error(error);
|
|
1913
|
+
this.debugLog(error);
|
|
1956
1914
|
if (error instanceof Issue && !error.isHandleAsOO()) {
|
|
1957
|
-
return this.handlePrepareError(error, coded, mode.mode);
|
|
1915
|
+
return await this.handlePrepareError(error, coded, mode.mode, txp);
|
|
1958
1916
|
} else {
|
|
1959
1917
|
throw error;
|
|
1960
1918
|
}
|
|
@@ -1986,11 +1944,12 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
1986
1944
|
|
|
1987
1945
|
let txp = new TxParameters(this.languages, this.i18n, true);
|
|
1988
1946
|
txp.readParams(params);
|
|
1947
|
+
for (const item of txp.supplements) this.requiredSupplements.add(item);
|
|
1989
1948
|
|
|
1990
1949
|
// Get the CodeSystem by id
|
|
1991
1950
|
const codeSystem = await this.provider.getCodeSystemById(this.opContext, id);
|
|
1992
1951
|
if (!codeSystem) {
|
|
1993
|
-
return res.status(
|
|
1952
|
+
return res.status(422).json(this.operationOutcome('error', 'not-found',
|
|
1994
1953
|
`CodeSystem/${id} not found`));
|
|
1995
1954
|
}
|
|
1996
1955
|
const csp = new FhirCodeSystemProvider(this.opContext, new CodeSystem(codeSystem), []);
|
|
@@ -2010,6 +1969,7 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2010
1969
|
|
|
2011
1970
|
} catch (error) {
|
|
2012
1971
|
this.log.error(error);
|
|
1972
|
+
this.debugLog(error);
|
|
2013
1973
|
return res.status(error.statusCode || 500).json(this.operationOutcome(
|
|
2014
1974
|
'error', error.issueCode || 'exception', error.message));
|
|
2015
1975
|
}
|
|
@@ -2030,6 +1990,7 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2030
1990
|
|
|
2031
1991
|
} catch (error) {
|
|
2032
1992
|
this.log.error(error);
|
|
1993
|
+
this.debugLog(error);
|
|
2033
1994
|
if (error instanceof Issue) {
|
|
2034
1995
|
let op = new OperationOutcome();
|
|
2035
1996
|
op.addIssue(error);
|
|
@@ -2047,6 +2008,7 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2047
2008
|
|
|
2048
2009
|
let txp = new TxParameters(this.languages, this.i18n, true);
|
|
2049
2010
|
txp.readParams(params);
|
|
2011
|
+
for (const item of txp.supplements) this.requiredSupplements.add(item);
|
|
2050
2012
|
|
|
2051
2013
|
// Get the ValueSet - from parameter or by url
|
|
2052
2014
|
const valueSet = await this.resolveValueSet(params, txp);
|
|
@@ -2058,7 +2020,7 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2058
2020
|
let mode = { mode : null };
|
|
2059
2021
|
const coded = this.extractCodedValue(params, false, mode);
|
|
2060
2022
|
if (!coded) {
|
|
2061
|
-
throw new Issue("error", "invalid", null, null, 'Unable to find code to validate (looked for coding | codeableConcept | code in parameters
|
|
2023
|
+
throw new Issue("error", "invalid", null, null, 'Unable to find code to validate (looked for coding | codeableConcept | code+system | code+inferSystem in parameters', null, 422);
|
|
2062
2024
|
}
|
|
2063
2025
|
|
|
2064
2026
|
// Perform validation
|
|
@@ -2083,12 +2045,13 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2083
2045
|
|
|
2084
2046
|
let txp = new TxParameters(this.languages, this.i18n, true);
|
|
2085
2047
|
txp.readParams(params);
|
|
2048
|
+
for (const item of txp.supplements) this.requiredSupplements.add(item);
|
|
2086
2049
|
|
|
2087
2050
|
// Get the ValueSet by id
|
|
2088
2051
|
const valueSet = await this.provider.getValueSetById(this.opContext, id);
|
|
2089
2052
|
this.seeSourceVS(valueSet, id);
|
|
2090
2053
|
if (!valueSet) {
|
|
2091
|
-
return res.status(
|
|
2054
|
+
return res.status(422).json(this.operationOutcome('error', 'not-found',
|
|
2092
2055
|
`ValueSet/${id} not found`));
|
|
2093
2056
|
}
|
|
2094
2057
|
|
|
@@ -2107,6 +2070,7 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2107
2070
|
|
|
2108
2071
|
} catch (error) {
|
|
2109
2072
|
this.log.error(error);
|
|
2073
|
+
this.debugLog(error);
|
|
2110
2074
|
return res.status(error.statusCode || 500).json(this.operationOutcome(
|
|
2111
2075
|
'error', error.issueCode || 'exception', error.message));
|
|
2112
2076
|
}
|
|
@@ -2118,13 +2082,13 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2118
2082
|
* Resolve the CodeSystem to validate against
|
|
2119
2083
|
* @param {Object} params - Parameters resource
|
|
2120
2084
|
* @param {string|null} id - Instance id (if instance-level request)
|
|
2121
|
-
* @returns {
|
|
2085
|
+
* @returns {CodeSystemProvider|null} CodeSystem resource (wrapper or JSON)
|
|
2122
2086
|
*/
|
|
2123
2087
|
async resolveCodeSystem(params, txParams, coded, mode) {
|
|
2124
2088
|
// Check for codeSystem resource parameter
|
|
2125
2089
|
const csResource = this.getResourceParam(params, 'codeSystem');
|
|
2126
2090
|
if (csResource) {
|
|
2127
|
-
return csResource;
|
|
2091
|
+
return new FhirCodeSystemProvider(this.opContext, new CodeSystem(csResource), []); // todo: supplements
|
|
2128
2092
|
}
|
|
2129
2093
|
let path = coded == null ? null : mode.issuePath+".system";
|
|
2130
2094
|
let fromCoded = false;
|
|
@@ -2150,7 +2114,7 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2150
2114
|
}
|
|
2151
2115
|
version = this.determineVersionBase(url, version, txParams);
|
|
2152
2116
|
|
|
2153
|
-
let supplements = this.loadSupplements(url, version);
|
|
2117
|
+
let supplements = this.loadSupplements(url, version, this.requiredSupplements);
|
|
2154
2118
|
|
|
2155
2119
|
// First check additional resources
|
|
2156
2120
|
const fromAdditional = this.findInAdditionalResources(url, version, 'CodeSystem', false);
|
|
@@ -2169,12 +2133,12 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2169
2133
|
} else if (version) {
|
|
2170
2134
|
let vl = await this.listVersions(url);
|
|
2171
2135
|
if (vl.length == 0) {
|
|
2172
|
-
throw new Issue("error", "not-found", this.systemPath(mode), 'UNKNOWN_CODESYSTEM_VERSION_NONE', this.opContext.i18n.translate('UNKNOWN_CODESYSTEM_VERSION_NONE', this.opContext.HTTPLanguages, [url, version]), 'not-found').setUnknownSystem(url).addIssue(issue);
|
|
2136
|
+
throw new Issue("error", "not-found", this.systemPath(mode), 'UNKNOWN_CODESYSTEM_VERSION_NONE', this.opContext.i18n.translate('UNKNOWN_CODESYSTEM_VERSION_NONE', this.opContext.HTTPLanguages, [url, version]), 'not-found', 422).setUnknownSystem(url).addIssue(issue);
|
|
2173
2137
|
} else {
|
|
2174
|
-
throw new Issue("error", "not-found", this.systemPath(mode), 'UNKNOWN_CODESYSTEM_VERSION', this.opContext.i18n.translate('UNKNOWN_CODESYSTEM_VERSION', this.opContext.HTTPLanguages, [url, version, this.presentVersionList(vl)]), 'not-found').setUnknownSystem(url + "|" + version).addIssue(issue);
|
|
2138
|
+
throw new Issue("error", "not-found", this.systemPath(mode), 'UNKNOWN_CODESYSTEM_VERSION', this.opContext.i18n.translate('UNKNOWN_CODESYSTEM_VERSION', this.opContext.HTTPLanguages, [url, version, this.presentVersionList(vl)], 422), 'not-found').setUnknownSystem(url + "|" + version).addIssue(issue);
|
|
2175
2139
|
}
|
|
2176
2140
|
} else {
|
|
2177
|
-
throw new Issue("error", "not-found", this.systemPath(mode), 'UNKNOWN_CODESYSTEM', this.opContext.i18n.translate('UNKNOWN_CODESYSTEM', this.opContext.HTTPLanguages, [url]), 'not-found').setUnknownSystem(url).addIssue(issue);
|
|
2141
|
+
throw new Issue("error", "not-found", this.systemPath(mode), 'UNKNOWN_CODESYSTEM', this.opContext.i18n.translate('UNKNOWN_CODESYSTEM', this.opContext.HTTPLanguages, [url]), 'not-found', 422).setUnknownSystem(url).addIssue(issue);
|
|
2178
2142
|
}
|
|
2179
2143
|
}
|
|
2180
2144
|
}
|
|
@@ -2208,7 +2172,7 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2208
2172
|
let vs = await this.provider.findValueSet(this.opContext, url, version);
|
|
2209
2173
|
this.seeSourceVS(vs, url);
|
|
2210
2174
|
if (vs == null) {
|
|
2211
|
-
throw new Issue('error', 'not-found', null, 'Unable_to_resolve_value_Set_', this.i18n.translate('Unable_to_resolve_value_Set_', params.HTTPLanguages, [url+(version ? "|"+version : "")]), 'not-found',
|
|
2175
|
+
throw new Issue('error', 'not-found', null, 'Unable_to_resolve_value_Set_', this.i18n.translate('Unable_to_resolve_value_Set_', params.HTTPLanguages, [url+(version ? "|"+version : "")]), 'not-found', 422);
|
|
2212
2176
|
} else {
|
|
2213
2177
|
return vs;
|
|
2214
2178
|
}
|
|
@@ -2263,6 +2227,9 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2263
2227
|
}
|
|
2264
2228
|
const display = this.getStringParam(params, 'display');
|
|
2265
2229
|
|
|
2230
|
+
if (!system && !this.getStringParam(params, 'inferSystem')) {
|
|
2231
|
+
return null;
|
|
2232
|
+
}
|
|
2266
2233
|
const codingObj = {code};
|
|
2267
2234
|
if (system) codingObj.system = system;
|
|
2268
2235
|
if (version) codingObj.version = version;
|
|
@@ -2289,14 +2256,11 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2289
2256
|
|
|
2290
2257
|
let vs = this.makeVsForCS(codeSystem);
|
|
2291
2258
|
|
|
2292
|
-
// Get parameters
|
|
2293
|
-
const abstractOk = this._getBoolParam(params, 'abstract', true);
|
|
2294
|
-
|
|
2295
2259
|
// Create and prepare checker
|
|
2296
2260
|
const checker = new ValueSetChecker(this, vs, params);
|
|
2297
2261
|
|
|
2298
2262
|
// Perform validation
|
|
2299
|
-
const result = await checker.checkCodeableConcept(mode.issuePath, coded,
|
|
2263
|
+
const result = await checker.checkCodeableConcept(mode.issuePath, coded, mode.mode);
|
|
2300
2264
|
|
|
2301
2265
|
// Add diagnostics if requested
|
|
2302
2266
|
if (params.diagnostics) {
|
|
@@ -2336,9 +2300,9 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2336
2300
|
this.deadCheck('doValidationVS');
|
|
2337
2301
|
this.params = params;
|
|
2338
2302
|
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2303
|
+
for (let ext of Extensions.list(valueSet.jsonObj, 'http://hl7.org/fhir/StructureDefinition/valueset-supplement')) {
|
|
2304
|
+
this.requiredSupplements.add(getValuePrimitive(ext));
|
|
2305
|
+
}
|
|
2342
2306
|
|
|
2343
2307
|
// Create and prepare checker
|
|
2344
2308
|
const checker = new ValueSetChecker(this, valueSet, params);
|
|
@@ -2346,15 +2310,16 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2346
2310
|
await checker.prepare();
|
|
2347
2311
|
} catch (error) {
|
|
2348
2312
|
this.log.error(error);
|
|
2313
|
+
this.debugLog(error);
|
|
2349
2314
|
if (!(error instanceof Issue) || error.isHandleAsOO()) {
|
|
2350
2315
|
throw error;
|
|
2351
2316
|
} else {
|
|
2352
|
-
return this.handlePrepareError(error, coded, mode);
|
|
2317
|
+
return await this.handlePrepareError(error, coded, mode, params);
|
|
2353
2318
|
}
|
|
2354
2319
|
}
|
|
2355
2320
|
|
|
2356
2321
|
// Perform validation
|
|
2357
|
-
const result = await checker.checkCodeableConcept(issuePath, coded,
|
|
2322
|
+
const result = await checker.checkCodeableConcept(issuePath, coded, mode);
|
|
2358
2323
|
|
|
2359
2324
|
// Add diagnostics if requested
|
|
2360
2325
|
if (params.diagnostics) {
|
|
@@ -2462,14 +2427,16 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2462
2427
|
}
|
|
2463
2428
|
|
|
2464
2429
|
|
|
2465
|
-
handlePrepareError(error, coded, mode) {
|
|
2430
|
+
async handlePrepareError(error, coded, mode, txp) {
|
|
2466
2431
|
let op = new OperationOutcome();
|
|
2467
2432
|
op.addIssue(error);
|
|
2468
2433
|
let p = new Parameters();
|
|
2469
2434
|
p.addParamResource('issues', op.jsonObj);
|
|
2470
2435
|
p.addParamBool('result', false);
|
|
2471
2436
|
p.addParamStr('message', error.message);
|
|
2472
|
-
if (mode
|
|
2437
|
+
if (!mode || !coded) {
|
|
2438
|
+
// nothing to add
|
|
2439
|
+
} else if (mode == 'codeableConcept') {
|
|
2473
2440
|
p.addParam('codeableConcept', 'valueCodeableConcept', coded);
|
|
2474
2441
|
} else if (coded.coding) {
|
|
2475
2442
|
if (coded.coding[0].system) {
|
|
@@ -2477,13 +2444,19 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2477
2444
|
}
|
|
2478
2445
|
if (coded.coding[0].version) {
|
|
2479
2446
|
p.addParamStr('version', coded.coding[0].version)
|
|
2447
|
+
} else if (coded.coding[0].system) {
|
|
2448
|
+
try {
|
|
2449
|
+
let cs = await this.findCodeSystem(coded.coding[0].system, null, txp);
|
|
2450
|
+
if (cs && cs.version()) {
|
|
2451
|
+
p.addParamStr('version', cs.version());
|
|
2452
|
+
}
|
|
2453
|
+
} catch (e) {
|
|
2454
|
+
// nothing. not interested.
|
|
2455
|
+
}
|
|
2480
2456
|
}
|
|
2481
2457
|
if (coded.coding[0].code) {
|
|
2482
2458
|
p.addParamCode('code', coded.coding[0].code)
|
|
2483
2459
|
}
|
|
2484
|
-
if (coded.coding[0].display) {
|
|
2485
|
-
p.addParamStr('display', coded.coding[0].display)
|
|
2486
|
-
}
|
|
2487
2460
|
}
|
|
2488
2461
|
if (error.unknownSystem) {
|
|
2489
2462
|
p.addParamCanonical("x-caused-by-unknown-system", error.unknownSystem);
|