fhirsmith 0.11.2 → 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 +377 -2
- package/README.md +1 -1
- package/config-template.json +31 -1
- 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 +208 -30
- package/publisher/validation.js +210 -0
- package/server.js +24 -15
- package/stats-db.js +127 -0
- package/stats.js +172 -6
- package/translations/Messages.properties +5 -1
- package/tx/README.md +21 -0
- package/tx/cs/cs-api.js +30 -1
- package/tx/cs/cs-api.md +1 -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 +141 -58
- 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/conceptmap.js +39 -0
- package/tx/library/nesting-limit.js +79 -0
- package/tx/library/operation-outcome.js +97 -3
- package/tx/library/parameter-check.js +139 -0
- 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/operation-context.js +220 -10
- package/tx/params.js +226 -175
- package/tx/sct/expressions.js +677 -15
- package/tx/tx.fhir.org.yml +3 -2
- package/tx/tx.js +72 -92
- package/tx/workers/batch-validate.js +11 -18
- package/tx/workers/batch.js +16 -28
- package/tx/workers/cache-control.js +87 -17
- package/tx/workers/closure.js +3 -8
- package/tx/workers/compare.js +6 -34
- package/tx/workers/expand.js +108 -60
- package/tx/workers/lookup.js +35 -40
- 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 +472 -47
- package/tx/workers/validate.js +83 -78
- package/tx/workers/worker.js +92 -11
- package/tx/xml/xml-base.js +264 -77
- package/tx/xversion/xv-conceptmap.js +81 -0
- package/tx/xversion/xv-parameters.js +8 -0
- package/tx/xversion/xv-valueset.js +39 -1
- package/utilities/merge-audit.py +178 -0
- package/utilities/why-merge-audit.md +39 -0
- package/tx/data/snomed-subset.txt +0 -84
package/tx/workers/lookup.js
CHANGED
|
@@ -12,8 +12,9 @@ const { FhirCodeSystemProvider } = require('../cs/cs-cs');
|
|
|
12
12
|
const { Designations} = require("../library/designations");
|
|
13
13
|
const {TxParameters} = require("../params");
|
|
14
14
|
const {Parameters} = require("../library/parameters");
|
|
15
|
-
const {Issue, OperationOutcome} = require(
|
|
15
|
+
const { Issue, OperationOutcome, buildOperationOutcome, outcomeFromError } = require('../library/operation-outcome');
|
|
16
16
|
const {debugLog} = require("../operation-context");
|
|
17
|
+
const {checkAddedParameters} = require("../library/parameter-check");
|
|
17
18
|
|
|
18
19
|
class LookupWorker extends TerminologyWorker {
|
|
19
20
|
/**
|
|
@@ -48,16 +49,7 @@ class LookupWorker extends TerminologyWorker {
|
|
|
48
49
|
this.log.error(error);
|
|
49
50
|
debugLog(error);
|
|
50
51
|
req.logInfo = this.usedSources.join("|")+" - error"+(error.msgId ? " "+error.msgId : "");
|
|
51
|
-
|
|
52
|
-
const issueCode = error.issueCode || 'exception';
|
|
53
|
-
return res.status(statusCode).json({
|
|
54
|
-
resourceType: 'OperationOutcome',
|
|
55
|
-
issue: [{
|
|
56
|
-
severity: 'error',
|
|
57
|
-
code: issueCode,
|
|
58
|
-
diagnostics: error.message
|
|
59
|
-
}]
|
|
60
|
-
});
|
|
52
|
+
return res.status(error.statusCode || 500).json(outcomeFromError(error));
|
|
61
53
|
}
|
|
62
54
|
}
|
|
63
55
|
|
|
@@ -75,15 +67,7 @@ class LookupWorker extends TerminologyWorker {
|
|
|
75
67
|
this.log.error(error);
|
|
76
68
|
debugLog(error);
|
|
77
69
|
req.logInfo = this.usedSources.join("|")+" - error"+(error.msgId ? " "+error.msgId : "");
|
|
78
|
-
|
|
79
|
-
return res.status(400).json({
|
|
80
|
-
resourceType: 'OperationOutcome',
|
|
81
|
-
issue: [{
|
|
82
|
-
severity: 'error',
|
|
83
|
-
code: issueCode,
|
|
84
|
-
diagnostics: error.message
|
|
85
|
-
}]
|
|
86
|
-
});
|
|
70
|
+
return res.status(400).json(outcomeFromError(error));
|
|
87
71
|
}
|
|
88
72
|
}
|
|
89
73
|
|
|
@@ -139,8 +123,11 @@ class LookupWorker extends TerminologyWorker {
|
|
|
139
123
|
}
|
|
140
124
|
|
|
141
125
|
if (!csProvider) {
|
|
142
|
-
|
|
143
|
-
|
|
126
|
+
// params is a Map-like: params.system is always undefined, so the message read
|
|
127
|
+
// "CodeSystem not found: undefined" for every unknown system or version.
|
|
128
|
+
const coding = params.has('coding') ? params.get('coding') : null;
|
|
129
|
+
const systemUrl = params.has('system') ? params.get('system') : coding?.system;
|
|
130
|
+
const versionStr = params.has('version') ? params.get('version') : coding?.version;
|
|
144
131
|
const msg = versionStr
|
|
145
132
|
? `CodeSystem not found: ${systemUrl} version ${versionStr}`
|
|
146
133
|
: `CodeSystem not found: ${systemUrl}`;
|
|
@@ -168,8 +155,7 @@ class LookupWorker extends TerminologyWorker {
|
|
|
168
155
|
oo.addIssue(error);
|
|
169
156
|
return res.status(error.statusCode || 500).json(oo.jsonObj);
|
|
170
157
|
} else {
|
|
171
|
-
return res.status(error.statusCode || 500).json(
|
|
172
|
-
'error', error.issueCode || 'exception', error.message));
|
|
158
|
+
return res.status(error.statusCode || 500).json(outcomeFromError(error));
|
|
173
159
|
}
|
|
174
160
|
}
|
|
175
161
|
}
|
|
@@ -232,8 +218,7 @@ class LookupWorker extends TerminologyWorker {
|
|
|
232
218
|
oo.addIssue(error);
|
|
233
219
|
return res.status(error.statusCode || 500).json(oo.jsonObj);
|
|
234
220
|
} else {
|
|
235
|
-
return res.status(error.statusCode || 500).json(
|
|
236
|
-
'error', error.issueCode || 'exception', error.message));
|
|
221
|
+
return res.status(error.statusCode || 500).json(outcomeFromError(error));
|
|
237
222
|
}
|
|
238
223
|
}
|
|
239
224
|
}
|
|
@@ -270,7 +255,9 @@ class LookupWorker extends TerminologyWorker {
|
|
|
270
255
|
if (locateResult?.message) {
|
|
271
256
|
message += ' ('+locateResult.message+')';
|
|
272
257
|
}
|
|
273
|
-
|
|
258
|
+
// invalid-code, not not-found: the code system was found, the code in it was not.
|
|
259
|
+
// not-found is for a code system or value set that could not be resolved at all.
|
|
260
|
+
throw new Issue('error', 'not-found', null, null, message, 'invalid-code', 404);
|
|
274
261
|
}
|
|
275
262
|
|
|
276
263
|
const ctxt = locateResult.context;
|
|
@@ -393,8 +380,20 @@ class LookupWorker extends TerminologyWorker {
|
|
|
393
380
|
}
|
|
394
381
|
}
|
|
395
382
|
|
|
396
|
-
// Let the provider add additional properties
|
|
383
|
+
// Let the provider add additional properties. Some providers pass content
|
|
384
|
+
// through from a remote API, where a value can be any JSON at all, so what
|
|
385
|
+
// comes back is checked before it goes out: a structure where a FHIR
|
|
386
|
+
// primitive belongs is a provider bug, and shipping it would produce a
|
|
387
|
+
// response that isn't valid FHIR (and, in XML, isn't even parseable).
|
|
388
|
+
const providerParamsFrom = responseParams.length;
|
|
397
389
|
await csProvider.extendLookup(ctxt, params.properties || [], responseParams);
|
|
390
|
+
let providerName;
|
|
391
|
+
try {
|
|
392
|
+
providerName = csProvider.system();
|
|
393
|
+
} catch (e) {
|
|
394
|
+
providerName = 'unknown';
|
|
395
|
+
}
|
|
396
|
+
checkAddedParameters(responseParams, providerParamsFrom, providerName);
|
|
398
397
|
|
|
399
398
|
if (reportedSupplements) {
|
|
400
399
|
for (const supplement of reportedSupplements) {
|
|
@@ -428,21 +427,17 @@ class LookupWorker extends TerminologyWorker {
|
|
|
428
427
|
}
|
|
429
428
|
|
|
430
429
|
/**
|
|
431
|
-
* Build an OperationOutcome
|
|
430
|
+
* Build an OperationOutcome. Delegates to the shared builder so that every outcome this
|
|
431
|
+
* server emits has details.text and a tx-issue-type coding: diagnostics is stripped by
|
|
432
|
+
* the test harness, so nothing a caller needs may live there.
|
|
432
433
|
* @param {string} severity - error, warning, information
|
|
433
|
-
* @param {string} code -
|
|
434
|
-
* @param {string} message -
|
|
434
|
+
* @param {string} code - FHIR issue type
|
|
435
|
+
* @param {string} message - the human readable account of the problem
|
|
436
|
+
* @param {string} [txIssueType] - tx-issue-type; defaulted from code when not given
|
|
435
437
|
* @returns {Object} OperationOutcome resource
|
|
436
438
|
*/
|
|
437
|
-
operationOutcome(severity, code, message) {
|
|
438
|
-
return
|
|
439
|
-
resourceType: 'OperationOutcome',
|
|
440
|
-
issue: [{
|
|
441
|
-
severity,
|
|
442
|
-
code,
|
|
443
|
-
details: {text : message}
|
|
444
|
-
}]
|
|
445
|
-
};
|
|
439
|
+
operationOutcome(severity, code, message, txIssueType = null) {
|
|
440
|
+
return buildOperationOutcome(severity, code, message, txIssueType);
|
|
446
441
|
}
|
|
447
442
|
}
|
|
448
443
|
|
package/tx/workers/read.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
const { TerminologyWorker } = require('./worker');
|
|
8
8
|
const {debugLog} = require("../operation-context");
|
|
9
9
|
|
|
10
|
+
const { buildOperationOutcome } = require('../library/operation-outcome');
|
|
10
11
|
class ReadWorker extends TerminologyWorker {
|
|
11
12
|
/**
|
|
12
13
|
* @param {OperationContext} opContext - Operation context
|
|
@@ -50,27 +51,13 @@ class ReadWorker extends TerminologyWorker {
|
|
|
50
51
|
return await this.handleConceptMap(req, res, id);
|
|
51
52
|
|
|
52
53
|
default:
|
|
53
|
-
return res.status(404).json({
|
|
54
|
-
resourceType: 'OperationOutcome',
|
|
55
|
-
issue: [{
|
|
56
|
-
severity: 'error',
|
|
57
|
-
code: 'not-found',
|
|
58
|
-
diagnostics: `Unknown resource type: ${resourceType}`
|
|
59
|
-
}]
|
|
60
|
-
});
|
|
54
|
+
return res.status(404).json(buildOperationOutcome('error', 'not-found', `Unknown resource type: ${resourceType}`));
|
|
61
55
|
}
|
|
62
56
|
} catch (error) {
|
|
63
57
|
this.log.error(error);
|
|
64
58
|
debugLog(error);
|
|
65
59
|
req.logInfo = this.usedSources.join("|")+" - error"+(error.msgId ? " "+error.msgId : "");
|
|
66
|
-
return res.status(500).json(
|
|
67
|
-
resourceType: 'OperationOutcome',
|
|
68
|
-
issue: [{
|
|
69
|
-
severity: 'error',
|
|
70
|
-
code: 'exception',
|
|
71
|
-
diagnostics: error.message
|
|
72
|
-
}]
|
|
73
|
-
});
|
|
60
|
+
return res.status(500).json(buildOperationOutcome('error', 'exception', error.message));
|
|
74
61
|
}
|
|
75
62
|
}
|
|
76
63
|
|
|
@@ -127,14 +114,7 @@ class ReadWorker extends TerminologyWorker {
|
|
|
127
114
|
}
|
|
128
115
|
}
|
|
129
116
|
|
|
130
|
-
return res.status(404).json({
|
|
131
|
-
resourceType: 'OperationOutcome',
|
|
132
|
-
issue: [{
|
|
133
|
-
severity: 'error',
|
|
134
|
-
code: 'not-found',
|
|
135
|
-
diagnostics: `CodeSystem/${id} not found`
|
|
136
|
-
}]
|
|
137
|
-
});
|
|
117
|
+
return res.status(404).json(buildOperationOutcome('error', 'not-found', `CodeSystem/${id} not found`));
|
|
138
118
|
}
|
|
139
119
|
|
|
140
120
|
/**
|
|
@@ -151,14 +131,7 @@ class ReadWorker extends TerminologyWorker {
|
|
|
151
131
|
}
|
|
152
132
|
}
|
|
153
133
|
|
|
154
|
-
return res.status(404).json({
|
|
155
|
-
resourceType: 'OperationOutcome',
|
|
156
|
-
issue: [{
|
|
157
|
-
severity: 'error',
|
|
158
|
-
code: 'not-found',
|
|
159
|
-
diagnostics: `ValueSet/${id} not found`
|
|
160
|
-
}]
|
|
161
|
-
});
|
|
134
|
+
return res.status(404).json(buildOperationOutcome('error', 'not-found', `ValueSet/${id} not found`));
|
|
162
135
|
}
|
|
163
136
|
/**
|
|
164
137
|
* Handle ConceptMap read
|
|
@@ -174,14 +147,7 @@ class ReadWorker extends TerminologyWorker {
|
|
|
174
147
|
}
|
|
175
148
|
}
|
|
176
149
|
|
|
177
|
-
return res.status(404).json({
|
|
178
|
-
resourceType: 'OperationOutcome',
|
|
179
|
-
issue: [{
|
|
180
|
-
severity: 'error',
|
|
181
|
-
code: 'not-found',
|
|
182
|
-
diagnostics: `ConceptMap/${id} not found`
|
|
183
|
-
}]
|
|
184
|
-
});
|
|
150
|
+
return res.status(404).json(buildOperationOutcome('error', 'not-found', `ConceptMap/${id} not found`));
|
|
185
151
|
}
|
|
186
152
|
}
|
|
187
153
|
|
package/tx/workers/search.js
CHANGED
|
@@ -10,6 +10,7 @@ const {Utilities} = require("../../library/utilities");
|
|
|
10
10
|
const {debugLog} = require("../operation-context");
|
|
11
11
|
const {acceptsHtml} = require("../tx-html");
|
|
12
12
|
|
|
13
|
+
const { buildOperationOutcome } = require('../library/operation-outcome');
|
|
13
14
|
class SearchWorker extends TerminologyWorker {
|
|
14
15
|
/**
|
|
15
16
|
* @param {OperationContext} opContext - Operation context
|
|
@@ -127,14 +128,7 @@ class SearchWorker extends TerminologyWorker {
|
|
|
127
128
|
this.log.error(error);
|
|
128
129
|
debugLog(error);
|
|
129
130
|
req.logInfo = "error "+(error.msgId || error.className);
|
|
130
|
-
return res.status(500).json(
|
|
131
|
-
resourceType: 'OperationOutcome',
|
|
132
|
-
issue: [{
|
|
133
|
-
severity: 'error',
|
|
134
|
-
code: 'exception',
|
|
135
|
-
diagnostics: error.message
|
|
136
|
-
}]
|
|
137
|
-
});
|
|
131
|
+
return res.status(500).json(buildOperationOutcome('error', 'exception', error.message));
|
|
138
132
|
}
|
|
139
133
|
}
|
|
140
134
|
|
package/tx/workers/subsumes.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// POST /CodeSystem/{id}/$subsumes
|
|
8
8
|
//
|
|
9
9
|
|
|
10
|
-
const { TerminologyWorker } = require('./worker');
|
|
10
|
+
const { TerminologyWorker, Unknown_Code_in_VersionSCT, SCTVersion } = require('./worker');
|
|
11
11
|
const { FhirCodeSystemProvider } = require('../cs/cs-cs');
|
|
12
12
|
const {TxParameters} = require("../params");
|
|
13
13
|
const {Parameters} = require("../library/parameters");
|
|
@@ -51,8 +51,7 @@ class SubsumesWorker extends TerminologyWorker {
|
|
|
51
51
|
oo.addIssue(error);
|
|
52
52
|
return res.status(error.statusCode || 500).json(oo.jsonObj);
|
|
53
53
|
} else {
|
|
54
|
-
return res.status(error.statusCode || 500).json(this.
|
|
55
|
-
'error', error.issueCode || 'exception', error.message));
|
|
54
|
+
return res.status(error.statusCode || 500).json(this.unexpectedErrorOutcome(error));
|
|
56
55
|
}
|
|
57
56
|
}
|
|
58
57
|
}
|
|
@@ -74,8 +73,7 @@ class SubsumesWorker extends TerminologyWorker {
|
|
|
74
73
|
oo.addIssue(error);
|
|
75
74
|
return res.status(error.statusCode || 500).json(oo.jsonObj);
|
|
76
75
|
} else {
|
|
77
|
-
return res.status(error.statusCode || 500).json(this.
|
|
78
|
-
'error', error.issueCode || 'exception', error.message));
|
|
76
|
+
return res.status(error.statusCode || 500).json(this.unexpectedErrorOutcome(error));
|
|
79
77
|
}
|
|
80
78
|
}
|
|
81
79
|
}
|
|
@@ -87,13 +85,14 @@ class SubsumesWorker extends TerminologyWorker {
|
|
|
87
85
|
async handleTypeLevelSubsumes(req, res) {
|
|
88
86
|
this.deadCheck('subsumes-type-level');
|
|
89
87
|
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
//
|
|
88
|
+
// Parse the parameters first, then hand them to setupAdditionalResources. It used to be
|
|
89
|
+
// handed req.body, which meant a GET (no body) never reached the cache-id handling: when a
|
|
90
|
+
// client front-loads a suite's resources into a server-side cache the request carries no
|
|
91
|
+
// tx-resource at all, only the X-Cache-Id header, and every code system supplied that way
|
|
92
|
+
// was invisible. On a GET parseParameters turns the query string into the same Parameters
|
|
93
|
+
// shape, and the cache-id comes off the operation context
|
|
96
94
|
const params = new Parameters(this.parseParameters(req));
|
|
95
|
+
this.setupAdditionalResources(params.jsonObj);
|
|
97
96
|
const txp = new TxParameters(this.opContext.i18n.languageDefinitions, this.opContext.i18n);
|
|
98
97
|
txp.readParams(params.jsonObj);
|
|
99
98
|
|
|
@@ -101,7 +100,9 @@ class SubsumesWorker extends TerminologyWorker {
|
|
|
101
100
|
let codingA, codingB;
|
|
102
101
|
let csProvider;
|
|
103
102
|
|
|
103
|
+
let names = ['codeA', 'codeB'];
|
|
104
104
|
if (params.has('codingA') && params.has('codingB')) {
|
|
105
|
+
names = ['codingA', 'codingB'];
|
|
105
106
|
// Using codingA and codingB (only from Parameters resource)
|
|
106
107
|
codingA = params.get('codingA');
|
|
107
108
|
codingB = params.get('codingB');
|
|
@@ -138,7 +139,7 @@ class SubsumesWorker extends TerminologyWorker {
|
|
|
138
139
|
}
|
|
139
140
|
|
|
140
141
|
// Perform the subsumes check
|
|
141
|
-
const result = await this.doSubsumes(csProvider, codingA, codingB);
|
|
142
|
+
const result = await this.doSubsumes(csProvider, codingA, codingB, txp, names);
|
|
142
143
|
req.logInfo = this.usedSources.join("|")+txp.logInfo();
|
|
143
144
|
return res.status(200).json(result);
|
|
144
145
|
}
|
|
@@ -159,13 +160,9 @@ class SubsumesWorker extends TerminologyWorker {
|
|
|
159
160
|
throw new Issue('error', 'not found', null, null, `CodeSystem/${id} not found`, null, 404);
|
|
160
161
|
}
|
|
161
162
|
|
|
162
|
-
//
|
|
163
|
-
if (req.body && req.body.resourceType === 'Parameters') {
|
|
164
|
-
this.setupAdditionalResources(req.body);
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Parse parameters from request
|
|
163
|
+
// See handleTypeLevelSubsumes: parse first, so a GET reaches the cache-id handling too
|
|
168
164
|
const params = new Parameters(this.parseParameters(req));
|
|
165
|
+
this.setupAdditionalResources(params.jsonObj);
|
|
169
166
|
const txp = new TxParameters(this.opContext.i18n.languageDefinitions, this.opContext.i18n);
|
|
170
167
|
txp.readParams(params.jsonObj);
|
|
171
168
|
|
|
@@ -178,7 +175,9 @@ class SubsumesWorker extends TerminologyWorker {
|
|
|
178
175
|
// Get the codings
|
|
179
176
|
let codingA, codingB;
|
|
180
177
|
|
|
178
|
+
let names = ['codeA', 'codeB'];
|
|
181
179
|
if (params.has('codingA') && params.has('codingB')) {
|
|
180
|
+
names = ['codingA', 'codingB'];
|
|
182
181
|
codingA = params.get('codingA');
|
|
183
182
|
codingB = params.get('codingB');
|
|
184
183
|
} else if (params.has('codeA') && params.has('codeB')) {
|
|
@@ -198,7 +197,7 @@ class SubsumesWorker extends TerminologyWorker {
|
|
|
198
197
|
}
|
|
199
198
|
|
|
200
199
|
// Perform the subsumes check
|
|
201
|
-
const result = await this.doSubsumes(csProvider, codingA, codingB);
|
|
200
|
+
const result = await this.doSubsumes(csProvider, codingA, codingB, txp, names);
|
|
202
201
|
req.logInfo = this.usedSources.join("|")+txp.logInfo();
|
|
203
202
|
return res.json(result);
|
|
204
203
|
}
|
|
@@ -263,9 +262,11 @@ class SubsumesWorker extends TerminologyWorker {
|
|
|
263
262
|
* @param {CodeSystemProvider} csProvider - CodeSystem provider
|
|
264
263
|
* @param {Object} codingA - First coding
|
|
265
264
|
* @param {Object} codingB - Second coding
|
|
265
|
+
* @param {TxParameters} txp - parsed parameters (for languages)
|
|
266
|
+
* @param {Array<string>} names - the parameters the two codes came from, for `expression`
|
|
266
267
|
* @returns {Object} Parameters resource with subsumes result
|
|
267
268
|
*/
|
|
268
|
-
async doSubsumes(csProvider, codingA, codingB) {
|
|
269
|
+
async doSubsumes(csProvider, codingA, codingB, txp, names = ['codeA', 'codeB']) {
|
|
269
270
|
this.deadCheck('doSubsumes');
|
|
270
271
|
|
|
271
272
|
const csSystem = csProvider.system();
|
|
@@ -287,18 +288,12 @@ class SubsumesWorker extends TerminologyWorker {
|
|
|
287
288
|
// Validate both codes exist
|
|
288
289
|
const locateA = await csProvider.locate(codingA.code);
|
|
289
290
|
if (!locateA || !locateA.context) {
|
|
290
|
-
|
|
291
|
-
error.statusCode = 404;
|
|
292
|
-
error.issueCode = 'not-found';
|
|
293
|
-
throw error;
|
|
291
|
+
throw this.unknownCodeIssue(csProvider, codingA.code, names[0], locateA ? locateA.message : null, txp);
|
|
294
292
|
}
|
|
295
293
|
|
|
296
294
|
const locateB = await csProvider.locate(codingB.code);
|
|
297
295
|
if (!locateB || !locateB.context) {
|
|
298
|
-
|
|
299
|
-
error.statusCode = 404;
|
|
300
|
-
error.issueCode = 'not-found';
|
|
301
|
-
throw error;
|
|
296
|
+
throw this.unknownCodeIssue(csProvider, codingB.code, names[1], locateB ? locateB.message : null, txp);
|
|
302
297
|
}
|
|
303
298
|
|
|
304
299
|
let equal = false;
|
|
@@ -324,21 +319,43 @@ class SubsumesWorker extends TerminologyWorker {
|
|
|
324
319
|
}
|
|
325
320
|
|
|
326
321
|
/**
|
|
327
|
-
* Build
|
|
328
|
-
*
|
|
329
|
-
* @param {
|
|
330
|
-
* @param {string}
|
|
322
|
+
* Build the Issue for a code that isn't in the code system. Uses the same message ids
|
|
323
|
+
* and tx-issue-type detail code as $validate-code, so clients get consistent errors
|
|
324
|
+
* @param {CodeSystemProvider} csProvider - CodeSystem provider
|
|
325
|
+
* @param {string} code - the code that wasn't found
|
|
326
|
+
* @param {string} path - the parameter the code came from ('codeA' / 'codeB')
|
|
327
|
+
* @param {string} message - any explanation the provider gave for not finding it
|
|
328
|
+
* @param {TxParameters} txp - parsed parameters (for languages)
|
|
329
|
+
* @returns {Issue}
|
|
330
|
+
*/
|
|
331
|
+
unknownCodeIssue(csProvider, code, path, message, txp) {
|
|
332
|
+
const system = csProvider.system();
|
|
333
|
+
const version = csProvider.version();
|
|
334
|
+
const msgId = Unknown_Code_in_VersionSCT(system, version);
|
|
335
|
+
const langs = txp ? txp.HTTPLanguages : null;
|
|
336
|
+
const msg = this.i18n.translate(msgId, langs, [code, system, version, SCTVersion(system, version)]);
|
|
337
|
+
const issue = new Issue('error', 'code-invalid', path, msgId, msg, 'invalid-code', 404);
|
|
338
|
+
if (message) {
|
|
339
|
+
issue.withDiagnostics(message);
|
|
340
|
+
}
|
|
341
|
+
return issue;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Wrap an error we did not expect as an OperationOutcome. The message goes in details.text,
|
|
346
|
+
* not diagnostics: TxTester strips diagnostics from every issue it compares, so anything
|
|
347
|
+
* reported that way is invisible in a failing test - all you see is 'exception'
|
|
348
|
+
* @param {Error} error - the error that escaped
|
|
331
349
|
* @returns {Object} OperationOutcome resource
|
|
332
350
|
*/
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
};
|
|
351
|
+
unexpectedErrorOutcome(error) {
|
|
352
|
+
// txIssueType, when the thrower set one, becomes the details.coding - that is where
|
|
353
|
+
// a client looks for the machine-readable reason (e.g. cannot-determine).
|
|
354
|
+
const issue = new Issue('error', error.issueCode || 'exception', null, null,
|
|
355
|
+
error.message || String(error), error.txIssueType || null, error.statusCode || 500);
|
|
356
|
+
const oo = new OperationOutcome();
|
|
357
|
+
oo.addIssue(issue);
|
|
358
|
+
return oo.jsonObj;
|
|
342
359
|
}
|
|
343
360
|
}
|
|
344
361
|
|