fhirsmith 0.14.0 → 0.14.2
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 +64 -0
- package/README.md +3 -2
- package/config-template.json +20 -0
- package/library/fhir-openapi-schema.js +441 -0
- package/library/html-server.js +9 -3
- package/library/languages.js +15 -0
- package/library/npm-audit.js +265 -0
- package/library/openapi-doc.js +557 -0
- package/library/same-origin.js +62 -0
- package/library/utilities.js +21 -0
- package/nginx.md +17 -9
- package/package.json +10 -4
- package/packages/openapi.js +13 -0
- package/packages/openapi.yaml +603 -0
- package/packages/packages-template.html +4 -2
- package/packages/packages.js +286 -81
- package/packages/readme.md +120 -0
- package/publisher/publisher-template.html +1 -0
- package/publisher/publisher.js +95 -42
- package/registry/api.js +117 -41
- package/registry/crawler.js +24 -0
- package/registry/fhirsmith-releases.js +170 -0
- package/registry/model.js +10 -0
- package/registry/openapi.js +13 -0
- package/registry/openapi.yaml +311 -0
- package/registry/readme.md +107 -208
- package/registry/registry-template.html +4 -1
- package/registry/registry.js +183 -20
- package/server.js +25 -6
- package/testing/openapi-schemas.config.js +58 -0
- package/testing/openapi-schemas.json +1920 -0
- package/testing/openapi.js +15 -0
- package/testing/openapi.yaml +273 -0
- package/testing/readme.md +75 -1
- package/testing/render.js +270 -29
- package/testing/search.js +1 -1
- package/testing/store.js +152 -8
- package/testing/testing-template.html +2 -0
- package/testing/testing.js +307 -9
- package/translations/Messages.properties +1 -0
- package/tx/README.md +58 -0
- package/tx/cm/cm-database.js +10 -1
- package/tx/cm/cm-package.js +8 -1
- package/tx/cs/cs-rxnorm.js +23 -6
- package/tx/cs/cs-snomed.js +3 -72
- package/tx/html/tx-template.html +2 -0
- package/tx/library/canonical-resource.js +50 -1
- package/tx/library/codesystem.js +1 -0
- package/tx/library/conceptmap.js +1 -0
- package/tx/library/valueset.js +16 -0
- package/tx/library.js +17 -2
- package/tx/openapi-operations.js +363 -0
- package/tx/openapi-schemas.config.js +118 -0
- package/tx/openapi-schemas.json +7903 -0
- package/tx/openapi.js +289 -0
- package/tx/openapi.yaml +151 -0
- package/tx/operation-context.js +15 -0
- package/tx/params.js +3 -12
- package/tx/provider.js +10 -1
- package/tx/sct/editions.js +70 -0
- package/tx/tx-html.js +6 -0
- package/tx/tx.js +55 -0
- package/tx/vs/vs-database.js +14 -5
- package/tx/vs/vs-package.js +10 -0
- package/tx/workers/expand.js +37 -18
- package/tx/workers/metadata.js +0 -13
- package/tx/workers/translate.js +25 -1
- package/tx/workers/validate.js +74 -24
- package/tx/workers/worker.js +32 -43
- package/utilities/extract-schema-json.js +167 -0
- package/utilities/generate-openapi-schemas.js +49 -0
- package/utilities/submit-test-report.js +1 -1
- package/xig/xig.js +2 -4
package/tx/README.md
CHANGED
|
@@ -148,6 +148,64 @@ The file is read from disk per request, so editing it is reflected immediately -
|
|
|
148
148
|
this means the published file can be ahead of what the running server actually loaded, if it
|
|
149
149
|
has been edited since startup.
|
|
150
150
|
|
|
151
|
+
## OpenAPI description
|
|
152
|
+
|
|
153
|
+
Each R5 endpoint is described by an OpenAPI 3.1 spec (the R4 and R3 endpoints work the same
|
|
154
|
+
way, with that version's resources, and aren't described separately):
|
|
155
|
+
|
|
156
|
+
| URL | |
|
|
157
|
+
|---|---|
|
|
158
|
+
| `/{path}/openapi` | Browsable reference (HTML, in the tx page template), with a "try it" form for each GET operation |
|
|
159
|
+
| `/{path}/openapi.json` | The spec as JSON |
|
|
160
|
+
| `/{path}/openapi.yaml` | The spec as YAML |
|
|
161
|
+
|
|
162
|
+
`servers` is the endpoint. Every response from an R5 endpoint carries a
|
|
163
|
+
`Link: </{path}/openapi.json>; rel="service-desc", </{path}/openapi>; rel="service-doc"` header
|
|
164
|
+
(RFC 8631), the tx pages carry the matching `<link>` element and an **API** item in the
|
|
165
|
+
navigation bar, and the server home page links to it. The HTML pages (`/`, `/op.html`, `/ecl`,
|
|
166
|
+
`/problems.html`, `/info`, `/library`) aren't described.
|
|
167
|
+
|
|
168
|
+
The spec is in three parts, merged when it's served ([openapi.js](openapi.js)):
|
|
169
|
+
|
|
170
|
+
* [openapi.yaml](openapi.yaml) - the overview, `/metadata`, `/$versions`, and the shared
|
|
171
|
+
components, written by hand
|
|
172
|
+
* [openapi-operations.js](openapi-operations.js) - the terminology operations, as data: their
|
|
173
|
+
in and out parameters, with types, cardinalities and descriptions. openapi.js makes the paths
|
|
174
|
+
from them (type and instance level; GET with the primitive parameters in the query, POST with
|
|
175
|
+
a Parameters resource or a form) and a table of the parameters for each. The parameters
|
|
176
|
+
TxParameters reads for most operations (tx-resource, cache-id, the version rules,
|
|
177
|
+
displayLanguage, ...) are listed once, in the overview. The read and search paths are made
|
|
178
|
+
there too, from the search worker's parameters
|
|
179
|
+
* [openapi-schemas.json](openapi-schemas.json) - the FHIR R5 schemas, generated from the
|
|
180
|
+
StructureDefinitions by `library/fhir-openapi-schema.js`, as configured in
|
|
181
|
+
[openapi-schemas.config.js](openapi-schemas.config.js)
|
|
182
|
+
|
|
183
|
+
The schemas are documentation: they describe what the server handles and returns, but the
|
|
184
|
+
server doesn't reject content outside them. They describe:
|
|
185
|
+
|
|
186
|
+
* CodeSystem, ValueSet, ConceptMap, OperationOutcome, Parameters, Bundle, CapabilityStatement
|
|
187
|
+
and TerminologyCapabilities, closed, with the `_x` siblings of primitive elements and
|
|
188
|
+
`modifierExtension` wherever FHIR allows them
|
|
189
|
+
* contained resources only in a ValueSet, and only ValueSets
|
|
190
|
+
* extension and parameter values: the primitive types (not `base64Binary`), `Coding` and
|
|
191
|
+
`CodeableConcept`
|
|
192
|
+
* resources in a Bundle or a Parameters: CodeSystem, ValueSet, ConceptMap, OperationOutcome -
|
|
193
|
+
and Parameters, in a Parameters, for `$batch-validate-code` and `profile`. Never a Bundle
|
|
194
|
+
* OperationOutcome issues with `details.text`, and (except for information) a tx-issue-type
|
|
195
|
+
coding
|
|
196
|
+
|
|
197
|
+
After changing the operations, `openapi.js` picks them up. After changing the schema config,
|
|
198
|
+
regenerate (this needs `hl7.fhir.r5.core#5.0.0` in the terminology cache):
|
|
199
|
+
|
|
200
|
+
node utilities/generate-openapi-schemas.js tx
|
|
201
|
+
|
|
202
|
+
`tests/tx/openapi.test.js` fails if a route isn't described (or excluded, with a reason), if a
|
|
203
|
+
worker or TxParameters reads a parameter that isn't described - or a described one isn't read
|
|
204
|
+
anywhere - or if the generated schemas are out of date. It also validates what the server
|
|
205
|
+
actually returns (metadata, read and search, `$expand`, `$validate-code`, `$lookup`,
|
|
206
|
+
`$subsumes`, `$batch-validate-code`, `$versions`, `$cache-control`, an error) against the
|
|
207
|
+
schemas, using ajv. So a new parameter needs adding to openapi-operations.js.
|
|
208
|
+
|
|
151
209
|
## Library Configuration
|
|
152
210
|
|
|
153
211
|
The library - the source to load - is configured using a YAML file.
|
package/tx/cm/cm-database.js
CHANGED
|
@@ -349,7 +349,16 @@ class ConceptMapDatabase {
|
|
|
349
349
|
this.cmCount = rows.length;
|
|
350
350
|
|
|
351
351
|
for (const row of rows) {
|
|
352
|
-
|
|
352
|
+
let conceptMap;
|
|
353
|
+
try {
|
|
354
|
+
conceptMap = new ConceptMap(JSON.parse(row.content));
|
|
355
|
+
} catch (e) {
|
|
356
|
+
// one unacceptable concept map must not take the rest with it
|
|
357
|
+
require('../../library/logger').getInstance().child({ module: 'tx' })
|
|
358
|
+
.warn(`ConceptMap database: skipping ConceptMap ${row.url || row.id}: ${e.message}`);
|
|
359
|
+
this.cmCount--;
|
|
360
|
+
continue;
|
|
361
|
+
}
|
|
353
362
|
|
|
354
363
|
// Store by URL and id alone
|
|
355
364
|
conceptMapMap.set(row.url, conceptMap);
|
package/tx/cm/cm-package.js
CHANGED
|
@@ -63,7 +63,14 @@ class PackageConceptMapProvider extends AbstractConceptMapProvider {
|
|
|
63
63
|
for (const entry of conceptMapEntries) {
|
|
64
64
|
const conceptMap = await this.packageLoader.loadFile(entry);
|
|
65
65
|
if (conceptMap.url) {
|
|
66
|
-
|
|
66
|
+
try {
|
|
67
|
+
conceptMaps.push(new ConceptMap(conceptMap, this.packageLoader.fhirVersion())); // get in converted to R5 format
|
|
68
|
+
} catch (e) {
|
|
69
|
+
// one unacceptable concept map (e.g. one with contained resources, which the
|
|
70
|
+
// server doesn't support) must not stop the rest of the package loading
|
|
71
|
+
require('../../library/logger').getInstance().child({ module: 'tx' })
|
|
72
|
+
.warn(`Package ${this.packageLoader.pid ? this.packageLoader.pid() : ''}: skipping ConceptMap ${conceptMap.url}: ${e.message}`);
|
|
73
|
+
}
|
|
67
74
|
}
|
|
68
75
|
}
|
|
69
76
|
|
package/tx/cs/cs-rxnorm.js
CHANGED
|
@@ -36,12 +36,27 @@ class RxNormPrep {
|
|
|
36
36
|
|
|
37
37
|
// Iterator context
|
|
38
38
|
class RxNormIteratorContext {
|
|
39
|
-
|
|
39
|
+
/**
|
|
40
|
+
* @param {string|null} query - the SQL that produces the rows, or null for an iterator with no rows
|
|
41
|
+
* @param {object} params - the query parameters
|
|
42
|
+
* @param {number} [total] - the number of rows the query will return, if known. The expander
|
|
43
|
+
* reads it to refuse an over-limit whole-code-system expansion before it is materialised
|
|
44
|
+
*/
|
|
45
|
+
constructor(query, params = {}, total = undefined) {
|
|
40
46
|
this.query = query;
|
|
41
47
|
this.params = params;
|
|
42
48
|
this.cursor = 0;
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
if (query) {
|
|
50
|
+
this.results = null;
|
|
51
|
+
this.executed = false;
|
|
52
|
+
this.total = total;
|
|
53
|
+
} else {
|
|
54
|
+
// nothing to run: an empty statement isn't a no-op in node-sqlite3, it fails with
|
|
55
|
+
// "SQLITE_MISUSE: not an error"
|
|
56
|
+
this.results = [];
|
|
57
|
+
this.executed = true;
|
|
58
|
+
this.total = 0;
|
|
59
|
+
}
|
|
45
60
|
}
|
|
46
61
|
|
|
47
62
|
more() {
|
|
@@ -288,10 +303,12 @@ class RxNormServices extends CodeSystemProvider {
|
|
|
288
303
|
if (!context) {
|
|
289
304
|
// Iterate all codes
|
|
290
305
|
const query = `SELECT ${this.getCodeField()}, STR FROM rxnconso WHERE SAB = ? AND TTY <> 'SY' ORDER BY ${this.getCodeField()}`;
|
|
291
|
-
return new RxNormIteratorContext(query, { sab: this.getSAB() });
|
|
306
|
+
return new RxNormIteratorContext(query, { sab: this.getSAB() }, this.totalCodeCount);
|
|
292
307
|
} else {
|
|
293
|
-
// No hierarchical iteration for specific contexts in this implementation
|
|
294
|
-
|
|
308
|
+
// No hierarchical iteration for specific contexts in this implementation. The expander
|
|
309
|
+
// asks every concept for its children (includeCodeAndDescendants), so this has to be an
|
|
310
|
+
// iterator with no rows - not one with an empty query, which sqlite rejects
|
|
311
|
+
return new RxNormIteratorContext(null, {});
|
|
295
312
|
}
|
|
296
313
|
}
|
|
297
314
|
|
package/tx/cs/cs-snomed.js
CHANGED
|
@@ -16,6 +16,7 @@ const {ConceptMap} = require("../library/conceptmap");
|
|
|
16
16
|
const {ECLLexer, ECLParser, ECLNodeType, ECLTokenType} = require("../sct/ecl");
|
|
17
17
|
const {Issue} = require("../library/operation-outcome");
|
|
18
18
|
const {debugLog} = require("../operation-context");
|
|
19
|
+
const {editionName, editionCode} = require("../sct/editions");
|
|
19
20
|
const {SnomedTextIndex, rankMatches, filterTokens} = require("../sct/text-index");
|
|
20
21
|
const Logger = require("../../library/logger");
|
|
21
22
|
|
|
@@ -3038,81 +3039,11 @@ class SnomedServicesFactory extends CodeSystemFactoryProvider {
|
|
|
3038
3039
|
}
|
|
3039
3040
|
|
|
3040
3041
|
function getEditionName(edition) {
|
|
3041
|
-
|
|
3042
|
-
'900000000000207008': 'International Edition',
|
|
3043
|
-
// The terminology-ecosystem test distribution, published under xsct - see the
|
|
3044
|
-
// tx-ecosystem IG, which requires a server claiming the snomed mode to load it.
|
|
3045
|
-
'31000003106': 'Test Edition',
|
|
3046
|
-
'449081005': 'International Spanish Edition',
|
|
3047
|
-
'11000221109': 'Argentinian Edition',
|
|
3048
|
-
'32506021000036107': 'Australian Edition (with drug extension)',
|
|
3049
|
-
'11000234105': 'Austrian Edition',
|
|
3050
|
-
'11000172109': 'Belgian Edition',
|
|
3051
|
-
'20621000087109': 'Canadian English Edition',
|
|
3052
|
-
'20611000087101': 'Canadian Canadian French Edition',
|
|
3053
|
-
'554471000005108': 'Danish Edition',
|
|
3054
|
-
'11000279109': 'Czech Edition',
|
|
3055
|
-
'11000181102': 'Estonian Edition',
|
|
3056
|
-
'11000229106': 'Finnish Edition',
|
|
3057
|
-
'11000274103': 'German Edition',
|
|
3058
|
-
'1121000189102': 'Indian Edition',
|
|
3059
|
-
'827022005': 'IPS Terminology',
|
|
3060
|
-
'11000220105': 'Irish Edition',
|
|
3061
|
-
'11000146104': 'Netherlands Edition',
|
|
3062
|
-
'21000210109': 'New Zealand Edition',
|
|
3063
|
-
'51000202101': 'Norwegian Edition',
|
|
3064
|
-
'11000267109': 'Republic of Korea Edition (South Korea)',
|
|
3065
|
-
'900000001000122104': 'Spanish National Edition',
|
|
3066
|
-
'45991000052106': 'Swedish Edition',
|
|
3067
|
-
'2011000195101': 'Swiss Edition',
|
|
3068
|
-
'83821000000107': 'UK Edition',
|
|
3069
|
-
'999000021000000109': 'UK Clinical Edition',
|
|
3070
|
-
'5631000179106': 'Uruguay Edition',
|
|
3071
|
-
'731000124108': 'US Edition',
|
|
3072
|
-
'21000325107': 'Chilean Edition',
|
|
3073
|
-
'5991000124107': 'US Edition (with ICD-10-CM maps)'
|
|
3074
|
-
};
|
|
3075
|
-
|
|
3076
|
-
return editionMap[edition] || 'Unknown Edition';
|
|
3042
|
+
return editionName(edition) || 'Unknown Edition';
|
|
3077
3043
|
}
|
|
3078
3044
|
|
|
3079
3045
|
function getEditionCode(edition) {
|
|
3080
|
-
|
|
3081
|
-
'900000000000207008': 'Intl',
|
|
3082
|
-
// The terminology-ecosystem test distribution, published under xsct - see the
|
|
3083
|
-
// tx-ecosystem IG, which requires a server claiming the snomed mode to load it.
|
|
3084
|
-
'31000003106': 'Test',
|
|
3085
|
-
'449081005': 'es',
|
|
3086
|
-
'11000221109': 'AR-es',
|
|
3087
|
-
'32506021000036107': 'AU+',
|
|
3088
|
-
'11000234105': 'AT',
|
|
3089
|
-
'11000172109': 'BE',
|
|
3090
|
-
'20621000087109': 'CA-en',
|
|
3091
|
-
'20611000087101': 'CA-fr',
|
|
3092
|
-
'554471000005108': 'DK',
|
|
3093
|
-
'11000279109': 'CZ',
|
|
3094
|
-
'11000181102': 'ES',
|
|
3095
|
-
'11000229106': 'FI',
|
|
3096
|
-
'11000274103': 'DE',
|
|
3097
|
-
'1121000189102': 'IN',
|
|
3098
|
-
'827022005': 'IPS',
|
|
3099
|
-
'11000220105': 'IE',
|
|
3100
|
-
'11000146104': 'NL',
|
|
3101
|
-
'21000210109': 'NZ',
|
|
3102
|
-
'51000202101': 'NO',
|
|
3103
|
-
'11000267109': 'KR',
|
|
3104
|
-
'900000001000122104': 'ES-es',
|
|
3105
|
-
'45991000052106': 'SW',
|
|
3106
|
-
'2011000195101': 'CH',
|
|
3107
|
-
'83821000000107': 'UK',
|
|
3108
|
-
'999000021000000109': 'UK-Clinical',
|
|
3109
|
-
'5631000179106': 'UR',
|
|
3110
|
-
'731000124108': 'US',
|
|
3111
|
-
'21000325107': 'CL',
|
|
3112
|
-
'5991000124107': 'US+)'
|
|
3113
|
-
};
|
|
3114
|
-
|
|
3115
|
-
return editionMap[edition] || 'Unknown Edition';
|
|
3046
|
+
return editionCode(edition) || 'Unknown Edition';
|
|
3116
3047
|
}
|
|
3117
3048
|
|
|
3118
3049
|
|
package/tx/html/tx-template.html
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
<meta charset="utf-8" http-equiv="X-UA-Compatible" content="IE=edge" />
|
|
11
11
|
|
|
12
12
|
<link rel="stylesheet" href="/fhir.css"/>
|
|
13
|
+
[%api-head%]
|
|
13
14
|
|
|
14
15
|
<!-- Bootstrap core CSS -->
|
|
15
16
|
<link rel="stylesheet" href="/assets/css/bootstrap.css"/>
|
|
@@ -91,6 +92,7 @@
|
|
|
91
92
|
<a href="[%endpoint-path%]/op.html" style="color: gold">Operations</a> |
|
|
92
93
|
<a href="[%endpoint-path%]/ecl" style="color: gold">ECL</a> |
|
|
93
94
|
<a href="[%endpoint-path%]/problems.html" style="color: gold">Problems</a> |
|
|
95
|
+
[%api-link%]
|
|
94
96
|
[%library-link%]
|
|
95
97
|
|
|
96
98
|
</div>
|
|
@@ -1,4 +1,45 @@
|
|
|
1
1
|
const {VersionUtilities, VersionPrecision} = require("../../library/version-utilities");
|
|
2
|
+
const {Issue} = require("./operation-outcome");
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Contained resources. The terminology server supports exactly one use of them: a ValueSet
|
|
6
|
+
* that contains ValueSets, which its compose refers to as #id. Any other contained resource
|
|
7
|
+
* - a contained CodeSystem or ConceptMap, anything contained in a CodeSystem or ConceptMap,
|
|
8
|
+
* or a contained ValueSet that itself contains something - is rejected, whatever path the
|
|
9
|
+
* resource arrives by (request, tx-resource, cache, package). Supporting them would mean
|
|
10
|
+
* resolving references to them everywhere, and the terminology ecosystem only requires
|
|
11
|
+
* servers to support contained value sets in value sets.
|
|
12
|
+
*
|
|
13
|
+
* @param {Object} jsonObj - the resource (R5 form)
|
|
14
|
+
* @throws {Issue} if the resource contains anything not supported
|
|
15
|
+
*/
|
|
16
|
+
function checkContained(jsonObj) {
|
|
17
|
+
const contained = jsonObj ? jsonObj.contained : undefined;
|
|
18
|
+
if (contained === undefined || contained === null) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const type = jsonObj.resourceType;
|
|
22
|
+
const which = `${type}${jsonObj.id ? '/' + jsonObj.id : ''}${jsonObj.url ? ' (' + jsonObj.url + ')' : ''}`;
|
|
23
|
+
if (!Array.isArray(contained)) {
|
|
24
|
+
throw new Issue('error', 'structure', `${type}.contained`, 'CONTAINED_RESOURCE_NOT_SUPPORTED',
|
|
25
|
+
`${which}: contained must be an array`, 'invalid-data', 400);
|
|
26
|
+
}
|
|
27
|
+
contained.forEach((c, i) => {
|
|
28
|
+
const ct = c && typeof c === 'object' ? c.resourceType : undefined;
|
|
29
|
+
let problem = null;
|
|
30
|
+
if (type !== 'ValueSet') {
|
|
31
|
+
problem = `${which} contains a ${ct || 'resource'}: this server only supports ValueSets contained in a ValueSet`;
|
|
32
|
+
} else if (ct !== 'ValueSet') {
|
|
33
|
+
problem = `${which} contains a ${ct || 'resource with no resourceType'}: this server only supports ValueSets contained in a ValueSet`;
|
|
34
|
+
} else if (Array.isArray(c.contained) && c.contained.length > 0) {
|
|
35
|
+
problem = `${which}: the contained ValueSet${c.id ? ' #' + c.id : ''} itself contains resources, which is not allowed`;
|
|
36
|
+
}
|
|
37
|
+
if (problem) {
|
|
38
|
+
throw new Issue('error', 'not-supported', `${type}.contained[${i}]`, 'CONTAINED_RESOURCE_NOT_SUPPORTED',
|
|
39
|
+
problem, 'not-supported', 400);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}
|
|
2
43
|
|
|
3
44
|
/**
|
|
4
45
|
* Base class for metadata resources to provide common interface
|
|
@@ -31,6 +72,14 @@ class CanonicalResource {
|
|
|
31
72
|
this.fhirVersion = fhirVersion;
|
|
32
73
|
}
|
|
33
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Rejects contained resources this server doesn't support - see checkContained.
|
|
77
|
+
* Subclasses call this once the resource is in R5 form.
|
|
78
|
+
*/
|
|
79
|
+
checkContained() {
|
|
80
|
+
checkContained(this.jsonObj);
|
|
81
|
+
}
|
|
82
|
+
|
|
34
83
|
get resourceType() {
|
|
35
84
|
return this.jsonObj.resourceType;
|
|
36
85
|
}
|
|
@@ -163,4 +212,4 @@ class CanonicalResource {
|
|
|
163
212
|
}
|
|
164
213
|
}
|
|
165
214
|
|
|
166
|
-
module.exports = { CanonicalResource };
|
|
215
|
+
module.exports = { CanonicalResource, checkContained };
|
package/tx/library/codesystem.js
CHANGED
package/tx/library/conceptmap.js
CHANGED
|
@@ -19,6 +19,7 @@ class ConceptMap extends CanonicalResource {
|
|
|
19
19
|
// Convert to R5 format internally (modifies input for performance)
|
|
20
20
|
this.jsonObj = conceptMapToR5(jsonObj, fhirVersion);
|
|
21
21
|
this.validate();
|
|
22
|
+
this.checkContained();
|
|
22
23
|
this.id = this.jsonObj.id;
|
|
23
24
|
// Precalculated at construction so callers (e.g. the resource cache) have a
|
|
24
25
|
// cheap O(1) sense of how large this resource is.
|
package/tx/library/valueset.js
CHANGED
|
@@ -25,12 +25,28 @@ class ValueSet extends CanonicalResource {
|
|
|
25
25
|
// Convert to R5 format internally (modifies input for performance)
|
|
26
26
|
this.jsonObj = valueSetToR5(jsonObj, fhirVersion);
|
|
27
27
|
this.validate();
|
|
28
|
+
this.checkContained();
|
|
28
29
|
this.buildMaps();
|
|
29
30
|
// Precalculated at construction so callers (e.g. the resource cache) have a
|
|
30
31
|
// cheap O(1) sense of how large this resource is.
|
|
31
32
|
this._conceptCount = ValueSet._countConcepts(this.jsonObj, this._containsCount);
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
/**
|
|
36
|
+
* What circular reference detection knows this value set by: its versioned url, or, for
|
|
37
|
+
* a contained value set (see TerminologyWorker.findValueSet), its container's key plus
|
|
38
|
+
* #id - a contained value set's own url, if it has one, says nothing about which
|
|
39
|
+
* container it came from. null for a value set with no url that isn't contained.
|
|
40
|
+
* @returns {string|null}
|
|
41
|
+
*/
|
|
42
|
+
get contextKey() {
|
|
43
|
+
if (this.isContained && this.container) {
|
|
44
|
+
const base = this.container.contextKey || this.container.vurl || '(unidentified)';
|
|
45
|
+
return `${base}#${this.jsonObj.id || ''}`;
|
|
46
|
+
}
|
|
47
|
+
return this.vurl || null;
|
|
48
|
+
}
|
|
49
|
+
|
|
34
50
|
/**
|
|
35
51
|
* Number of concepts this ValueSet carries: the enumerated concepts in its
|
|
36
52
|
* compose (include + exclude `concept` lists) plus any concepts in an inline
|
package/tx/library.js
CHANGED
|
@@ -664,7 +664,15 @@ class Library {
|
|
|
664
664
|
const resources = await contentLoader.getResourcesByType("CodeSystem");
|
|
665
665
|
let csc = 0;
|
|
666
666
|
for (const resource of resources) {
|
|
667
|
-
|
|
667
|
+
let cs;
|
|
668
|
+
try {
|
|
669
|
+
cs = new CodeSystem(await contentLoader.loadFile(resource, contentLoader.fhirVersion()));
|
|
670
|
+
} catch (e) {
|
|
671
|
+
// one unacceptable resource (e.g. contained resources the server doesn't support)
|
|
672
|
+
// must not stop the rest of the package loading
|
|
673
|
+
this.log.warn(`Package ${contentLoader.pid()}: skipping CodeSystem ${resource.id || resource.filename || ''}: ${e.message}`);
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
668
676
|
if (this.#isIgnored(cs.url, cs.version)) {
|
|
669
677
|
this.log.info(`Ignoring CodeSystem ${cs.url}${cs.version ? '#' + cs.version : ''} (excluded by config)`);
|
|
670
678
|
continue;
|
|
@@ -775,7 +783,14 @@ class Library {
|
|
|
775
783
|
const csEntries = await contentLoader.getResourcesByType("CodeSystem");
|
|
776
784
|
let csc = 0;
|
|
777
785
|
for (const entry of csEntries) {
|
|
778
|
-
|
|
786
|
+
let cs;
|
|
787
|
+
try {
|
|
788
|
+
cs = new CodeSystem(await contentLoader.loadFile(entry, contentLoader.fhirVersion()));
|
|
789
|
+
} catch (e) {
|
|
790
|
+
// one unacceptable resource must not stop the rest of the package loading
|
|
791
|
+
this.log.warn(`Package ${contentLoader.pid()}: skipping CodeSystem ${entry.id || entry.filename || ''}: ${e.message}`);
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
779
794
|
if (this.#isIgnored(cs.url, cs.version)) {
|
|
780
795
|
this.log.info(`Ignoring CodeSystem ${cs.url}${cs.version ? '#' + cs.version : ''} (excluded by config)`);
|
|
781
796
|
continue;
|