fhirsmith 0.12.0 → 0.13.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 +305 -0
- package/README.md +1 -1
- package/config-template.json +13 -0
- package/library/languages.js +191 -34
- package/library/path-safety.js +51 -0
- package/library/request-token.js +56 -0
- package/package.json +2 -2
- package/packages/packages.js +17 -118
- package/publisher/publisher.js +60 -30
- package/publisher/validation.js +15 -3
- package/server.js +12 -3
- package/translations/Messages.properties +1 -0
- package/tx/README.md +21 -0
- package/tx/cs/cs-api.js +30 -1
- package/tx/cs/cs-cs.js +1 -1
- package/tx/cs/cs-icd11.js +805 -0
- package/tx/cs/cs-lang.js +331 -85
- package/tx/cs/cs-loinc.js +45 -4
- package/tx/cs/cs-mimetypes.js +492 -21
- package/tx/cs/cs-snomed.js +38 -4
- package/tx/cs/cs-ucum.js +33 -3
- package/tx/cs/icd11-expressions.js +243 -0
- package/tx/importers/export-icd11-codesystem.js +823 -0
- package/tx/importers/icd11-schema.md +285 -0
- package/tx/importers/import-icd11.js +861 -0
- package/tx/importers/import-loinc.module.js +14 -7
- package/tx/importers/import-sct.module.js +10 -1
- package/tx/library/codesystem.js +21 -27
- package/tx/library/nesting-limit.js +79 -0
- package/tx/library/operation-outcome.js +97 -3
- package/tx/library/valueset.js +16 -20
- package/tx/library.js +23 -1
- package/tx/ocl/cache/cache-utils.cjs +5 -3
- package/tx/ocl/cs-ocl.cjs +73 -17
- package/tx/ocl/jobs/background-queue.cjs +6 -3
- package/tx/ocl/mappers/concept-mapper.cjs +9 -1
- package/tx/ocl/shared/patches.cjs +48 -1
- package/tx/ocl/vs-ocl.cjs +15 -13
- package/tx/sct/expressions.js +194 -17
- package/tx/tx.fhir.org.yml +2 -1
- package/tx/tx.js +15 -42
- package/tx/workers/batch-validate.js +6 -16
- package/tx/workers/batch.js +16 -28
- package/tx/workers/cache-control.js +6 -12
- package/tx/workers/closure.js +3 -8
- package/tx/workers/compare.js +6 -34
- package/tx/workers/expand.js +36 -45
- package/tx/workers/lookup.js +21 -39
- package/tx/workers/read.js +6 -40
- package/tx/workers/search.js +2 -8
- package/tx/workers/subsumes.js +58 -41
- package/tx/workers/translate.js +11 -17
- package/tx/workers/validate.js +76 -77
- package/tx/workers/worker.js +70 -1
- package/tx/xml/xml-base.js +243 -76
- package/tx/xversion/xv-valueset.js +39 -1
package/tx/workers/translate.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
const { TerminologyWorker } = require('./worker');
|
|
11
11
|
const { TxParameters } = require('../params');
|
|
12
12
|
const { Parameters } = require('../library/parameters');
|
|
13
|
-
const { Issue, OperationOutcome } = require('../library/operation-outcome');
|
|
13
|
+
const { Issue, OperationOutcome, buildOperationOutcome, outcomeFromError } = require('../library/operation-outcome');
|
|
14
14
|
const {ConceptMap} = require("../library/conceptmap");
|
|
15
15
|
const {Extensions} = require("../library/extensions");
|
|
16
16
|
const {VersionUtilities} = require("../../library/version-utilities");
|
|
@@ -62,8 +62,7 @@ class TranslateWorker extends TerminologyWorker {
|
|
|
62
62
|
oo.addIssue(error);
|
|
63
63
|
return res.status(error.statusCode || 500).json(oo.jsonObj);
|
|
64
64
|
} else {
|
|
65
|
-
return res.status(error.statusCode || 500).json(
|
|
66
|
-
'error', error.issueCode || 'exception', error.message));
|
|
65
|
+
return res.status(error.statusCode || 500).json(outcomeFromError(error));
|
|
67
66
|
}
|
|
68
67
|
}
|
|
69
68
|
}
|
|
@@ -85,8 +84,7 @@ class TranslateWorker extends TerminologyWorker {
|
|
|
85
84
|
oo.addIssue(error);
|
|
86
85
|
return res.status(error.statusCode || 500).json(oo.jsonObj);
|
|
87
86
|
} else {
|
|
88
|
-
return res.status(error.statusCode || 500).json(
|
|
89
|
-
'error', error.issueCode || 'exception', error.message));
|
|
87
|
+
return res.status(error.statusCode || 500).json(outcomeFromError(error));
|
|
90
88
|
}
|
|
91
89
|
}
|
|
92
90
|
}
|
|
@@ -1013,21 +1011,17 @@ class TranslateWorker extends TerminologyWorker {
|
|
|
1013
1011
|
}
|
|
1014
1012
|
}
|
|
1015
1013
|
/**
|
|
1016
|
-
* Build an OperationOutcome
|
|
1014
|
+
* Build an OperationOutcome. Delegates to the shared builder so that every outcome this
|
|
1015
|
+
* server emits has details.text and a tx-issue-type coding: diagnostics is stripped by
|
|
1016
|
+
* the test harness, so nothing a caller needs may live there.
|
|
1017
1017
|
* @param {string} severity - error, warning, information
|
|
1018
|
-
* @param {string} code -
|
|
1019
|
-
* @param {string} message -
|
|
1018
|
+
* @param {string} code - FHIR issue type
|
|
1019
|
+
* @param {string} message - the human readable account of the problem
|
|
1020
|
+
* @param {string} [txIssueType] - tx-issue-type; defaulted from code when not given
|
|
1020
1021
|
* @returns {Object} OperationOutcome resource
|
|
1021
1022
|
*/
|
|
1022
|
-
operationOutcome(severity, code, message) {
|
|
1023
|
-
return
|
|
1024
|
-
resourceType: 'OperationOutcome',
|
|
1025
|
-
issue: [{
|
|
1026
|
-
severity,
|
|
1027
|
-
code,
|
|
1028
|
-
diagnostics: message
|
|
1029
|
-
}]
|
|
1030
|
-
};
|
|
1023
|
+
operationOutcome(severity, code, message, txIssueType = null) {
|
|
1024
|
+
return buildOperationOutcome(severity, code, message, txIssueType);
|
|
1031
1025
|
}
|
|
1032
1026
|
|
|
1033
1027
|
/**
|
package/tx/workers/validate.js
CHANGED
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
// POST /ValueSet/{id}/$validate-code
|
|
12
12
|
//
|
|
13
13
|
|
|
14
|
-
const { TerminologyWorker } = require('./worker');
|
|
14
|
+
const { TerminologyWorker, Unknown_Code_in_VersionSCT, SCTVersion } = require('./worker');
|
|
15
15
|
const {Languages, Language} = require("../../library/languages");
|
|
16
16
|
const {Extensions} = require("../library/extensions");
|
|
17
17
|
const {validateParameter, isAbsoluteUrl, validateOptionalParameter, getValuePrimitive} = require("../../library/utilities");
|
|
18
18
|
const {TxParameters} = require("../params");
|
|
19
|
-
const {OperationOutcome, Issue} = require(
|
|
19
|
+
const { OperationOutcome, Issue, buildOperationOutcome, outcomeFromError } = require('../library/operation-outcome');
|
|
20
20
|
const {Parameters} = require("../library/parameters");
|
|
21
21
|
const {Designations, DisplayCheckingStyle, DisplayDifference, SearchFilterText} = require("../library/designations");
|
|
22
22
|
const ValueSet = require("../library/valueset");
|
|
@@ -953,7 +953,7 @@ class ValueSetChecker {
|
|
|
953
953
|
let msg = this.worker.i18n.translate('INACTIVE_CONCEPT_FOUND', this.params.HTTPLanguages, [vstatus.value, coding.code]);
|
|
954
954
|
messages.push(msg);
|
|
955
955
|
op.addIssue(new Issue('warning', 'business-rule', path, 'INACTIVE_CONCEPT_FOUND', msg, 'code-comment'));
|
|
956
|
-
} else if (vstatus.value.toLowerCase() === 'deprecated') {
|
|
956
|
+
} else if (vstatus.value && vstatus.value.toLowerCase() === 'deprecated') {
|
|
957
957
|
result.addParamCode('status', vstatus.value);
|
|
958
958
|
let msg = this.worker.i18n.translate('DEPRECATED_CONCEPT_FOUND', this.params.HTTPLanguages, [vstatus.value, coding.code]);
|
|
959
959
|
messages.push(msg);
|
|
@@ -1051,6 +1051,7 @@ class ValueSetChecker {
|
|
|
1051
1051
|
this.checkCanonicalStatus(issuePath, op, this.valueSet.jsonObj, this.valueSet.jsonObj);
|
|
1052
1052
|
let list = new Designations(this.worker.languages);
|
|
1053
1053
|
let ok = false;
|
|
1054
|
+
let matchedCodings = new Set();
|
|
1054
1055
|
let codelist = '';
|
|
1055
1056
|
mt = [];
|
|
1056
1057
|
let i = 0;
|
|
@@ -1082,6 +1083,9 @@ class ValueSetChecker {
|
|
|
1082
1083
|
if (v === false) {
|
|
1083
1084
|
cause.value = 'code-invalid';
|
|
1084
1085
|
}
|
|
1086
|
+
if (v === true) {
|
|
1087
|
+
matchedCodings.add(i);
|
|
1088
|
+
}
|
|
1085
1089
|
let ws;
|
|
1086
1090
|
if (impliedSystem.value) {
|
|
1087
1091
|
ws = impliedSystem.value;
|
|
@@ -1249,6 +1253,17 @@ class ValueSetChecker {
|
|
|
1249
1253
|
c.version = prov.version();
|
|
1250
1254
|
}
|
|
1251
1255
|
await this.worker.listDisplaysFromCodeSystem(list, prov, ctxt.context);
|
|
1256
|
+
// the code resolved in the code system, so its status is known whether or not it
|
|
1257
|
+
// turns out to be in the value set. check() only picks the status up on the paths
|
|
1258
|
+
// where the code was found in the value set, so pick it up here for the rest
|
|
1259
|
+
if (!this.params.membershipOnly && !inactive.value && await prov.isInactive(ctxt.context)) {
|
|
1260
|
+
inactive.value = true;
|
|
1261
|
+
inactive.path = path;
|
|
1262
|
+
const st = await prov.getStatus(ctxt.context);
|
|
1263
|
+
if (st) {
|
|
1264
|
+
vstatus.value = st;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1252
1267
|
let pd = list.preferredDisplay(this.params.workingLanguages());
|
|
1253
1268
|
if (pd) {
|
|
1254
1269
|
pdisp = pd;
|
|
@@ -1290,6 +1305,36 @@ class ValueSetChecker {
|
|
|
1290
1305
|
}
|
|
1291
1306
|
i++;
|
|
1292
1307
|
}
|
|
1308
|
+
|
|
1309
|
+
// A required binding on a CodeableConcept is satisfied when ANY coding is in the value set, so
|
|
1310
|
+
// a coding whose code system could not be resolved must not veto a membership that another
|
|
1311
|
+
// coding has confirmed. It is still reported, but as a warning, and with a message that does
|
|
1312
|
+
// not read as though nothing could be validated (hapifhir/org.hl7.fhir.core#2272).
|
|
1313
|
+
if (issuePath === 'CodeableConcept' && matchedCodings.size > 0) {
|
|
1314
|
+
const notChecked = ['UNKNOWN_CODESYSTEM', 'UNKNOWN_CODESYSTEM_VERSION', 'UNKNOWN_CODESYSTEM_VERSION_NONE'];
|
|
1315
|
+
let j = 0;
|
|
1316
|
+
for (let c of code.coding || []) {
|
|
1317
|
+
if (!matchedCodings.has(j)) {
|
|
1318
|
+
let sysPath = issuePath + '.coding[' + j + '].system';
|
|
1319
|
+
for (let iss of op.jsonObj.issue || []) {
|
|
1320
|
+
let mid = (iss.extension || []).find(e => e.url === 'http://hl7.org/fhir/StructureDefinition/operationoutcome-message-id');
|
|
1321
|
+
if (iss.severity === 'error' && mid && notChecked.includes(mid.valueString) && (iss.expression || []).includes(sysPath)) {
|
|
1322
|
+
let was = iss.details.text;
|
|
1323
|
+
let now = this.worker.i18n.translate('UNKNOWN_CODESYSTEM_CODING_NOT_CHECKED', this.params.HTTPLanguages,
|
|
1324
|
+
[c.version ? c.system + '|' + c.version : c.system]);
|
|
1325
|
+
iss.severity = 'warning';
|
|
1326
|
+
iss.details.text = now;
|
|
1327
|
+
mid.valueString = 'UNKNOWN_CODESYSTEM_CODING_NOT_CHECKED';
|
|
1328
|
+
// the summary message is built from mt, so it has to say the same thing
|
|
1329
|
+
mt = mt.filter(t => t !== was);
|
|
1330
|
+
msg(now);
|
|
1331
|
+
}
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
j++;
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1293
1338
|
// for an internally defined value set (CodeSystem validation), the code-system-level
|
|
1294
1339
|
// message is preferred - but if nothing produced an error, this is the only
|
|
1295
1340
|
// explanation there will be, so it can't be suppressed
|
|
@@ -1323,7 +1368,10 @@ class ValueSetChecker {
|
|
|
1323
1368
|
}
|
|
1324
1369
|
}
|
|
1325
1370
|
|
|
1326
|
-
|
|
1371
|
+
// the answer, which is not the same as ok: a coding can be in the value set and still carry an
|
|
1372
|
+
// error of its own (an unknown version, say), and then the CodeableConcept is not valid
|
|
1373
|
+
let valid = ok === true && !op.hasErrors();
|
|
1374
|
+
result.addParamBool('result', valid);
|
|
1327
1375
|
if (psys) {
|
|
1328
1376
|
result.addParamUri('system', psys);
|
|
1329
1377
|
} else if (ok === true && impliedSystem.value) {
|
|
@@ -1335,9 +1383,10 @@ class ValueSetChecker {
|
|
|
1335
1383
|
for (let us of unknownSystems) {
|
|
1336
1384
|
if (ok === false) {
|
|
1337
1385
|
result.addParamCanonical('x-unknown-system', us);
|
|
1338
|
-
} else {
|
|
1386
|
+
} else if (!valid) {
|
|
1339
1387
|
result.addParamCanonical('x-caused-by-unknown-system', us);
|
|
1340
1388
|
}
|
|
1389
|
+
// if the CodeableConcept is valid, the unknown system caused nothing: neither parameter applies
|
|
1341
1390
|
}
|
|
1342
1391
|
if (normalForm.value) {
|
|
1343
1392
|
result.addParamCode('normalized-code', normalForm.value);
|
|
@@ -1535,7 +1584,7 @@ class ValueSetChecker {
|
|
|
1535
1584
|
let msg = this.worker.i18n.translate('INACTIVE_CONCEPT_FOUND', this.params.HTTPLanguages, [vstatus.value, code]);
|
|
1536
1585
|
messages.push(msg);
|
|
1537
1586
|
op.addIssue(new Issue('warning', 'business-rule', 'code', 'INACTIVE_CONCEPT_FOUND', msg, 'code-comment'));
|
|
1538
|
-
} else if (vstatus.value.toLowerCase() === 'deprecated') {
|
|
1587
|
+
} else if (vstatus.value && vstatus.value.toLowerCase() === 'deprecated') {
|
|
1539
1588
|
result.addParamCode('status', vstatus.value);
|
|
1540
1589
|
let msg = this.worker.i18n.translate('DEPRECATED_CONCEPT_FOUND', this.params.HTTPLanguages, [vstatus.value, code]);
|
|
1541
1590
|
messages.push(msg);
|
|
@@ -1569,7 +1618,7 @@ class ValueSetChecker {
|
|
|
1569
1618
|
let loc = await cs.locate(code);
|
|
1570
1619
|
result = false;
|
|
1571
1620
|
if (loc.context == null) {
|
|
1572
|
-
this.worker.opContext.addNote(this.valueSet, 'Code "' + code + '" not found in ' + this.worker.renderer.displayCoded(cs)+": "+loc.
|
|
1621
|
+
this.worker.opContext.addNote(this.valueSet, 'Code "' + code + '" not found in ' + this.worker.renderer.displayCoded(cs)+": "+loc.message, this.indentCount);
|
|
1573
1622
|
if (!this.params.membershipOnly && role !== 'not in') {
|
|
1574
1623
|
if (cs.contentMode() !== 'complete') {
|
|
1575
1624
|
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'));
|
|
@@ -1745,6 +1794,19 @@ class ValueSetChecker {
|
|
|
1745
1794
|
if (!vcc.coding) { vcc.coding = []}
|
|
1746
1795
|
vcc.coding.push( { system : cs.system(), version: cs.version(), code: await cs.code(loc), display: displays.preferredDisplay(this.params.workingLanguages())});
|
|
1747
1796
|
}
|
|
1797
|
+
if (role !== 'not in' && !inactive.value) {
|
|
1798
|
+
if (await cs.isInactive(loc)) {
|
|
1799
|
+
inactive.value = true;
|
|
1800
|
+
inactive.path = path;
|
|
1801
|
+
// only replace a status we already have with a real one - a filter context
|
|
1802
|
+
// doesn't always carry the status (SNOMED, LOINC), and the assembly reads
|
|
1803
|
+
// vstatus for the message wording as well as for the status parameter
|
|
1804
|
+
const st = await cs.getStatus(loc);
|
|
1805
|
+
if (st) {
|
|
1806
|
+
vstatus.value = st;
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1748
1810
|
result = true;
|
|
1749
1811
|
return result;
|
|
1750
1812
|
}
|
|
@@ -1875,56 +1937,6 @@ function addToPath(path, name) {
|
|
|
1875
1937
|
}
|
|
1876
1938
|
|
|
1877
1939
|
|
|
1878
|
-
function Unknown_Code_in_VersionSCT(url, version) {
|
|
1879
|
-
if (url === 'http://snomed.info/sct') {
|
|
1880
|
-
return 'Unknown_Code_in_Version_SCT';
|
|
1881
|
-
} else if (version) {
|
|
1882
|
-
return 'Unknown_Code_in_Version';
|
|
1883
|
-
} else {
|
|
1884
|
-
return 'Unknown_Code_in';
|
|
1885
|
-
}
|
|
1886
|
-
}
|
|
1887
|
-
|
|
1888
|
-
function SCTVersion(url, ver) {
|
|
1889
|
-
if (url !== 'http://snomed.info/sct' || !ver) {
|
|
1890
|
-
return '';
|
|
1891
|
-
} else {
|
|
1892
|
-
let result = 'unknown';
|
|
1893
|
-
let s = ver.split('/');
|
|
1894
|
-
if (s.length >= 5) {
|
|
1895
|
-
if (s[4] === '900000000000207008') result = 'International Edition';
|
|
1896
|
-
else if (s[4] === '449081005') result = 'International Spanish Edition';
|
|
1897
|
-
else if (s[4] === '11000221109') result = 'Argentinian Edition';
|
|
1898
|
-
else if (s[4] === '32506021000036107') result = 'Australian Edition (with drug extension)';
|
|
1899
|
-
else if (s[4] === '11000234105') result = 'Austrian Edition';
|
|
1900
|
-
else if (s[4] === '11000172109') result = 'Belgian Edition';
|
|
1901
|
-
else if (s[4] === '20621000087109') result = 'Canadian English Edition';
|
|
1902
|
-
else if (s[4] === '20611000087101') result = 'Canadian Canadian French Edition';
|
|
1903
|
-
else if (s[4] === '11000279109') result = 'Czech Edition';
|
|
1904
|
-
else if (s[4] === '554471000005108') result = 'Danish Edition';
|
|
1905
|
-
else if (s[4] === '11000181102') result = 'Estonian Edition';
|
|
1906
|
-
else if (s[4] === '11000229106') result = 'Finnish Edition';
|
|
1907
|
-
else if (s[4] === '11000274103') result = 'German Edition';
|
|
1908
|
-
else if (s[4] === '1121000189102') result = 'Indian Edition';
|
|
1909
|
-
else if (s[4] === '827022005') result = 'IPS Terminology';
|
|
1910
|
-
else if (s[4] === '11000220105') result = 'Irish Edition';
|
|
1911
|
-
else if (s[4] === '11000146104') result = 'Netherlands Edition';
|
|
1912
|
-
else if (s[4] === '21000210109') result = 'New Zealand Edition';
|
|
1913
|
-
else if (s[4] === '51000202101') result = 'Norwegian Edition';
|
|
1914
|
-
else if (s[4] === '11000267109') result = 'Republic of Korea Edition (South Korea)';
|
|
1915
|
-
else if (s[4] === '900000001000122104') result = 'Spanish National Edition';
|
|
1916
|
-
else if (s[4] === '45991000052106') result = 'Swedish Edition';
|
|
1917
|
-
else if (s[4] === '2011000195101') result = 'Swiss Edition';
|
|
1918
|
-
else if (s[4] === '83821000000107') result = 'UK Edition';
|
|
1919
|
-
else if (s[4] === '999000021000000109') result = 'UK Clinical Edition';
|
|
1920
|
-
else if (s[4] === '5631000179106') result = 'Uruguay Edition';
|
|
1921
|
-
else if (s[4] === '21000325107') result = 'Chilean Edition';
|
|
1922
|
-
else if (s[4] === '731000124108') result = 'US Edition';
|
|
1923
|
-
else if (s[4] === '5991000124107') result = 'US Edition (with ICD-10-CM maps)';
|
|
1924
|
-
}
|
|
1925
|
-
return result;
|
|
1926
|
-
}
|
|
1927
|
-
}
|
|
1928
1940
|
|
|
1929
1941
|
|
|
1930
1942
|
function toText(st, sep) {
|
|
@@ -1990,8 +2002,7 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
1990
2002
|
// this is actually handled in the inner method
|
|
1991
2003
|
}
|
|
1992
2004
|
} else {
|
|
1993
|
-
return res.status(error.statusCode || 500).json(
|
|
1994
|
-
'error', error.issueCode || 'exception', error.message));
|
|
2005
|
+
return res.status(error.statusCode || 500).json(outcomeFromError(error));
|
|
1995
2006
|
}
|
|
1996
2007
|
|
|
1997
2008
|
}
|
|
@@ -2099,8 +2110,7 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2099
2110
|
} catch (error) {
|
|
2100
2111
|
this.log.error(error);
|
|
2101
2112
|
debugLog(error);
|
|
2102
|
-
return res.status(error.statusCode || 500).json(
|
|
2103
|
-
'error', error.issueCode || 'exception', error.message));
|
|
2113
|
+
return res.status(error.statusCode || 500).json(outcomeFromError(error));
|
|
2104
2114
|
}
|
|
2105
2115
|
}
|
|
2106
2116
|
|
|
@@ -2125,8 +2135,7 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2125
2135
|
op.addIssue(error);
|
|
2126
2136
|
return res.status(error.statusCode || 500).json(op.jsonObj);
|
|
2127
2137
|
} else {
|
|
2128
|
-
return res.status(error.statusCode || 500).json(
|
|
2129
|
-
'error', error.issueCode || 'exception', error.message));
|
|
2138
|
+
return res.status(error.statusCode || 500).json(outcomeFromError(error));
|
|
2130
2139
|
}
|
|
2131
2140
|
}
|
|
2132
2141
|
}
|
|
@@ -2200,8 +2209,7 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2200
2209
|
} catch (error) {
|
|
2201
2210
|
this.log.error(error);
|
|
2202
2211
|
debugLog(error);
|
|
2203
|
-
return res.status(error.statusCode || 500).json(
|
|
2204
|
-
'error', error.issueCode || 'exception', error.message));
|
|
2212
|
+
return res.status(error.statusCode || 500).json(outcomeFromError(error));
|
|
2205
2213
|
}
|
|
2206
2214
|
}
|
|
2207
2215
|
|
|
@@ -2541,18 +2549,9 @@ class ValidateWorker extends TerminologyWorker {
|
|
|
2541
2549
|
/**
|
|
2542
2550
|
* Build an OperationOutcome
|
|
2543
2551
|
*/
|
|
2544
|
-
operationOutcome(severity, code, message) {
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
issue: [{
|
|
2548
|
-
severity,
|
|
2549
|
-
code,
|
|
2550
|
-
details: {
|
|
2551
|
-
text: message
|
|
2552
|
-
},
|
|
2553
|
-
diagnostics: message
|
|
2554
|
-
}]
|
|
2555
|
-
};
|
|
2552
|
+
operationOutcome(severity, code, message, txIssueType = null) {
|
|
2553
|
+
// the shared builder, so that every outcome has details.text and a tx-issue-type coding
|
|
2554
|
+
return buildOperationOutcome(severity, code, message, txIssueType);
|
|
2556
2555
|
}
|
|
2557
2556
|
|
|
2558
2557
|
|
package/tx/workers/worker.js
CHANGED
|
@@ -1062,8 +1062,77 @@ class TerminologyWorker {
|
|
|
1062
1062
|
}
|
|
1063
1063
|
}
|
|
1064
1064
|
|
|
1065
|
+
|
|
1066
|
+
/**
|
|
1067
|
+
* Choose the message id to use when reporting an unknown code, based on the
|
|
1068
|
+
* code system and whether a version is known. SNOMED CT gets its own message
|
|
1069
|
+
* because the edition is named in the message (see SCTVersion below)
|
|
1070
|
+
* @param {string} url - code system url
|
|
1071
|
+
* @param {string} version - code system version (may be null)
|
|
1072
|
+
* @returns {string} message id
|
|
1073
|
+
*/
|
|
1074
|
+
function Unknown_Code_in_VersionSCT(url, version) {
|
|
1075
|
+
if (url === 'http://snomed.info/sct') {
|
|
1076
|
+
return 'Unknown_Code_in_Version_SCT';
|
|
1077
|
+
} else if (version) {
|
|
1078
|
+
return 'Unknown_Code_in_Version';
|
|
1079
|
+
} else {
|
|
1080
|
+
return 'Unknown_Code_in';
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
/**
|
|
1085
|
+
* Human readable name for the SNOMED CT edition in a SNOMED CT version URI.
|
|
1086
|
+
* Returns '' for anything that isn't a versioned SNOMED CT reference
|
|
1087
|
+
* @param {string} url - code system url
|
|
1088
|
+
* @param {string} ver - code system version
|
|
1089
|
+
* @returns {string} edition name, or ''
|
|
1090
|
+
*/
|
|
1091
|
+
function SCTVersion(url, ver) {
|
|
1092
|
+
if (url !== 'http://snomed.info/sct' || !ver) {
|
|
1093
|
+
return '';
|
|
1094
|
+
} else {
|
|
1095
|
+
let result = 'unknown';
|
|
1096
|
+
let s = ver.split('/');
|
|
1097
|
+
if (s.length >= 5) {
|
|
1098
|
+
if (s[4] === '900000000000207008') result = 'International Edition';
|
|
1099
|
+
else if (s[4] === '449081005') result = 'International Spanish Edition';
|
|
1100
|
+
else if (s[4] === '11000221109') result = 'Argentinian Edition';
|
|
1101
|
+
else if (s[4] === '32506021000036107') result = 'Australian Edition (with drug extension)';
|
|
1102
|
+
else if (s[4] === '11000234105') result = 'Austrian Edition';
|
|
1103
|
+
else if (s[4] === '11000172109') result = 'Belgian Edition';
|
|
1104
|
+
else if (s[4] === '20621000087109') result = 'Canadian English Edition';
|
|
1105
|
+
else if (s[4] === '20611000087101') result = 'Canadian Canadian French Edition';
|
|
1106
|
+
else if (s[4] === '11000279109') result = 'Czech Edition';
|
|
1107
|
+
else if (s[4] === '554471000005108') result = 'Danish Edition';
|
|
1108
|
+
else if (s[4] === '11000181102') result = 'Estonian Edition';
|
|
1109
|
+
else if (s[4] === '11000229106') result = 'Finnish Edition';
|
|
1110
|
+
else if (s[4] === '11000274103') result = 'German Edition';
|
|
1111
|
+
else if (s[4] === '1121000189102') result = 'Indian Edition';
|
|
1112
|
+
else if (s[4] === '827022005') result = 'IPS Terminology';
|
|
1113
|
+
else if (s[4] === '11000220105') result = 'Irish Edition';
|
|
1114
|
+
else if (s[4] === '11000146104') result = 'Netherlands Edition';
|
|
1115
|
+
else if (s[4] === '21000210109') result = 'New Zealand Edition';
|
|
1116
|
+
else if (s[4] === '51000202101') result = 'Norwegian Edition';
|
|
1117
|
+
else if (s[4] === '11000267109') result = 'Republic of Korea Edition (South Korea)';
|
|
1118
|
+
else if (s[4] === '900000001000122104') result = 'Spanish National Edition';
|
|
1119
|
+
else if (s[4] === '45991000052106') result = 'Swedish Edition';
|
|
1120
|
+
else if (s[4] === '2011000195101') result = 'Swiss Edition';
|
|
1121
|
+
else if (s[4] === '83821000000107') result = 'UK Edition';
|
|
1122
|
+
else if (s[4] === '999000021000000109') result = 'UK Clinical Edition';
|
|
1123
|
+
else if (s[4] === '5631000179106') result = 'Uruguay Edition';
|
|
1124
|
+
else if (s[4] === '21000325107') result = 'Chilean Edition';
|
|
1125
|
+
else if (s[4] === '731000124108') result = 'US Edition';
|
|
1126
|
+
else if (s[4] === '5991000124107') result = 'US Edition (with ICD-10-CM maps)';
|
|
1127
|
+
}
|
|
1128
|
+
return result;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1065
1132
|
module.exports = {
|
|
1066
1133
|
TerminologyWorker,
|
|
1067
1134
|
TerminologySetupError,
|
|
1068
|
-
CACHE_ID_HEADER
|
|
1135
|
+
CACHE_ID_HEADER,
|
|
1136
|
+
Unknown_Code_in_VersionSCT,
|
|
1137
|
+
SCTVersion
|
|
1069
1138
|
};
|