fhirsmith 0.3.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 +42 -0
- package/FHIRsmith.png +0 -0
- package/README.md +277 -0
- package/config-template.json +144 -0
- package/library/folder-setup.js +58 -0
- package/library/html-server.js +166 -0
- package/library/html.js +835 -0
- package/library/i18nsupport.js +259 -0
- package/library/languages.js +779 -0
- package/library/logger-telnet.js +205 -0
- package/library/logger.js +279 -0
- package/library/package-manager.js +876 -0
- package/library/utilities.js +196 -0
- package/library/version-utilities.js +1056 -0
- package/npmprojector/config-example.json +13 -0
- package/npmprojector/indexer.js +394 -0
- package/npmprojector/npmprojector.js +395 -0
- package/npmprojector/readme.md +174 -0
- package/npmprojector/watcher.js +335 -0
- package/package.json +119 -0
- package/packages/package-crawler.js +846 -0
- package/packages/packages-template.html +126 -0
- package/packages/packages.js +2838 -0
- package/passwords.ini +2 -0
- package/publisher/publisher-template.html +208 -0
- package/publisher/publisher.js +2167 -0
- package/publisher/task-draft.js +458 -0
- package/registry/api.js +735 -0
- package/registry/crawler.js +637 -0
- package/registry/model.js +513 -0
- package/registry/readme.md +243 -0
- package/registry/registry-data.json +121015 -0
- package/registry/registry-template.html +126 -0
- package/registry/registry.js +1395 -0
- package/registry/test-runner.js +237 -0
- package/root-template.html +124 -0
- package/server.js +524 -0
- package/shl/private-key.pem +5 -0
- package/shl/public-key.pem +18 -0
- package/shl/shl.js +1125 -0
- package/shl/vhl.js +69 -0
- package/static/FHIRsmith128.png +0 -0
- package/static/FHIRsmith16.png +0 -0
- package/static/FHIRsmith32.png +0 -0
- package/static/FHIRsmith64.png +0 -0
- package/static/assets/css/bootstrap-fhir.css +5302 -0
- package/static/assets/css/bootstrap-glyphicons.css +2 -0
- package/static/assets/css/bootstrap.css +4097 -0
- package/static/assets/css/jquery-ui.css +523 -0
- package/static/assets/css/jquery-ui.structure.css +863 -0
- package/static/assets/css/jquery-ui.structure.min.css +5 -0
- package/static/assets/css/jquery-ui.theme.css +439 -0
- package/static/assets/css/jquery-ui.theme.min.css +5 -0
- package/static/assets/css/jquery.ui.all.css +7 -0
- package/static/assets/css/modules.css +18 -0
- package/static/assets/css/project.css +367 -0
- package/static/assets/css/pygments-manni.css +66 -0
- package/static/assets/css/tags.css +74 -0
- package/static/assets/css/xml.css +2 -0
- package/static/assets/fonts/glyphiconshalflings-regular.eot +0 -0
- package/static/assets/fonts/glyphiconshalflings-regular.otf +0 -0
- package/static/assets/fonts/glyphiconshalflings-regular.svg +175 -0
- package/static/assets/fonts/glyphiconshalflings-regular.ttf +0 -0
- package/static/assets/fonts/glyphiconshalflings-regular.woff +0 -0
- package/static/assets/ico/apple-touch-icon-114-precomposed.png +0 -0
- package/static/assets/ico/apple-touch-icon-144-precomposed.png +0 -0
- package/static/assets/ico/apple-touch-icon-57-precomposed.png +0 -0
- package/static/assets/ico/apple-touch-icon-72-precomposed.png +0 -0
- package/static/assets/ico/favicon.ico +0 -0
- package/static/assets/ico/favicon.png +0 -0
- package/static/assets/images/fhir-logo-www.png +0 -0
- package/static/assets/images/fhir-logo.png +0 -0
- package/static/assets/images/hl7-logo.png +0 -0
- package/static/assets/images/logo_ansinew.jpg +0 -0
- package/static/assets/images/search.png +0 -0
- package/static/assets/images/stripe.png +0 -0
- package/static/assets/images/target.png +0 -0
- package/static/assets/images/tx-registry-root.gif +0 -0
- package/static/assets/images/tx-registry.png +0 -0
- package/static/assets/images/tx-server.png +0 -0
- package/static/assets/images/tx-version.png +0 -0
- package/static/assets/js/bootstrap.min.js +6 -0
- package/static/assets/js/fhir-gw.js +259 -0
- package/static/assets/js/fhir.js +2 -0
- package/static/assets/js/html5shiv.js +8 -0
- package/static/assets/js/jcookie.js +96 -0
- package/static/assets/js/jquery-ui.min.js +6 -0
- package/static/assets/js/jquery.js +10716 -0
- package/static/assets/js/jquery.min.js +2 -0
- package/static/assets/js/jquery.ui.core.js +314 -0
- package/static/assets/js/jquery.ui.draggable.js +825 -0
- package/static/assets/js/jquery.ui.mouse.js +162 -0
- package/static/assets/js/jquery.ui.resizable.js +842 -0
- package/static/assets/js/jquery.ui.widget.js +268 -0
- package/static/assets/js/json2.js +487 -0
- package/static/assets/js/jtip.js +97 -0
- package/static/assets/js/respond.min.js +6 -0
- package/static/assets/js/statuspage.js +70 -0
- package/static/assets/js/xml.js +2 -0
- package/static/dist/js/bootstrap.js +1964 -0
- package/static/favicon.png +0 -0
- package/static/fhir.css +626 -0
- package/static/icon-fhir-16.png +0 -0
- package/static/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
- package/static/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
- package/static/images/ui-bg_flat_10_000000_40x100.png +0 -0
- package/static/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- package/static/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- package/static/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- package/static/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
- package/static/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- package/static/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- package/static/images/ui-icons_222222_256x240.png +0 -0
- package/static/images/ui-icons_228ef1_256x240.png +0 -0
- package/static/images/ui-icons_ef8c08_256x240.png +0 -0
- package/static/images/ui-icons_ffd27a_256x240.png +0 -0
- package/static/images/ui-icons_ffffff_256x240.png +0 -0
- package/static/js/jquery.effects.blind.js +49 -0
- package/static/js/jquery.effects.bounce.js +78 -0
- package/static/js/jquery.effects.clip.js +54 -0
- package/static/js/jquery.effects.core.js +763 -0
- package/static/js/jquery.effects.drop.js +50 -0
- package/static/js/jquery.effects.explode.js +79 -0
- package/static/js/jquery.effects.fade.js +32 -0
- package/static/js/jquery.effects.fold.js +56 -0
- package/static/js/jquery.effects.highlight.js +50 -0
- package/static/js/jquery.effects.pulsate.js +51 -0
- package/static/js/jquery.effects.scale.js +178 -0
- package/static/js/jquery.effects.shake.js +57 -0
- package/static/js/jquery.effects.slide.js +50 -0
- package/static/js/jquery.effects.transfer.js +45 -0
- package/static/js/jquery.ui.accordion.js +611 -0
- package/static/js/jquery.ui.autocomplete.js +612 -0
- package/static/js/jquery.ui.button.js +416 -0
- package/static/js/jquery.ui.datepicker.js +1823 -0
- package/static/js/jquery.ui.dialog.js +878 -0
- package/static/js/jquery.ui.droppable.js +296 -0
- package/static/js/jquery.ui.position.js +252 -0
- package/static/js/jquery.ui.progressbar.js +109 -0
- package/static/js/jquery.ui.selectable.js +266 -0
- package/static/js/jquery.ui.slider.js +666 -0
- package/static/js/jquery.ui.sortable.js +1077 -0
- package/static/js/jquery.ui.tabs.js +758 -0
- package/stats.js +80 -0
- package/test-cache/vsac/vsac-valuesets.db +0 -0
- package/token/nginx_passport_setup.md +383 -0
- package/token/security_guide.md +294 -0
- package/token/token-template.html +330 -0
- package/token/token.js +1300 -0
- package/translations/Messages.properties +1510 -0
- package/translations/Messages_ar.properties +1399 -0
- package/translations/Messages_de.properties +836 -0
- package/translations/Messages_es.properties +737 -0
- package/translations/Messages_fr.properties +1 -0
- package/translations/Messages_ja.properties +893 -0
- package/translations/Messages_nl.properties +1357 -0
- package/translations/Messages_pt.properties +1302 -0
- package/translations/Messages_ru.properties +1 -0
- package/translations/Messages_uz.properties +1 -0
- package/translations/Messages_zh.properties +1 -0
- package/translations/rendering-phrases.properties +1128 -0
- package/translations/rendering-phrases_ar.properties +1091 -0
- package/translations/rendering-phrases_de.properties +6 -0
- package/translations/rendering-phrases_es.properties +6 -0
- package/translations/rendering-phrases_fr.properties +624 -0
- package/translations/rendering-phrases_ja.properties +21 -0
- package/translations/rendering-phrases_nl.properties +970 -0
- package/translations/rendering-phrases_pt.properties +1020 -0
- package/translations/rendering-phrases_ru.properties +1094 -0
- package/translations/rendering-phrases_uz.properties +1 -0
- package/translations/rendering-phrases_zh.properties +1 -0
- package/tx/README.md +418 -0
- package/tx/cm/cm-api.js +110 -0
- package/tx/cm/cm-database.js +735 -0
- package/tx/cm/cm-package.js +325 -0
- package/tx/cs/cs-api.js +789 -0
- package/tx/cs/cs-areacode.js +615 -0
- package/tx/cs/cs-country.js +1110 -0
- package/tx/cs/cs-cpt.js +785 -0
- package/tx/cs/cs-cs.js +1579 -0
- package/tx/cs/cs-currency.js +539 -0
- package/tx/cs/cs-db.js +1321 -0
- package/tx/cs/cs-hgvs.js +329 -0
- package/tx/cs/cs-lang.js +465 -0
- package/tx/cs/cs-loinc.js +1485 -0
- package/tx/cs/cs-mimetypes.js +238 -0
- package/tx/cs/cs-ndc.js +704 -0
- package/tx/cs/cs-omop.js +1025 -0
- package/tx/cs/cs-provider-api.js +43 -0
- package/tx/cs/cs-provider-list.js +37 -0
- package/tx/cs/cs-rxnorm.js +808 -0
- package/tx/cs/cs-snomed.js +1102 -0
- package/tx/cs/cs-ucum.js +514 -0
- package/tx/cs/cs-unii.js +271 -0
- package/tx/cs/cs-uri.js +218 -0
- package/tx/cs/cs-usstates.js +305 -0
- package/tx/dev.fhir.org.yml +14 -0
- package/tx/fixtures/test-cases-setup.json +18 -0
- package/tx/fixtures/test-cases.yml +16 -0
- package/tx/html/codesystem-operations.liquid +25 -0
- package/tx/html/home-metrics.liquid +247 -0
- package/tx/html/operations-form.liquid +148 -0
- package/tx/html/search-form.liquid +62 -0
- package/tx/html/tx-template.html +133 -0
- package/tx/html/valueset-operations.liquid +54 -0
- package/tx/importers/atc-to-fhir.js +316 -0
- package/tx/importers/import-loinc.module.js +1536 -0
- package/tx/importers/import-ndc.module.js +1088 -0
- package/tx/importers/import-rxnorm.module.js +898 -0
- package/tx/importers/import-sct.module.js +2457 -0
- package/tx/importers/import-unii.module.js +601 -0
- package/tx/importers/readme.md +453 -0
- package/tx/importers/subset-loinc.module.js +1081 -0
- package/tx/importers/subset-rxnorm.module.js +938 -0
- package/tx/importers/tx-import-base.js +351 -0
- package/tx/importers/tx-import-settings.js +310 -0
- package/tx/importers/tx-import.js +357 -0
- package/tx/library/canonical-resource.js +88 -0
- package/tx/library/capabilitystatement.js +292 -0
- package/tx/library/codesystem.js +774 -0
- package/tx/library/conceptmap.js +568 -0
- package/tx/library/designations.js +932 -0
- package/tx/library/errors.js +77 -0
- package/tx/library/extensions.js +117 -0
- package/tx/library/namingsystem.js +322 -0
- package/tx/library/operation-outcome.js +127 -0
- package/tx/library/parameters.js +105 -0
- package/tx/library/renderer.js +1559 -0
- package/tx/library/terminologycapabilities.js +418 -0
- package/tx/library/ucum-parsers.js +1029 -0
- package/tx/library/ucum-service.js +370 -0
- package/tx/library/ucum-types.js +1099 -0
- package/tx/library/valueset.js +543 -0
- package/tx/library.js +676 -0
- package/tx/ocl/cm-ocl.js +106 -0
- package/tx/ocl/cs-ocl.js +39 -0
- package/tx/ocl/vs-ocl.js +105 -0
- package/tx/operation-context.js +568 -0
- package/tx/params.js +613 -0
- package/tx/provider.js +403 -0
- package/tx/sct/ecl.js +1560 -0
- package/tx/sct/expressions.js +2077 -0
- package/tx/sct/structures.js +1396 -0
- package/tx/tx-html.js +1063 -0
- package/tx/tx.fhir.org.yml +39 -0
- package/tx/tx.js +927 -0
- package/tx/vs/vs-api.js +112 -0
- package/tx/vs/vs-database.js +786 -0
- package/tx/vs/vs-package.js +358 -0
- package/tx/vs/vs-vsac.js +366 -0
- package/tx/workers/batch-validate.js +129 -0
- package/tx/workers/batch.js +361 -0
- package/tx/workers/closure.js +32 -0
- package/tx/workers/expand.js +1845 -0
- package/tx/workers/lookup.js +407 -0
- package/tx/workers/metadata.js +467 -0
- package/tx/workers/operations.js +34 -0
- package/tx/workers/read.js +164 -0
- package/tx/workers/search.js +384 -0
- package/tx/workers/subsumes.js +334 -0
- package/tx/workers/translate.js +492 -0
- package/tx/workers/validate.js +2504 -0
- package/tx/workers/worker.js +904 -0
- package/tx/xml/capabilitystatement-xml.js +63 -0
- package/tx/xml/codesystem-xml.js +62 -0
- package/tx/xml/conceptmap-xml.js +65 -0
- package/tx/xml/namingsystem-xml.js +65 -0
- package/tx/xml/operationoutcome-xml.js +127 -0
- package/tx/xml/parameters-xml.js +312 -0
- package/tx/xml/terminologycapabilities-xml.js +64 -0
- package/tx/xml/valueset-xml.js +64 -0
- package/tx/xml/xml-base.js +603 -0
- package/vcl/vcl-parser.js +1098 -0
- package/vcl/vcl.js +253 -0
- package/windows-install.js +19 -0
- package/xig/xig-template.html +124 -0
- package/xig/xig.js +3049 -0
|
@@ -0,0 +1,1845 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Expand Worker - Handles ValueSet $expand operation
|
|
3
|
+
//
|
|
4
|
+
// GET /ValueSet/{id}/$expand
|
|
5
|
+
// GET /ValueSet/$expand?url=...&version=...
|
|
6
|
+
// POST /ValueSet/$expand (form body or Parameters with url)
|
|
7
|
+
// POST /ValueSet/$expand (body is ValueSet resource)
|
|
8
|
+
// POST /ValueSet/$expand (body is Parameters with valueSet parameter)
|
|
9
|
+
//
|
|
10
|
+
|
|
11
|
+
const { TerminologyWorker } = require('./worker');
|
|
12
|
+
const {TxParameters} = require("../params");
|
|
13
|
+
const {Designations, SearchFilterText} = require("../library/designations");
|
|
14
|
+
const {Extensions} = require("../library/extensions");
|
|
15
|
+
const {getValuePrimitive, getValueName} = require("../../library/utilities");
|
|
16
|
+
const {div} = require("../../library/html");
|
|
17
|
+
const {Issue, OperationOutcome} = require("../library/operation-outcome");
|
|
18
|
+
const crypto = require('crypto');
|
|
19
|
+
const ValueSet = require("../library/valueset");
|
|
20
|
+
const {VersionUtilities} = require("../../library/version-utilities");
|
|
21
|
+
|
|
22
|
+
// Expansion limits (from Pascal constants)
|
|
23
|
+
const UPPER_LIMIT_NO_TEXT = 1000;
|
|
24
|
+
const UPPER_LIMIT_TEXT = 1000;
|
|
25
|
+
const INTERNAL_LIMIT = 10000;
|
|
26
|
+
const EXPANSION_DEAD_TIME_SECS = 30;
|
|
27
|
+
const CACHE_WHEN_DEBUGGING = false;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Total status for expansion
|
|
31
|
+
*/
|
|
32
|
+
const TotalStatus = {
|
|
33
|
+
Uninitialized: 'uninitialized',
|
|
34
|
+
Set: 'set',
|
|
35
|
+
Off: 'off'
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Wraps an already-expanded ValueSet for fast code lookups
|
|
40
|
+
* Used when importing ValueSets during expansion
|
|
41
|
+
*/
|
|
42
|
+
class ImportedValueSet {
|
|
43
|
+
/**
|
|
44
|
+
* @param {Object} valueSet - Expanded ValueSet resource
|
|
45
|
+
*/
|
|
46
|
+
constructor(valueSet) {
|
|
47
|
+
this.valueSet = valueSet;
|
|
48
|
+
this.url = valueSet.url || '';
|
|
49
|
+
this.version = valueSet.version || '';
|
|
50
|
+
|
|
51
|
+
/** @type {Map<string, Object>} Maps system|code -> contains entry */
|
|
52
|
+
this.codeMap = new Map();
|
|
53
|
+
|
|
54
|
+
/** @type {Set<string>} Set of systems in this ValueSet */
|
|
55
|
+
this.systems = new Set();
|
|
56
|
+
|
|
57
|
+
this._buildCodeMap();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Build the code lookup map from the expansion
|
|
62
|
+
* @private
|
|
63
|
+
*/
|
|
64
|
+
_buildCodeMap() {
|
|
65
|
+
if (!this.valueSet.expansion || !this.valueSet.expansion.contains) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
this._indexContains(this.valueSet.expansion.contains);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Recursively index contains entries
|
|
74
|
+
* @private
|
|
75
|
+
*/
|
|
76
|
+
_indexContains(contains) {
|
|
77
|
+
for (const entry of contains) {
|
|
78
|
+
if (entry.system && entry.code) {
|
|
79
|
+
const key = this._makeKey(entry.system, entry.code);
|
|
80
|
+
this.codeMap.set(key, entry);
|
|
81
|
+
this.systems.add(entry.system);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Handle nested contains (hierarchy)
|
|
85
|
+
if (entry.contains && entry.contains.length > 0) {
|
|
86
|
+
this._indexContains(entry.contains);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Make a lookup key from system and code
|
|
93
|
+
* @private
|
|
94
|
+
*/
|
|
95
|
+
_makeKey(system, code) {
|
|
96
|
+
return `${system}\x00${code}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Check if this ValueSet contains a specific code
|
|
101
|
+
* @param {string} system - Code system URL
|
|
102
|
+
* @param {string} code - Code value
|
|
103
|
+
* @returns {boolean}
|
|
104
|
+
*/
|
|
105
|
+
hasCode(system, code) {
|
|
106
|
+
return this.codeMap.has(this._makeKey(system, code));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get a contains entry for a specific code
|
|
111
|
+
* @param {string} system - Code system URL
|
|
112
|
+
* @param {string} code - Code value
|
|
113
|
+
* @returns {Object|null}
|
|
114
|
+
*/
|
|
115
|
+
getCode(system, code) {
|
|
116
|
+
return this.codeMap.get(this._makeKey(system, code)) || null;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Check if this ValueSet contains any codes from a system
|
|
121
|
+
* @param {string} system - Code system URL
|
|
122
|
+
* @returns {boolean}
|
|
123
|
+
*/
|
|
124
|
+
hasSystem(system) {
|
|
125
|
+
return this.systems.has(system);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get total number of codes
|
|
130
|
+
* @returns {number}
|
|
131
|
+
*/
|
|
132
|
+
get count() {
|
|
133
|
+
return this.codeMap.size;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Iterate over all codes
|
|
138
|
+
* @yields {{system: string, code: string, entry: Object}}
|
|
139
|
+
*/
|
|
140
|
+
*codes() {
|
|
141
|
+
for (const entry of this.codeMap.values()) {
|
|
142
|
+
yield {
|
|
143
|
+
system: entry.system,
|
|
144
|
+
code: entry.code,
|
|
145
|
+
entry
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Special filter context for ValueSet import optimization
|
|
153
|
+
* When a ValueSet can be used as a filter instead of full expansion
|
|
154
|
+
*/
|
|
155
|
+
class ValueSetFilterContext {
|
|
156
|
+
/**
|
|
157
|
+
* @param {ImportedValueSet} importedVs - The imported ValueSet
|
|
158
|
+
*/
|
|
159
|
+
constructor(importedVs) {
|
|
160
|
+
this.importedVs = importedVs;
|
|
161
|
+
this.type = 'valueset';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Check if a code passes this filter
|
|
166
|
+
* @param {string} system - Code system URL
|
|
167
|
+
* @param {string} code - Code value
|
|
168
|
+
* @returns {boolean}
|
|
169
|
+
*/
|
|
170
|
+
passesFilter(system, code) {
|
|
171
|
+
return this.importedVs.hasCode(system, code);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Special filter context for empty filter (nothing matches)
|
|
177
|
+
*/
|
|
178
|
+
class EmptyFilterContext {
|
|
179
|
+
constructor() {
|
|
180
|
+
this.type = 'empty';
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
passesFilter() {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
class ValueSetCounter {
|
|
189
|
+
constructor() {
|
|
190
|
+
this.count = 0;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
increment() {
|
|
194
|
+
this.count++;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
class ValueSetExpander {
|
|
199
|
+
worker;
|
|
200
|
+
params;
|
|
201
|
+
excluded = new Set();
|
|
202
|
+
hasExclusions = false;
|
|
203
|
+
|
|
204
|
+
constructor(worker, params) {
|
|
205
|
+
this.worker = worker;
|
|
206
|
+
this.params = params;
|
|
207
|
+
|
|
208
|
+
this.csCounter = new Map();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
addDefinedCode(cs, system, c, imports, parent, excludeInactive, srcURL) {
|
|
212
|
+
this.worker.deadCheck('addDefinedCode');
|
|
213
|
+
let n = null;
|
|
214
|
+
if (!this.params.excludeNotForUI || !cs.isAbstract(c)) {
|
|
215
|
+
const cds = new Designations(this.worker.opContext.i18n.languageDefinitions);
|
|
216
|
+
this.listDisplays(cds, c);
|
|
217
|
+
n = this.includeCode(null, parent, system, '', c.code, cs.isAbstract(c), cs.isInactive(c), cs.isDeprecated(c), cs.codeStatus(c), cds, c.definition, c.itemWeight,
|
|
218
|
+
null, imports, c.getAllExtensionsW(), null, c.properties, null, excludeInactive, srcURL);
|
|
219
|
+
}
|
|
220
|
+
for (let i = 0; i < c.concept.length; i++) {
|
|
221
|
+
this.worker.deadCheck('addDefinedCode');
|
|
222
|
+
this.addDefinedCode(cs, system, c.concept[i], imports, n, excludeInactive, srcURL);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
async listDisplaysFromProvider(displays, cs, context) {
|
|
227
|
+
await cs.designations(context, displays);
|
|
228
|
+
displays.source = cs;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
listDisplaysFromConcept(displays, concept) {
|
|
232
|
+
for (const ccd of concept.designations || []) {
|
|
233
|
+
displays.addDesignation(ccd);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
listDisplaysFromIncludeConcept(displays, concept, vs) {
|
|
238
|
+
if (concept.display) {
|
|
239
|
+
if (!VersionUtilities.isR4Plus(this.worker.provider.getFhirVersion())) {
|
|
240
|
+
displays.clear();
|
|
241
|
+
}
|
|
242
|
+
let lang = vs.language ? this.worker.languages.parse(vs.language) : null;
|
|
243
|
+
displays.addDesignation(true, "active", lang, null, concept.display);
|
|
244
|
+
}
|
|
245
|
+
for (const cd of concept.designation || []) {
|
|
246
|
+
displays.addDesignationFromConcept(cd);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
canonical(system, version) {
|
|
250
|
+
if (!version) {
|
|
251
|
+
return system;
|
|
252
|
+
} else {
|
|
253
|
+
return system + '|' + version;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
passesImport(imp, system, code) {
|
|
258
|
+
imp.buildMap();
|
|
259
|
+
return imp.hasCode(system, code);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
passesImports(imports, system, code, offset) {
|
|
263
|
+
if (imports == null) {
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
for (let i = offset; i < imports.length; i++) {
|
|
267
|
+
if (!this.passesImport(imports[i], system, code)) {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
useDesignation(cd) {
|
|
275
|
+
if (!this.params.hasDesignations) {
|
|
276
|
+
return true;
|
|
277
|
+
}
|
|
278
|
+
for (const s of this.params.designations) {
|
|
279
|
+
const [l, r] = s.split('|');
|
|
280
|
+
if (cd.use != null && cd.use.system === l && cd.use.code === r) {
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
if (cd.language != null && l === 'urn:ietf:bcp:47' && r === cd.language.code) {
|
|
284
|
+
return true;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
return false;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
isValidating() {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
opName() {
|
|
295
|
+
return 'expansion';
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
redundantDisplay(n, lang, use, value) {
|
|
299
|
+
if (!((lang == null) && (!this.valueSet.language)) || ((lang) && lang.code.startsWith(this.valueSet.language))) {
|
|
300
|
+
return false;
|
|
301
|
+
} else if (!((use == null) || (use.code === 'display'))) {
|
|
302
|
+
return false;
|
|
303
|
+
} else {
|
|
304
|
+
return value.asString === n.display;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
includeCode(cs, parent, system, version, code, isAbstract, isInactive, deprecated, status, displays, definition, itemWeight, expansion, imports, csExtList, vsExtList, csProps, expProps, excludeInactive, srcURL) {
|
|
309
|
+
let result = null;
|
|
310
|
+
this.worker.deadCheck('processCode');
|
|
311
|
+
|
|
312
|
+
if (!this.passesImports(imports, system, code, 0)) {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
if (isInactive && excludeInactive) {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
if (this.isExcluded(system, version, code)) {
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (cs != null && cs.expandLimitation > 0) {
|
|
323
|
+
let cnt = this.csCounter.get(cs.system);
|
|
324
|
+
if (cnt == null) {
|
|
325
|
+
cnt = new ValueSetCounter();
|
|
326
|
+
this.csCounter.set(cs.system, cnt);
|
|
327
|
+
}
|
|
328
|
+
cnt.increment();
|
|
329
|
+
if (cnt.count > cs.expandLimitation) {
|
|
330
|
+
return null;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
if (this.limitCount > 0 && this.fullList.length >= this.limitCount && !this.hasExclusions) {
|
|
335
|
+
if (this.count > -1 && this.offset > -1 && this.count + this.offset > 0 && this.fullList.length >= this.count + this.offset) {
|
|
336
|
+
throw new Issue('information', 'informational', null, null, null, null).setFinished();
|
|
337
|
+
} else {
|
|
338
|
+
if (!srcURL) {
|
|
339
|
+
srcURL = '??';
|
|
340
|
+
}
|
|
341
|
+
throw new Issue("error", "too-costly", null, 'VALUESET_TOO_COSTLY', this.worker.i18n.translate('VALUESET_TOO_COSTLY', this.params.httpLanguages, [srcURL, '>' + this.limitCount]), null, 400).withDiagnostics(this.worker.opContext.diagnostics());
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (expansion) {
|
|
346
|
+
const s = this.canonical(system, version);
|
|
347
|
+
this.addParamUri(expansion, 'used-codesystem', s);
|
|
348
|
+
if (cs != null) {
|
|
349
|
+
const ts = cs.listSupplements();
|
|
350
|
+
for (const vs of ts) {
|
|
351
|
+
this.addParamUri(expansion, 'used-supplement', vs);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const s = this.keyS(system, version, code);
|
|
357
|
+
|
|
358
|
+
if (!this.map.has(s)) {
|
|
359
|
+
const n = {};
|
|
360
|
+
n.system = system;
|
|
361
|
+
n.code = code;
|
|
362
|
+
if (this.doingVersion) {
|
|
363
|
+
n.version = version;
|
|
364
|
+
}
|
|
365
|
+
if (isAbstract) {
|
|
366
|
+
n.abstract = isAbstract;
|
|
367
|
+
}
|
|
368
|
+
if (isInactive) {
|
|
369
|
+
n.inactive = true;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
if (status && status.toLowerCase() !== 'active') {
|
|
373
|
+
this.defineProperty(expansion, n, 'http://hl7.org/fhir/concept-properties#status', 'status', "valueCode", status);
|
|
374
|
+
} else if (deprecated) {
|
|
375
|
+
this.defineProperty(expansion, n, 'http://hl7.org/fhir/concept-properties#status', 'status', "valueCode", 'deprecated');
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (Extensions.has(csExtList, 'http://hl7.org/fhir/StructureDefinition/codesystem-label')) {
|
|
379
|
+
this.defineProperty(expansion, n, 'http://hl7.org/fhir/concept-properties#label', 'label', "valueString", Extensions.readString(csExtList, 'http://hl7.org/fhir/StructureDefinition/codesystem-label'));
|
|
380
|
+
}
|
|
381
|
+
if (Extensions.has(vsExtList, 'http://hl7.org/fhir/StructureDefinition/valueset-label')) {
|
|
382
|
+
this.defineProperty(expansion, n, 'http://hl7.org/fhir/concept-properties#label', 'label', "valueString", Extensions.readString(vsExtList, 'http://hl7.org/fhir/StructureDefinition/valueset-label'));
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (Extensions.has(csExtList, 'http://hl7.org/fhir/StructureDefinition/codesystem-conceptOrder')) {
|
|
386
|
+
this.defineProperty(expansion, n, 'http://hl7.org/fhir/concept-properties#order', 'order', "valueDecimal", Extensions.readNumber(csExtList, 'http://hl7.org/fhir/StructureDefinition/codesystem-conceptOrder', undefined));
|
|
387
|
+
}
|
|
388
|
+
if (Extensions.has(vsExtList, 'http://hl7.org/fhir/StructureDefinition/valueset-conceptOrder')) {
|
|
389
|
+
this.defineProperty(expansion, n, 'http://hl7.org/fhir/concept-properties#order', 'order', "valueDecimal", Extensions.readNumber(vsExtList, 'http://hl7.org/fhir/StructureDefinition/valueset-conceptOrder', undefined));
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (Extensions.has(csExtList, 'http://hl7.org/fhir/StructureDefinition/itemWeight')) {
|
|
393
|
+
this.defineProperty(expansion, n, 'http://hl7.org/fhir/concept-properties#itemWeight', 'weight', "valueDecimal", Extensions.readNumber(csExtList, 'http://hl7.org/fhir/StructureDefinition/itemWeight', undefined));
|
|
394
|
+
}
|
|
395
|
+
if (Extensions.has(vsExtList, 'http://hl7.org/fhir/StructureDefinition/itemWeight')) {
|
|
396
|
+
this.defineProperty(expansion, n, 'http://hl7.org/fhir/concept-properties#itemWeight', 'weight', "valueDecimal", Extensions.readNumber(vsExtList, 'http://hl7.org/fhir/StructureDefinition/itemWeight', undefined));
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (csExtList != null) {
|
|
400
|
+
for (const ext of csExtList) {
|
|
401
|
+
if (['http://hl7.org/fhir/StructureDefinition/coding-sctdescid', 'http://hl7.org/fhir/StructureDefinition/rendering-style',
|
|
402
|
+
'http://hl7.org/fhir/StructureDefinition/rendering-xhtml', 'http://hl7.org/fhir/StructureDefinition/codesystem-alternate'].includes(ext.url)) {
|
|
403
|
+
if (!n.extension) {n.extension = []}
|
|
404
|
+
n.extension.push(ext);
|
|
405
|
+
}
|
|
406
|
+
if (['http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status'].includes(ext.url)) {
|
|
407
|
+
this.defineProperty(expansion, n, 'http://hl7.org/fhir/concept-properties#status', 'status', "valueCode", getValuePrimitive(ext));
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (vsExtList != null) {
|
|
413
|
+
for (const ext of vsExtList || []) {
|
|
414
|
+
if (['http://hl7.org/fhir/StructureDefinition/valueset-supplement', 'http://hl7.org/fhir/StructureDefinition/valueset-deprecated',
|
|
415
|
+
'http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status',
|
|
416
|
+
'http://hl7.org/fhir/StructureDefinition/valueset-concept-definition', 'http://hl7.org/fhir/StructureDefinition/coding-sctdescid',
|
|
417
|
+
'http://hl7.org/fhir/StructureDefinition/rendering-style', 'http://hl7.org/fhir/StructureDefinition/rendering-xhtml'].includes(ext.url)) {
|
|
418
|
+
if (!n.extension) {n.extension = []}
|
|
419
|
+
n.extension.push(ext);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// display and designations
|
|
425
|
+
const pref = displays.preferredDesignation(this.params.workingLanguages());
|
|
426
|
+
if (pref && pref.value) {
|
|
427
|
+
n.display = pref.value;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (this.params.includeDesignations) {
|
|
431
|
+
for (const t of displays.designations) {
|
|
432
|
+
if (t !== pref && this.useDesignation(t) && t.value != null && !this.redundantDisplay(n, t.language, t.use, t.value)) {
|
|
433
|
+
if (!n.designation) {
|
|
434
|
+
n.designation = [];
|
|
435
|
+
}
|
|
436
|
+
n.designation.push(t.asObject());
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
for (const pn of this.params.properties) {
|
|
442
|
+
if (pn === 'definition') {
|
|
443
|
+
if (definition) {
|
|
444
|
+
this.defineProperty(expansion, n, 'http://hl7.org/fhir/concept-properties#definition', pn, "valueString", definition);
|
|
445
|
+
}
|
|
446
|
+
} else if (csProps != null && cs != null) {
|
|
447
|
+
for (const cp of csProps) {
|
|
448
|
+
if (cp.code === pn) {
|
|
449
|
+
let vn = getValueName(cp);
|
|
450
|
+
let v = cp[vn];
|
|
451
|
+
this.defineProperty(expansion, n, this.getPropUrl(cs, pn), pn, vn, v);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
if (!this.map.has(s)) {
|
|
458
|
+
this.fullList.push(n);
|
|
459
|
+
this.map.set(s, n);
|
|
460
|
+
if (parent != null) {
|
|
461
|
+
if (!parent.contains) {
|
|
462
|
+
parent.contains = [];
|
|
463
|
+
}
|
|
464
|
+
parent.contains.push(n);
|
|
465
|
+
} else {
|
|
466
|
+
this.rootList.push(n);
|
|
467
|
+
}
|
|
468
|
+
} else {
|
|
469
|
+
this.canBeHierarchy = false;
|
|
470
|
+
}
|
|
471
|
+
result = n;
|
|
472
|
+
}
|
|
473
|
+
return result;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
excludeCode(cs, system, version, code, expansion, imports, srcURL) {
|
|
477
|
+
this.worker.deadCheck('excludeCode');
|
|
478
|
+
if (!this.passesImports(imports, system, code, 0)) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
if (this.limitCount > 0 && this.fullList.length >= this.limitCount && !this.hasExclusions) {
|
|
483
|
+
if (this.count > -1 && this.offset > -1 && this.count + this.offset > 0 && this.fullList.length >= this.count + this.offset) {
|
|
484
|
+
throw new Issue('information', 'informational', null, null, null, null).setFinished();
|
|
485
|
+
} else {
|
|
486
|
+
if (!srcURL) {
|
|
487
|
+
srcURL = '??';
|
|
488
|
+
}
|
|
489
|
+
throw new Issue("error", "too-costly", null, 'VALUESET_TOO_COSTLY', this.worker.i18n.translate('VALUESET_TOO_COSTLY', this.params.httpLanguages, [srcURL, '>' + this.limitCount]), null, 400).withDiagnostics(this.worker.opContext.diagnostics());
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
if (expansion) {
|
|
494
|
+
const s = this.canonical(system, version);
|
|
495
|
+
this.addParamUri(expansion, 'used-codesystem', s);
|
|
496
|
+
if (cs) {
|
|
497
|
+
const ts= cs.listSupplements();
|
|
498
|
+
for (const vs of ts) {
|
|
499
|
+
this.addParamUri(expansion, 'used-supplement', vs);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
this.excluded.add(system + '|' + version + '#' + code);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
async checkCanExpandValueset(uri, version) {
|
|
508
|
+
const vs = await this.worker.findValueSet(uri, version);
|
|
509
|
+
if (vs == null) {
|
|
510
|
+
if (!version && uri.includes('|')) {
|
|
511
|
+
version = uri.substring(uri.indexOf('|') + 1);
|
|
512
|
+
uri = uri.substring(0, uri.indexOf('|'));
|
|
513
|
+
}
|
|
514
|
+
if (!version) {
|
|
515
|
+
throw new Issue('error', 'not-found', null, 'VS_EXP_IMPORT_UNK', this.worker.i18n.translate('VS_EXP_IMPORT_UNK', this.params.httpLanguages, [uri]), 'unknown', 400);
|
|
516
|
+
} else {
|
|
517
|
+
throw new Issue('error', 'not-found', null, 'VS_EXP_IMPORT_UNK_PINNED', this.worker.i18n.translate('VS_EXP_IMPORT_UNK_PINNED', this.params.httpLanguages, [uri, version]), 'not-found', 400);
|
|
518
|
+
}
|
|
519
|
+
} else {
|
|
520
|
+
this.worker.seeSourceVS(vs, uri);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
async expandValueSet(uri, version, filter, notClosed) {
|
|
525
|
+
|
|
526
|
+
let vs = await this.worker.findValueSet(uri, version);
|
|
527
|
+
if (!vs) {
|
|
528
|
+
if (version) {
|
|
529
|
+
throw new Issue('error', 'not-found', null, 'VS_EXP_IMPORT_UNK_PINNED', this.worker.i18n.translate('VS_EXP_IMPORT_UNK_PINNED', this.params.httpLanguages, [uri, version]), "not-found", 400);
|
|
530
|
+
} else if (uri.includes('|')) {
|
|
531
|
+
throw new Issue('error', 'not-found', null, 'VS_EXP_IMPORT_UNK_PINNED', this.worker.i18n.translate('VS_EXP_IMPORT_UNK_PINNED', this.params.httpLanguages, [uri.substring(0, uri.indexOf("|")), uri.substring(uri.indexOf("|")+1)]), "not-found", 400);
|
|
532
|
+
} else {
|
|
533
|
+
throw new Issue('error', 'not-found', null, 'VS_EXP_IMPORT_UNK', this.worker.i18n.translate('VS_EXP_IMPORT_UNK', this.params.httpLanguages, [uri]), "not-found", 400);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
let worker = new ExpandWorker(this.worker.opContext, this.worker.log, this.worker.provider, this.worker.languages, this.worker.i18n);
|
|
537
|
+
worker.additionalResources = this.worker.additionalResources;
|
|
538
|
+
let expander = new ValueSetExpander(worker, this.params);
|
|
539
|
+
let result = await expander.expand(vs, filter, false);
|
|
540
|
+
if (result == null) {
|
|
541
|
+
throw new Issue('error', 'not-found', null, 'VS_EXP_IMPORT_UNK', this.worker.i18n.translate('VS_EXP_IMPORT_UNK', this.params.httpLanguages, [uri]), 'unknown');
|
|
542
|
+
}
|
|
543
|
+
if (Extensions.has(result.expansion, 'http://hl7.org/fhir/params/questionnaire-extensions#closed')) {
|
|
544
|
+
notClosed.value = true;
|
|
545
|
+
}
|
|
546
|
+
return result;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
async importValueSet(vs, expansion, imports, offset) {
|
|
550
|
+
this.canBeHierarchy = false;
|
|
551
|
+
for (let p of vs.expansion.parameter) {
|
|
552
|
+
let vn = getValueName(p);
|
|
553
|
+
let v = getValuePrimitive(p);
|
|
554
|
+
this.addParam(expansion, p.name, vn, v);
|
|
555
|
+
}
|
|
556
|
+
this.checkResourceCanonicalStatus(expansion, vs, this.valueSet);
|
|
557
|
+
|
|
558
|
+
for (const c of vs.expansion.contains || []) {
|
|
559
|
+
this.worker.deadCheck('importValueSet');
|
|
560
|
+
await this.importValueSetItem(null, c, imports, offset);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
async importValueSetItem(p, c, imports, offset) {
|
|
565
|
+
this.worker.deadCheck('importValueSetItem');
|
|
566
|
+
const s = this.keyC(c);
|
|
567
|
+
if (this.passesImports(imports, c.system, c.code, offset) && !this.map.has(s)) {
|
|
568
|
+
this.fullList.push(c);
|
|
569
|
+
if (p != null) {
|
|
570
|
+
if (!p.contains) {p.contains = [] }
|
|
571
|
+
p.contains.push(c);
|
|
572
|
+
} else {
|
|
573
|
+
this.rootList.push(c);
|
|
574
|
+
}
|
|
575
|
+
this.map.set(s, c);
|
|
576
|
+
}
|
|
577
|
+
for (const cc of c.contains || []) {
|
|
578
|
+
this.worker.deadCheck('importValueSetItem');
|
|
579
|
+
await this.importValueSetItem(c, cc, imports, offset);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
excludeValueSet(vs, expansion, imports, offset) {
|
|
584
|
+
for (const c of vs.expansion.contains) {
|
|
585
|
+
this.worker.deadCheck('excludeValueSet');
|
|
586
|
+
const s = this.keyC(c);
|
|
587
|
+
if (this.passesImports(imports, c.system, c.code, offset) && this.map.has(s)) {
|
|
588
|
+
const idx = this.fullList.indexOf(this.map.get(s));
|
|
589
|
+
if (idx >= 0) {
|
|
590
|
+
this.fullList.splice(idx, 1);
|
|
591
|
+
}
|
|
592
|
+
this.map.delete(s);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
async checkSource(cset, exp, filter, srcURL, ts) {
|
|
598
|
+
this.worker.deadCheck('checkSource');
|
|
599
|
+
Extensions.checkNoModifiers(cset, 'ValueSetExpander.checkSource', 'set');
|
|
600
|
+
let imp = false;
|
|
601
|
+
for (const u of cset.valueSet || []) {
|
|
602
|
+
this.worker.deadCheck('checkSource');
|
|
603
|
+
const s = this.worker.pinValueSet(u);
|
|
604
|
+
await this.checkCanExpandValueset(s, '');
|
|
605
|
+
imp = true;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
if (ts.has(cset.system)) {
|
|
609
|
+
const v = ts.get(cset.system);
|
|
610
|
+
if (v !== cset.version) {
|
|
611
|
+
this.doingVersion = true;
|
|
612
|
+
}
|
|
613
|
+
} else {
|
|
614
|
+
ts.set(cset.system, cset.version);
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
if (cset.system) {
|
|
618
|
+
const cs = await this.worker.findCodeSystem(cset.system, cset.version, this.params, ['complete', 'fragment'], false, true, true, null);
|
|
619
|
+
this.worker.seeSourceProvider(cs, cset.system);
|
|
620
|
+
if (cs == null) {
|
|
621
|
+
// nothing
|
|
622
|
+
} else {
|
|
623
|
+
if (cs.contentMode() !== 'complete') {
|
|
624
|
+
if (cs.contentMode() === 'not-present') {
|
|
625
|
+
throw new Issue('error', 'business-rule', null, null, 'The code system definition for ' + cset.system + ' has no content, so this expansion cannot be performed', 'invalid');
|
|
626
|
+
} else if (cs.contentMode() === 'supplement') {
|
|
627
|
+
throw new Issue('error', 'business-rule', null, null, 'The code system definition for ' + cset.system + ' defines a supplement, so this expansion cannot be performed', 'invalid');
|
|
628
|
+
} else if (this.params.incompleteOK) {
|
|
629
|
+
exp.addParamUri(cs.contentMode, cs.system + '|' + cs.version);
|
|
630
|
+
} else {
|
|
631
|
+
throw new Issue('error', 'business-rule', null, null, 'The code system definition for ' + cset.system + ' is a ' + cs.contentMode + ', so this expansion is not permitted unless the expansion parameter "incomplete-ok" has a value of "true"', 'invalid');
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
if (!cset.concept && !cset.filter) {
|
|
636
|
+
if (cs.specialEnumeration() && this.params.limitedExpansion) {
|
|
637
|
+
this.checkCanExpandValueSet(cs.specialEnumeration(), '');
|
|
638
|
+
} else if (filter.isNull) {
|
|
639
|
+
if (cs.isNotClosed()) {
|
|
640
|
+
if (cs.specialEnumeration()) {
|
|
641
|
+
throw new Issue("error", "too-costly", null, null, 'The code System "' + cs.system() + '" has a grammar, and cannot be enumerated directly. If an incomplete expansion is requested, a limited enumeration will be returned', null, 400).withDiagnostics(this.worker.opContext.diagnostics());
|
|
642
|
+
} else {
|
|
643
|
+
throw new Issue("error", "too-costly", null, null, 'The code System "' + cs.system() + '" has a grammar, and cannot be enumerated directly', null, 400).withDiagnostics(this.worker.opContext.diagnostics());
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
if (!imp && this.limitCount > 0 && cs.totalCount > this.limitCount && !this.params.limitedExpansion) {
|
|
648
|
+
throw new Issue("error", "too-costly", null, 'VALUESET_TOO_COSTLY', this.worker.i18n.translate('VALUESET_TOO_COSTLY', this.params.httpLanguages, [srcURL, '>' + this.limitCount]), null, 400).withDiagnostics(this.worker.opContext.diagnostics());
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
async includeCodes(cset, path, vsSrc, filter, expansion, excludeInactive, notClosed) {
|
|
657
|
+
this.worker.deadCheck('processCodes#1');
|
|
658
|
+
const valueSets = [];
|
|
659
|
+
|
|
660
|
+
Extensions.checkNoModifiers(cset, 'ValueSetExpander.processCodes', 'set');
|
|
661
|
+
|
|
662
|
+
if (cset.valueSet || cset.concept || (cset.filter || []).length > 1) {
|
|
663
|
+
this.canBeHierarchy = false;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
if (!cset.system) {
|
|
667
|
+
for (const u of cset.valueSet) {
|
|
668
|
+
this.worker.deadCheck('processCodes#2');
|
|
669
|
+
const s = this.worker.pinValueSet(u);
|
|
670
|
+
this.worker.opContext.log('import value set ' + s);
|
|
671
|
+
const ivs = new ImportedValueSet(await this.expandValueSet(s, '', filter, notClosed));
|
|
672
|
+
this.checkResourceCanonicalStatus(expansion, ivs.valueSet, this.valueSet);
|
|
673
|
+
this.addParamUri(expansion, 'used-valueset', this.worker.makeVurl(ivs.valueSet));
|
|
674
|
+
valueSets.push(ivs);
|
|
675
|
+
}
|
|
676
|
+
await this.importValueSet(valueSets[0].valueSet, expansion, valueSets, 1);
|
|
677
|
+
} else {
|
|
678
|
+
const filters = [];
|
|
679
|
+
const prep = null;
|
|
680
|
+
const cs = await this.worker.findCodeSystem(cset.system, cset.version, this.params, ['complete', 'fragment'], false, false, true, null);
|
|
681
|
+
|
|
682
|
+
if (cs == null) {
|
|
683
|
+
// nothing
|
|
684
|
+
} else {
|
|
685
|
+
|
|
686
|
+
this.worker.checkSupplements(cs, cset, this.requiredSupplements);
|
|
687
|
+
this.checkProviderCanonicalStatus(expansion, cs, this.valueSet);
|
|
688
|
+
const sv = this.canonical(await cs.system(), await cs.version());
|
|
689
|
+
this.addParamUri(expansion, 'used-codesystem', sv);
|
|
690
|
+
|
|
691
|
+
for (const u of cset.valueSet || []) {
|
|
692
|
+
this.worker.deadCheck('processCodes#3');
|
|
693
|
+
const s = this.pinValueSet(u);
|
|
694
|
+
let f = null;
|
|
695
|
+
this.opContext.log('import2 value set ' + s);
|
|
696
|
+
const vs = this.onGetValueSet(this, s, '');
|
|
697
|
+
if (vs != null) {
|
|
698
|
+
f = this.makeFilterForValueSet(cs, vs);
|
|
699
|
+
}
|
|
700
|
+
if (f != null) {
|
|
701
|
+
filters.push(f);
|
|
702
|
+
} else {
|
|
703
|
+
valueSets.push(new ImportedValueSet(await this.expandValueSet(s, '', filter, notClosed)));
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
if (!cset.concept && !cset.filter) {
|
|
708
|
+
if (cs.specialEnumeration() && this.params.limitedExpansion && filters.length === 0) {
|
|
709
|
+
this.worker.opContext.log('import special value set ' + cs.specialEnumeration());
|
|
710
|
+
const base = await this.expandValueSet(cs.specialEnumeration(), '', filter, notClosed);
|
|
711
|
+
Extensions.addBoolean(expansion, 'http://hl7.org/fhir/StructureDefinition/valueset-toocostly', true);
|
|
712
|
+
await this.importValueSet(base, expansion, valueSets, 0);
|
|
713
|
+
notClosed.value = true;
|
|
714
|
+
} else if (filter.isNull) {
|
|
715
|
+
this.worker.opContext.log('add whole code system');
|
|
716
|
+
if (cs.isNotClosed()) {
|
|
717
|
+
if (cs.specialEnumeration()) {
|
|
718
|
+
throw new Issue("error", "too-costly", null, null, 'The code System "' + cs.system() + '" has a grammar, and cannot be enumerated directly. If an incomplete expansion is requested, a limited enumeration will be returned', null, 400).withDiagnostics(this.worker.opContext.diagnostics());
|
|
719
|
+
|
|
720
|
+
} else {
|
|
721
|
+
throw new Issue("error", "too-costly", null, null, 'The code System "' + cs.system() + '" has a grammar, and cannot be enumerated directly', null, 400).withDiagnostics(this.worker.opContext.diagnostics());
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
const iter = await cs.iterator(null);
|
|
726
|
+
if (valueSets.length === 0 && this.limitCount > 0 && (iter && iter.total > this.limitCount) && !this.params.limitedExpansion && this.offset < 0) {
|
|
727
|
+
throw new Issue("error", "too-costly", null, 'VALUESET_TOO_COSTLY', this.worker.i18n.translate('VALUESET_TOO_COSTLY', this.params.httpLanguages, [vsSrc.vurl, '>' + this.limitCount]), null, 400).withDiagnostics(this.worker.opContext.diagnostics());
|
|
728
|
+
|
|
729
|
+
}
|
|
730
|
+
let tcount = 0;
|
|
731
|
+
let c = await cs.nextContext(iter);
|
|
732
|
+
while (c) {
|
|
733
|
+
this.worker.deadCheck('processCodes#3a');
|
|
734
|
+
if (await this.passesFilters(cs, c, prep, filters, 0)) {
|
|
735
|
+
tcount += await this.includeCodeAndDescendants(cs, c, expansion, valueSets, null, excludeInactive, vsSrc.vurl);
|
|
736
|
+
}
|
|
737
|
+
c = await cs.nextContext(iter);
|
|
738
|
+
}
|
|
739
|
+
this.addToTotal(tcount);
|
|
740
|
+
} else {
|
|
741
|
+
this.worker.opContext.log('prepare filters');
|
|
742
|
+
this.noTotal();
|
|
743
|
+
if (cs.isNotClosed(filter)) {
|
|
744
|
+
notClosed.value = true;
|
|
745
|
+
}
|
|
746
|
+
const prep = await cs.getPrepContext(true);
|
|
747
|
+
const ctxt = await cs.searchFilter(filter, prep, false);
|
|
748
|
+
await cs.prepare(prep);
|
|
749
|
+
this.worker.opContext.log('iterate filters');
|
|
750
|
+
while (await cs.filterMore(ctxt)) {
|
|
751
|
+
this.worker.deadCheck('processCodes#4');
|
|
752
|
+
const c = await cs.filterConcept(ctxt);
|
|
753
|
+
if (await this.passesFilters(cs, c, prep, filters, 0)) {
|
|
754
|
+
const cds = new Designations(this.worker.i18n.languageDefinitions);
|
|
755
|
+
await this.listDisplaysFromProvider(cds, cs, c);
|
|
756
|
+
await this.includeCode(cs, null, await cs.system(), await cs.version(), await cs.code(c), await cs.isAbstract(c), await cs.isInactive(c), await cs.deprecated(c), await cs.getCodeStatus(c),
|
|
757
|
+
cds, await cs.definition(c), await cs.itemWeight(c), expansion, valueSets, await cs.getExtensions(c), null, await cs.getProperties(c), null, excludeInactive, vsSrc.url);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
this.worker.opContext.log('iterate filters done');
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
if (cset.concept) {
|
|
765
|
+
this.worker.opContext.log('iterate concepts');
|
|
766
|
+
const cds = new Designations(this.worker.i18n.languageDefinitions);
|
|
767
|
+
let tcount = 0;
|
|
768
|
+
for (const cc of cset.concept) {
|
|
769
|
+
this.worker.deadCheck('processCodes#3');
|
|
770
|
+
cds.clear();
|
|
771
|
+
Extensions.checkNoModifiers(cc, 'ValueSetExpander.processCodes', 'set concept reference');
|
|
772
|
+
const cctxt = await cs.locate(cc.code, this.allAltCodes);
|
|
773
|
+
if (cctxt && cctxt.context && (!this.params.activeOnly || !await cs.isInactive(cctxt.context)) && await this.passesFilters(cs, cctxt.context, prep, filters, 0)) {
|
|
774
|
+
await this.listDisplaysFromProvider(cds, cs, cctxt.context);
|
|
775
|
+
this.listDisplaysFromIncludeConcept(cds, cc, vsSrc);
|
|
776
|
+
if (filter.passesDesignations(cds) || filter.passes(cc.code)) {
|
|
777
|
+
tcount++;
|
|
778
|
+
let ov = Extensions.readString(cc, 'http://hl7.org/fhir/StructureDefinition/itemWeight');
|
|
779
|
+
if (!ov) {
|
|
780
|
+
ov = await cs.itemWeight(cctxt.context);
|
|
781
|
+
}
|
|
782
|
+
await this.includeCode(cs, null, cs.system(), cs.version(), cc.code, await cs.isAbstract(cctxt.context), await cs.isInactive(cctxt.context), await cs.isDeprecated(cctxt.context), await cs.getStatus(cctxt.context), cds,
|
|
783
|
+
await cs.definition(cctxt.context), ov, expansion, valueSets, await cs.extensions(cctxt.context), cc.extension, await cs.properties(cctxt.context), null, excludeInactive, vsSrc.url);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
this.addToTotal(tcount);
|
|
788
|
+
this.worker.opContext.log('iterate concepts done');
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
if (cset.filter) {
|
|
792
|
+
this.worker.opContext.log('prepare filters');
|
|
793
|
+
const fcl = cset.filter;
|
|
794
|
+
const prep = await cs.getPrepContext(true);
|
|
795
|
+
if (!filter.isNull) {
|
|
796
|
+
await cs.searchFilter(filter, prep, true);
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
if (cs.specialEnumeration()) {
|
|
800
|
+
await cs.specialFilter(prep, true);
|
|
801
|
+
Extensions.addBoolean(expansion, 'http://hl7.org/fhir/StructureDefinition/valueset-toocostly', true);
|
|
802
|
+
notClosed.value = true;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
for (let i = 0; i < fcl.length; i++) {
|
|
806
|
+
this.worker.deadCheck('processCodes#4a');
|
|
807
|
+
const fc = fcl[i];
|
|
808
|
+
if (!fc.value) {
|
|
809
|
+
throw new Issue('error', 'invalid', path+".filter["+i+"]", 'UNABLE_TO_HANDLE_SYSTEM_FILTER_WITH_NO_VALUE', this.worker.i18n.translate('UNABLE_TO_HANDLE_SYSTEM_FILTER_WITH_NO_VALUE', this.params.httpLanguages, [cs.system(), fc.property, fc.op]), 'vs-invalid', 400);
|
|
810
|
+
}
|
|
811
|
+
Extensions.checkNoModifiers(fc, 'ValueSetExpander.processCodes', 'filter');
|
|
812
|
+
await cs.filter(prep, fc.property, fc.op, fc.value);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
const fset = await cs.executeFilters(prep);
|
|
816
|
+
if (await cs.filtersNotClosed(prep)) {
|
|
817
|
+
notClosed.value = true;
|
|
818
|
+
}
|
|
819
|
+
if (fset.length === 1 && !excludeInactive && !this.params.activeOnly) {
|
|
820
|
+
this.addToTotal(await cs.filterSize(prep, fset[0]));
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// let count = 0;
|
|
824
|
+
this.worker.opContext.log('iterate filters');
|
|
825
|
+
while (await cs.filterMore(prep, fset[0])) {
|
|
826
|
+
this.worker.deadCheck('processCodes#5');
|
|
827
|
+
const c = await cs.filterConcept(prep, fset[0]);
|
|
828
|
+
const ok = (!this.params.activeOnly || !await cs.isInactive(c)) && (await this.passesFilters(cs, c, prep, fset, 1));
|
|
829
|
+
if (ok) {
|
|
830
|
+
// count++;
|
|
831
|
+
const cds = new Designations(this.worker.i18n.languageDefinitions);
|
|
832
|
+
if (this.passesImports(valueSets, cs.system(), await cs.code(c), 0)) {
|
|
833
|
+
await this.listDisplaysFromProvider(cds, cs, c);
|
|
834
|
+
let parent = null;
|
|
835
|
+
if (cs.hasParents()) {
|
|
836
|
+
parent = this.map.get(this.keyS(cs.system(), cs.version(), await cs.parent(c)));
|
|
837
|
+
} else {
|
|
838
|
+
this.canBeHierarchy = false;
|
|
839
|
+
}
|
|
840
|
+
await this.includeCode(cs, parent, await cs.system(), await cs.version(), await cs.code(c), await cs.isAbstract(c), await cs.isInactive(c),
|
|
841
|
+
await cs.isDeprecated(c), await cs.getStatus(c), cds, await cs.definition(c), await cs.itemWeight(c),
|
|
842
|
+
expansion, null, await cs.extensions(c), null, await cs.properties(c), null, excludeInactive, vsSrc.url);
|
|
843
|
+
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
this.worker.opContext.log('iterate filters done');
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
async passesFilters(cs, c, prep, filters, offset) {
|
|
855
|
+
for (let j = offset; j < filters.length; j++) {
|
|
856
|
+
const f = filters[j];
|
|
857
|
+
// if (f instanceof SpecialProviderFilterContextNothing) {
|
|
858
|
+
// return false;
|
|
859
|
+
// } else if (f instanceof SpecialProviderFilterContextConcepts) {
|
|
860
|
+
// let ok = false;
|
|
861
|
+
// for (const t of f.list) {
|
|
862
|
+
// if (cs.sameContext(t, c)) {
|
|
863
|
+
// ok = true;
|
|
864
|
+
// }
|
|
865
|
+
// }
|
|
866
|
+
// if (!ok) return false;
|
|
867
|
+
// } else {
|
|
868
|
+
let ok = await cs.filterCheck(prep, f, c);
|
|
869
|
+
if (ok != true) {
|
|
870
|
+
return false;
|
|
871
|
+
}
|
|
872
|
+
// }
|
|
873
|
+
}
|
|
874
|
+
return true;
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
async excludeCodes(cset, path, vsSrc, filter, expansion, excludeInactive, notClosed) {
|
|
878
|
+
this.worker.deadCheck('processCodes#1');
|
|
879
|
+
const valueSets = [];
|
|
880
|
+
|
|
881
|
+
Extensions.checkNoModifiers(cset, 'ValueSetExpander.processCodes', 'set');
|
|
882
|
+
|
|
883
|
+
if (cset.valueSet || cset.concept || (cset.filter || []).length > 1) {
|
|
884
|
+
this.canBeHierarchy = false;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
if (!cset.system) {
|
|
888
|
+
if (cset.valueSet) {
|
|
889
|
+
this.noTotal();
|
|
890
|
+
for (const u of cset.valueSet) {
|
|
891
|
+
const s = this.worker.pinValueSet(u);
|
|
892
|
+
this.worker.deadCheck('processCodes#2');
|
|
893
|
+
const ivs = new ImportedValueSet(await this.expandValueSet(s, '', filter, notClosed));
|
|
894
|
+
this.checkResourceCanonicalStatus(expansion, ivs.valueSet, this.valueSet);
|
|
895
|
+
this.addParamUri(expansion, 'used-valueset', ivs.valueSet.vurl);
|
|
896
|
+
valueSets.push(ivs);
|
|
897
|
+
}
|
|
898
|
+
this.excludeValueSet(valueSets[0].valueSet, expansion, valueSets, 1);
|
|
899
|
+
}
|
|
900
|
+
} else {
|
|
901
|
+
const filters = [];
|
|
902
|
+
const prep = null;
|
|
903
|
+
const cs = await this.worker.findCodeSystem(cset.system, cset.version, this.params, ['complete', 'fragment'], false, true, true, null);
|
|
904
|
+
|
|
905
|
+
this.worker.checkSupplements(cs, cset, this.requiredSupplements);
|
|
906
|
+
this.checkResourceCanonicalStatus(expansion, cs, this.valueSet);
|
|
907
|
+
const sv = this.canonical(await cs.system(), await cs.version());
|
|
908
|
+
this.addParamUri(expansion, 'used-codesystem', sv);
|
|
909
|
+
|
|
910
|
+
for (const u of cset.valueSet || []) {
|
|
911
|
+
const s = this.pinValueSet(u);
|
|
912
|
+
this.worker.deadCheck('processCodes#3');
|
|
913
|
+
let f = null;
|
|
914
|
+
const vs = this.onGetValueSet(this, s, '');
|
|
915
|
+
if (vs != null) {
|
|
916
|
+
f = this.makeFilterForValueSet(cs, vs);
|
|
917
|
+
}
|
|
918
|
+
if (f != null) {
|
|
919
|
+
filters.push(f);
|
|
920
|
+
} else {
|
|
921
|
+
valueSets.push(new ImportedValueSet(await this.expandValueSet(s, '', filter, notClosed)));
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
if (!cset.concept && !cset.filter) {
|
|
926
|
+
this.opContext.log('handle system');
|
|
927
|
+
if (cs.specialEnumeration() && this.params.limitedExpansion && filters.length === 0) {
|
|
928
|
+
const base = await this.expandValueSet(cs.specialEnumeration(), '', filter, notClosed);
|
|
929
|
+
Extensions.addBoolean(expansion, 'http://hl7.org/fhir/StructureDefinition/valueset-toocostly', true);
|
|
930
|
+
this.excludeValueSet(base, expansion, valueSets, 0);
|
|
931
|
+
notClosed.value = true;
|
|
932
|
+
} else if (filter.isNull) {
|
|
933
|
+
if (cs.isNotClosed(filter)) {
|
|
934
|
+
if (cs.specialEnumeration()) {
|
|
935
|
+
throw new Issue("error", "too-costly", null, null, 'The code System "' + cs.system() + '" has a grammar, and cannot be enumerated directly. If an incomplete expansion is requested, a limited enumeration will be returned', null, 400).withDiagnostics(this.worker.opContext.diagnostics());
|
|
936
|
+
} else {
|
|
937
|
+
throw new Issue("error", "too-costly", null, null, 'The code System "' + cs.system + '" has a grammar, and cannot be enumerated directly', null, 400).withDiagnostics(this.worker.opContext.diagnostics());
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
const iter = await cs.getIterator(null);
|
|
942
|
+
if (valueSets.length === 0 && this.limitCount > 0 && iter.count > this.limitCount && !this.params.limitedExpansion) {
|
|
943
|
+
throw new Issue("error", "too-costly", null, 'VALUESET_TOO_COSTLY', this.worker.i18n.translate('VALUESET_TOO_COSTLY', this.params.httpLanguages, [vsSrc.url, '>' + this.limitCount]), null, 400).withDiagnostics(this.worker.opContext.diagnostics());
|
|
944
|
+
}
|
|
945
|
+
while (iter.more()) {
|
|
946
|
+
this.worker.deadCheck('processCodes#3a');
|
|
947
|
+
const c = await cs.getNextContext(iter);
|
|
948
|
+
if (await this.passesFilters(cs, c, prep, filters, 0)) {
|
|
949
|
+
await this.excludeCodeAndDescendants(cs, c, expansion, valueSets, excludeInactive, vsSrc.url);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
} else {
|
|
953
|
+
this.noTotal();
|
|
954
|
+
if (cs.isNotClosed(filter)) {
|
|
955
|
+
notClosed.value = true;
|
|
956
|
+
}
|
|
957
|
+
const prep = await cs.getPrepContext(true);
|
|
958
|
+
const ctxt = await cs.searchFilter(filter, prep, false);
|
|
959
|
+
await cs.prepare(prep);
|
|
960
|
+
while (await cs.filterMore(ctxt)) {
|
|
961
|
+
this.worker.deadCheck('processCodes#4');
|
|
962
|
+
const c = await cs.filterConcept(ctxt);
|
|
963
|
+
if (await this.passesFilters(cs, c, prep, filters, 0)) {
|
|
964
|
+
this.excludeCode(cs, await cs.system(), await cs.version(), await cs.code(c), expansion, valueSets, vsSrc.url);
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
if (cset.concept) {
|
|
971
|
+
this.worker.opContext.log('iterate concepts');
|
|
972
|
+
const cds = new Designations(this.worker.i18n.languageDefinitions);
|
|
973
|
+
for (const cc of cset.concept) {
|
|
974
|
+
this.worker.deadCheck('processCodes#3');
|
|
975
|
+
cds.clear();
|
|
976
|
+
Extensions.checkNoModifiers(cc, 'ValueSetExpander.processCodes', 'set concept reference');
|
|
977
|
+
const cctxt = await cs.locate(cc.code, this.allAltCodes);
|
|
978
|
+
if (cctxt && cctxt.context && (!this.params.activeOnly || !await cs.isInactive(cctxt)) && await this.passesFilters(cs, cctxt, prep, filters, 0)) {
|
|
979
|
+
if (filter.passesDesignations(cds) || filter.passes(cc.code)) {
|
|
980
|
+
let ov = Extensions.readString(cc, 'http://hl7.org/fhir/StructureDefinition/itemWeight');
|
|
981
|
+
if (!ov) {
|
|
982
|
+
ov = await cs.itemWeight(cctxt.context);
|
|
983
|
+
}
|
|
984
|
+
this.excludeCode(cs, await cs.system(), await cs.version(), cc.code, expansion, valueSets, vsSrc.url);
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
if (cset.filter) {
|
|
991
|
+
this.worker.opContext.log('prep filters');
|
|
992
|
+
const prep = await cs.getPrepContext(true);
|
|
993
|
+
if (!filter.isNull) {
|
|
994
|
+
await cs.searchFilter(filter, prep, true);
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
if (cs.specialEnumeration()) {
|
|
998
|
+
await cs.specialFilter(prep, true);
|
|
999
|
+
Extensions.addBoolean(expansion, 'http://hl7.org/fhir/StructureDefinition/valueset-toocostly', true);
|
|
1000
|
+
notClosed.value = true;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
for (let fc of cset.filter) {
|
|
1004
|
+
this.worker.deadCheck('processCodes#4a');
|
|
1005
|
+
Extensions.checkNoModifiers(fc, 'ValueSetExpander.processCodes', 'filter');
|
|
1006
|
+
await cs.filter(prep, fc.property, fc.op, fc.value);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
this.worker.opContext.log('iterate filters');
|
|
1010
|
+
const fset = await cs.executeFilters(prep);
|
|
1011
|
+
if (await cs.filtersNotClosed(prep)) {
|
|
1012
|
+
notClosed.value = true;
|
|
1013
|
+
}
|
|
1014
|
+
//let count = 0;
|
|
1015
|
+
while (await cs.filterMore(prep, fset[0])) {
|
|
1016
|
+
this.worker.deadCheck('processCodes#5');
|
|
1017
|
+
const c = await cs.filterConcept(prep, fset[0]);
|
|
1018
|
+
const ok = (!this.params.activeOnly || !await cs.isInactive(c)) && (await this.passesFilters(cs, c, prep, fset, 1));
|
|
1019
|
+
if (ok) {
|
|
1020
|
+
//count++;
|
|
1021
|
+
if (this.passesImports(valueSets, await cs.system(), await cs.code(c), 0)) {
|
|
1022
|
+
this.excludeCode(cs, await cs.system(), await cs.version(), await cs.code(c), expansion, null, vsSrc.url);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
this.worker.opContext.log('iterate filters finished');
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
async includeCodeAndDescendants(cs, context, expansion, imports, parent, excludeInactive, srcUrl) {
|
|
1032
|
+
let result = 0;
|
|
1033
|
+
this.worker.deadCheck('processCodeAndDescendants');
|
|
1034
|
+
|
|
1035
|
+
if (expansion) {
|
|
1036
|
+
const vs = this.canonical(await cs.system(), await cs.version());
|
|
1037
|
+
this.addParamUri(expansion, 'used-codesystem', vs);
|
|
1038
|
+
const ts = cs.listSupplements();
|
|
1039
|
+
for (const v of ts) {
|
|
1040
|
+
this.worker.deadCheck('processCodeAndDescendants');
|
|
1041
|
+
this.addParamUri(expansion, 'used-supplement', v);
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
let n = null;
|
|
1046
|
+
if ((!this.params.excludeNotForUI || !await cs.isAbstract(context)) && (!this.params.activeOnly || !await cs.isInactive(context))) {
|
|
1047
|
+
const cds = new Designations(this.worker.i18n.languageDefinitions);
|
|
1048
|
+
await this.listDisplaysFromProvider(cds, cs, context);
|
|
1049
|
+
const t = await this.includeCode(cs, parent, await cs.system(), await cs.version(), context.code, await cs.isAbstract(context), await cs.isInactive(context), await cs.isDeprecated(context), await cs.getStatus(context), cds, await cs.definition(context),
|
|
1050
|
+
await cs.itemWeight(context), expansion, imports, await cs.extensions(context), null, await cs.properties(context), null, excludeInactive, srcUrl);
|
|
1051
|
+
if (t != null) {
|
|
1052
|
+
result++;
|
|
1053
|
+
}
|
|
1054
|
+
if (n == null) {
|
|
1055
|
+
n = t;
|
|
1056
|
+
}
|
|
1057
|
+
} else {
|
|
1058
|
+
n = parent;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
const iter = await cs.iterator(context);
|
|
1062
|
+
if (iter) {
|
|
1063
|
+
let c = await cs.nextContext(iter);
|
|
1064
|
+
while (c) {
|
|
1065
|
+
this.worker.deadCheck('processCodeAndDescendants#3');
|
|
1066
|
+
result += await this.includeCodeAndDescendants(cs, c, expansion, imports, n, excludeInactive, srcUrl);
|
|
1067
|
+
c = await cs.nextContext(iter);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
return result;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
async excludeCodeAndDescendants(cs, context, expansion, imports, excludeInactive, srcUrl) {
|
|
1074
|
+
this.worker.deadCheck('processCodeAndDescendants');
|
|
1075
|
+
|
|
1076
|
+
if (expansion) {
|
|
1077
|
+
const vs = this.canonical(await cs.system(), await cs.version());
|
|
1078
|
+
this.addParamUri(expansion, 'used-codesystem', vs);
|
|
1079
|
+
const ts= cs.listSupplements();
|
|
1080
|
+
for (const v of ts) {
|
|
1081
|
+
this.worker.deadCheck('processCodeAndDescendants');
|
|
1082
|
+
this.addParamUri(expansion, 'used-supplement', v);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
if ((!this.params.excludeNotForUI || !await cs.isAbstract(context)) && (!this.params.activeOnly || !await cs.isInactive(context))) {
|
|
1087
|
+
const cds = new Designations(this.worker.i18n.languageDefinitions);
|
|
1088
|
+
await this.listDisplaysFromProvider(cds, cs, context);
|
|
1089
|
+
for (const code of await cs.listCodes(context, this.params.altCodeRules)) {
|
|
1090
|
+
this.worker.deadCheck('processCodeAndDescendants#2');
|
|
1091
|
+
this.excludeCode(cs, await cs.system(), await cs.version(), code, expansion, imports, srcUrl);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
const iter = await cs.getIterator(context);
|
|
1096
|
+
while (iter.more()) {
|
|
1097
|
+
this.worker.deadCheck('processCodeAndDescendants#3');
|
|
1098
|
+
const c = await cs.getNextContext(iter);
|
|
1099
|
+
await this.excludeCodeAndDescendants(cs, c, expansion, imports, excludeInactive, srcUrl);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
async handleCompose(source, filter, expansion, notClosed) {
|
|
1104
|
+
this.worker.opContext.log('compose #1');
|
|
1105
|
+
|
|
1106
|
+
const ts = new Map();
|
|
1107
|
+
for (const c of source.jsonObj.compose.include || []) {
|
|
1108
|
+
this.worker.deadCheck('handleCompose#2');
|
|
1109
|
+
await this.checkSource(c, expansion, filter, source.url, ts);
|
|
1110
|
+
}
|
|
1111
|
+
for (const c of source.jsonObj.compose.exclude || []) {
|
|
1112
|
+
this.worker.deadCheck('handleCompose#3');
|
|
1113
|
+
this.hasExclusions = true;
|
|
1114
|
+
await this.checkSource(c, expansion, filter, source.url, ts);
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
this.worker.opContext.log('compose #2');
|
|
1118
|
+
|
|
1119
|
+
let i = 0;
|
|
1120
|
+
for (const c of source.jsonObj.compose.exclude || []) {
|
|
1121
|
+
this.worker.deadCheck('handleCompose#4');
|
|
1122
|
+
await this.excludeCodes(c, "ValueSet.compose.exclude["+i+"]", source, filter, expansion, this.excludeInactives(source), notClosed);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
i = 0;
|
|
1126
|
+
for (const c of source.jsonObj.compose.include || []) {
|
|
1127
|
+
this.worker.deadCheck('handleCompose#5');
|
|
1128
|
+
await this.includeCodes(c, "ValueSet.compose.include["+i+"]", source, filter, expansion, this.excludeInactives(source), notClosed);
|
|
1129
|
+
i++;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
excludeInactives(source) {
|
|
1134
|
+
return source.jsonObj.compose && source.jsonObj.compose.inactive != undefined && !source.jsonObj.compose.inactive;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
async expand(source, filter, noCacheThisOne) {
|
|
1138
|
+
this.noCacheThisOne = noCacheThisOne;
|
|
1139
|
+
this.totalStatus = 'uninitialised';
|
|
1140
|
+
this.total = 0;
|
|
1141
|
+
|
|
1142
|
+
Extensions.checkNoImplicitRules(source,'ValueSetExpander.Expand', 'ValueSet');
|
|
1143
|
+
Extensions.checkNoModifiers(source,'ValueSetExpander.Expand', 'ValueSet');
|
|
1144
|
+
this.worker.seeValueSet(source, this.params);
|
|
1145
|
+
this.valueSet = source;
|
|
1146
|
+
|
|
1147
|
+
const result = structuredClone(source.jsonObj);
|
|
1148
|
+
result.id = '';
|
|
1149
|
+
let table = null;
|
|
1150
|
+
let div_ = null;
|
|
1151
|
+
|
|
1152
|
+
if (!this.params.includeDefinition) {
|
|
1153
|
+
result.purpose = undefined;
|
|
1154
|
+
result.compose = undefined;
|
|
1155
|
+
result.description = undefined;
|
|
1156
|
+
result.contactList = undefined;
|
|
1157
|
+
result.copyright = undefined;
|
|
1158
|
+
result.publisher = undefined;
|
|
1159
|
+
result.extension = undefined;
|
|
1160
|
+
result.text = undefined;
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
this.requiredSupplements = [];
|
|
1164
|
+
for (const ext of Extensions.list(source.jsonObj, 'http://hl7.org/fhir/StructureDefinition/valueset-supplement')) {
|
|
1165
|
+
this.requiredSupplements.push(getValuePrimitive(ext));
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
if (result.expansion) {
|
|
1169
|
+
return result; // just return the expansion
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
if (this.params.generateNarrative) {
|
|
1173
|
+
div_ = div();
|
|
1174
|
+
table = div_.table("grid");
|
|
1175
|
+
} else {
|
|
1176
|
+
result.text = undefined;
|
|
1177
|
+
}
|
|
1178
|
+
|
|
1179
|
+
this.map = new Map();
|
|
1180
|
+
this.rootList = [];
|
|
1181
|
+
this.fullList = [];
|
|
1182
|
+
this.canBeHierarchy = !this.params.excludeNested;
|
|
1183
|
+
|
|
1184
|
+
this.limitCount = INTERNAL_LIMIT;
|
|
1185
|
+
if (this.params.limit <= 0) {
|
|
1186
|
+
if (!filter.isNull) {
|
|
1187
|
+
this.limitCount = UPPER_LIMIT_TEXT;
|
|
1188
|
+
} else {
|
|
1189
|
+
this.limitCount = UPPER_LIMIT_NO_TEXT;
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
this.offset = this.params.offset;
|
|
1193
|
+
this.count = this.params.count;
|
|
1194
|
+
|
|
1195
|
+
if (this.params.offset > 0) {
|
|
1196
|
+
this.canBeHierarchy = false;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
const exp = {};
|
|
1200
|
+
exp.timestamp = new Date().toISOString();
|
|
1201
|
+
exp.identifier = 'urn:uuid:' + crypto.randomUUID();
|
|
1202
|
+
result.expansion = exp;
|
|
1203
|
+
|
|
1204
|
+
if (!filter.isNull) {
|
|
1205
|
+
this.addParamStr(exp, 'filter', filter.filter);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
if (this.params.hasLimitedExpansion) {
|
|
1209
|
+
this.addParamBool(exp, 'limitedExpansion', this.params.limitedExpansion);
|
|
1210
|
+
}
|
|
1211
|
+
if (this.params.DisplayLanguages) {
|
|
1212
|
+
this.addParamCode(exp, 'displayLanguage', this.params.DisplayLanguages.asString(true));
|
|
1213
|
+
} else if (this.params.HTTPLanguages) {
|
|
1214
|
+
this.addParamCode(exp, 'displayLanguage', this.params.HTTPLanguages.asString(true));
|
|
1215
|
+
}
|
|
1216
|
+
if (this.params.designations) {
|
|
1217
|
+
for (const s of this.params.designations) {
|
|
1218
|
+
this.addParamStr(exp, 'designation', s);
|
|
1219
|
+
}
|
|
1220
|
+
}
|
|
1221
|
+
if (this.params.hasExcludeNested) {
|
|
1222
|
+
this.addParamBool(exp, 'excludeNested', this.params.excludeNested);
|
|
1223
|
+
}
|
|
1224
|
+
if (this.params.hasActiveOnly) {
|
|
1225
|
+
this.addParamBool(exp, 'activeOnly', this.params.activeOnly);
|
|
1226
|
+
}
|
|
1227
|
+
if (this.params.hasIncludeDesignations) {
|
|
1228
|
+
this.addParamBool(exp, 'includeDesignations', this.params.includeDesignations);
|
|
1229
|
+
}
|
|
1230
|
+
if (this.params.hasIncludeDefinition) {
|
|
1231
|
+
this.addParamBool(exp, 'includeDefinition', this.params.includeDefinition);
|
|
1232
|
+
}
|
|
1233
|
+
if (this.params.hasExcludeNotForUI) {
|
|
1234
|
+
this.addParamBool(exp, 'excludeNotForUI', this.params.excludeNotForUI);
|
|
1235
|
+
}
|
|
1236
|
+
if (this.params.hasExcludePostCoordinated) {
|
|
1237
|
+
this.addParamBool(exp, 'excludePostCoordinated', this.params.excludePostCoordinated);
|
|
1238
|
+
}
|
|
1239
|
+
|
|
1240
|
+
this.checkResourceCanonicalStatus(exp, source, source);
|
|
1241
|
+
|
|
1242
|
+
if (this.offset > -1) {
|
|
1243
|
+
this.addParamInt(exp,'offset', this.offset);
|
|
1244
|
+
exp.offset = this.offset;
|
|
1245
|
+
}
|
|
1246
|
+
if (this.count > -1) {
|
|
1247
|
+
this.addParamInt(exp, 'count', this.count);
|
|
1248
|
+
}
|
|
1249
|
+
if (this.count > 0 && this.offset === -1) {
|
|
1250
|
+
this.offset = 0;
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
this.worker.opContext.log('start working');
|
|
1254
|
+
this.worker.deadCheck('expand');
|
|
1255
|
+
|
|
1256
|
+
let notClosed = { value : false};
|
|
1257
|
+
|
|
1258
|
+
try {
|
|
1259
|
+
if (source.jsonObj.compose && Extensions.checkNoModifiers(source.jsonObj.compose, 'ValueSetExpander.Expand', 'compose')) {
|
|
1260
|
+
await this.handleCompose(source, filter, exp, notClosed);
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
if (this.requiredSupplements.length > 0) {
|
|
1264
|
+
throw new Issue('error', 'not-found', null, 'VALUESET_SUPPLEMENT_MISSING', this.worker.opContext.i18n.translatePlural(this.requiredSupplements.length, 'VALUESET_SUPPLEMENT_MISSING', this.params.httpLanguages, [this.requiredSupplements.join(', ')]), 'not-found', 400);
|
|
1265
|
+
}
|
|
1266
|
+
} catch (e) {
|
|
1267
|
+
if (e instanceof Issue) {
|
|
1268
|
+
if (e.finished) {
|
|
1269
|
+
// nothing - we're just trapping this
|
|
1270
|
+
if (this.totalStatus === 'uninitialised') {
|
|
1271
|
+
this.totalStatus = 'off';
|
|
1272
|
+
} else if (e.toocostly) {
|
|
1273
|
+
if (this.params.limitedExpansion) {
|
|
1274
|
+
Extensions.addBoolean(exp, 'http://hl7.org/fhir/StructureDefinition/valueset-toocostly', 'value', true);
|
|
1275
|
+
if (table != null) {
|
|
1276
|
+
div_.p().style('color: Maroon').tx(e.message);
|
|
1277
|
+
}
|
|
1278
|
+
} else {
|
|
1279
|
+
throw e;
|
|
1280
|
+
}
|
|
1281
|
+
} else {
|
|
1282
|
+
// nothing- swallow it
|
|
1283
|
+
}
|
|
1284
|
+
} else {
|
|
1285
|
+
throw e;
|
|
1286
|
+
}
|
|
1287
|
+
} else {
|
|
1288
|
+
throw e;
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
this.worker.opContext.log('finish up');
|
|
1293
|
+
|
|
1294
|
+
let list;
|
|
1295
|
+
if (notClosed.value) {
|
|
1296
|
+
Extensions.addBoolean(exp, 'http://hl7.org/fhir/StructureDefinition/valueset-unclosed', true);
|
|
1297
|
+
list = this.fullList;
|
|
1298
|
+
for (const c of this.fullList) {
|
|
1299
|
+
c.contains = undefined;
|
|
1300
|
+
}
|
|
1301
|
+
if (table != null) {
|
|
1302
|
+
div_.addTag('p').setAttribute('style', 'color: Navy').tx('Because of the way that this value set is defined, not all the possible codes can be listed in advance');
|
|
1303
|
+
}
|
|
1304
|
+
} else {
|
|
1305
|
+
if (this.totalStatus === 'off' || this.total === -1) {
|
|
1306
|
+
this.canBeHierarchy = false;
|
|
1307
|
+
} else if (this.total > 0) {
|
|
1308
|
+
exp.total = this.total;
|
|
1309
|
+
} else {
|
|
1310
|
+
exp.total = this.fullList.length;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
if (this.canBeHierarchy && (this.count <= 0 || this.count > this.fullList.length)) {
|
|
1314
|
+
list = this.rootList;
|
|
1315
|
+
} else {
|
|
1316
|
+
list = this.fullList;
|
|
1317
|
+
for (const c of this.fullList) {
|
|
1318
|
+
c.contains = undefined;
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
if (this.offset + this.count < 0 && this.fullList.length > this.limit) {
|
|
1324
|
+
this.log.log('Operation took too long @ expand (' + this.constructor.name + ')');
|
|
1325
|
+
throw new Issue("error", "too-costly", null, 'VALUESET_TOO_COSTLY', this.worker.i18n.translate('VALUESET_TOO_COSTLY', this.params.httpLanguages, [source.vurl, '>' + this.limit]), null, 400).withDiagnostics(this.worker.opContext.diagnostics());
|
|
1326
|
+
} else {
|
|
1327
|
+
let t = 0;
|
|
1328
|
+
let o = 0;
|
|
1329
|
+
for (let i = 0; i < list.length; i++) {
|
|
1330
|
+
this.worker.deadCheck('expand#1');
|
|
1331
|
+
const c = list[i];
|
|
1332
|
+
if (this.map.has(this.keyC(c))) {
|
|
1333
|
+
o++;
|
|
1334
|
+
if (o > this.offset && (this.count <= 0 || t < this.count)) {
|
|
1335
|
+
t++;
|
|
1336
|
+
if (!exp.contains) {
|
|
1337
|
+
exp.contains = [];
|
|
1338
|
+
}
|
|
1339
|
+
exp.contains.push(c);
|
|
1340
|
+
if (table != null) {
|
|
1341
|
+
const tr = table.tr();
|
|
1342
|
+
tr.td().tx(c.system);
|
|
1343
|
+
tr.td().tx(c.code);
|
|
1344
|
+
tr.td().tx(c.display);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
for (const s of this.worker.foundParameters) {
|
|
1352
|
+
const [l, r] = s.split('=');
|
|
1353
|
+
if (r != source.vurl) {
|
|
1354
|
+
this.addParamUri(exp, l, r);
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
return result;
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
checkResourceCanonicalStatus(exp, resource, source) {
|
|
1362
|
+
if (resource.jsonObj) {
|
|
1363
|
+
resource = resource.jsonObj;
|
|
1364
|
+
}
|
|
1365
|
+
this.checkCanonicalStatus(exp, this.worker.makeVurl(resource), resource.status, Extensions.readString(resource, 'http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status'), resource.experimental, source);
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
checkProviderCanonicalStatus(exp, cs, source) {
|
|
1369
|
+
let status = cs.status();
|
|
1370
|
+
this.checkCanonicalStatus(exp, cs.vurl(), status.status, status.standardsStatus, status.experimental, source);
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
checkCanonicalStatus(exp, vurl, status, standardsStatus, experimental, source) {
|
|
1374
|
+
if (standardsStatus == 'deprecated') {
|
|
1375
|
+
this.addParamUri(exp, 'warning-deprecated', vurl);
|
|
1376
|
+
} else if (standardsStatus == 'withdrawn') {
|
|
1377
|
+
this.addParamUri(exp, 'warning-withdrawn', vurl);
|
|
1378
|
+
} else if (status == 'retired') {
|
|
1379
|
+
this.addParamUri(exp, 'warning-retired', vurl);
|
|
1380
|
+
} else if (experimental && !source.experimental) {
|
|
1381
|
+
this.addParamUri(exp, 'warning-experimental', vurl)
|
|
1382
|
+
} else if (((status == 'draft') || (standardsStatus == 'draft')) &&
|
|
1383
|
+
!((source.status == 'draft') || (Extensions.readString(source, 'http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status') == 'draft'))) {
|
|
1384
|
+
this.addParamUri(exp, 'warning-draft', vurl)
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
addParamStr(exp, name, value) {
|
|
1389
|
+
if (!this.hasParam(exp, name, value)) {
|
|
1390
|
+
if (!exp.parameter) {
|
|
1391
|
+
exp.parameter = [];
|
|
1392
|
+
}
|
|
1393
|
+
exp.parameter.push({name: name, valueString: value});
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
addParamBool(exp, name, value) {
|
|
1398
|
+
if (!this.hasParam(exp, name, value)) {
|
|
1399
|
+
if (!exp.parameter) {
|
|
1400
|
+
exp.parameter = [];
|
|
1401
|
+
}
|
|
1402
|
+
exp.parameter.push({name: name, valueBoolean: value});
|
|
1403
|
+
}
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
addParamCode(exp, name, value) {
|
|
1407
|
+
if (!this.hasParam(exp, name, value)) {
|
|
1408
|
+
if (!exp.parameter) {
|
|
1409
|
+
exp.parameter = [];
|
|
1410
|
+
}
|
|
1411
|
+
exp.parameter.push({name: name, valueCode: value});
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
addParamInt(exp, name, value) {
|
|
1416
|
+
if (!this.hasParam(exp, name, value)) {
|
|
1417
|
+
if (!exp.parameter) {
|
|
1418
|
+
exp.parameter = [];
|
|
1419
|
+
}
|
|
1420
|
+
exp.parameter.push({name: name, valueInteger: value});
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
addParamUri(exp, name, value) {
|
|
1425
|
+
if (!this.hasParam(exp, name, value)) {
|
|
1426
|
+
if (!exp.parameter) {
|
|
1427
|
+
exp.parameter = [];
|
|
1428
|
+
}
|
|
1429
|
+
exp.parameter.push({name: name, valueUri: value});
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
addParam(exp, name, valueName, value) {
|
|
1434
|
+
if (!this.hasParam(exp, name, value)) {
|
|
1435
|
+
if (!exp.parameter) {
|
|
1436
|
+
exp.parameter = [];
|
|
1437
|
+
}
|
|
1438
|
+
let p = {name: name}
|
|
1439
|
+
exp.parameter.push(p);
|
|
1440
|
+
p[valueName] = value;
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
|
|
1445
|
+
hasParam(exp, name, value) {
|
|
1446
|
+
return (exp.parameter || []).find((ex => ex.name == name && getValuePrimitive(ex) == value));
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
isExcluded(system, version, code) {
|
|
1450
|
+
return this.excluded.has(system+'|'+version+'#'+code);
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
keyS(system, version, code) {
|
|
1454
|
+
return system+"~"+(this.doingVersion ? version+"~" : "")+code;
|
|
1455
|
+
}
|
|
1456
|
+
|
|
1457
|
+
keyC(contains) {
|
|
1458
|
+
return this.keyS(contains.system, contains.version, contains.code);
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
defineProperty(expansion, contains, url, code, valueName, value) {
|
|
1462
|
+
if (value === undefined || value == null) {
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
if (!expansion.property) {
|
|
1466
|
+
expansion.property = [];
|
|
1467
|
+
}
|
|
1468
|
+
let pd = expansion.property.find(t1 => t1.uri == url || t1.code == code);
|
|
1469
|
+
if (!pd) {
|
|
1470
|
+
pd = {};
|
|
1471
|
+
expansion.property.push(pd);
|
|
1472
|
+
pd.uri = url;
|
|
1473
|
+
pd.code = code;
|
|
1474
|
+
} else if (!pd.uri) {
|
|
1475
|
+
pd.uri = url
|
|
1476
|
+
}
|
|
1477
|
+
if (pd.uri != url) {
|
|
1478
|
+
throw new Error('URL mismatch on expansion: ' + pd.uri + ' vs ' + url + ' for code ' + code);
|
|
1479
|
+
} else {
|
|
1480
|
+
code = pd.code;
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
if (!contains.property) {
|
|
1484
|
+
contains.property = [];
|
|
1485
|
+
}
|
|
1486
|
+
let pdv = contains.property.find(t2 => t2.code == code);
|
|
1487
|
+
if (!pdv) {
|
|
1488
|
+
pdv = {};
|
|
1489
|
+
contains.property.push(pdv);
|
|
1490
|
+
pdv.code = code;
|
|
1491
|
+
}
|
|
1492
|
+
pdv[valueName] = value;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
addToTotal(t) {
|
|
1496
|
+
if (this.total > -1 && this.totalStatus != "off") {
|
|
1497
|
+
this.total = this.total + t;
|
|
1498
|
+
this.totalStatus = 'set';
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
noTotal() {
|
|
1503
|
+
this.total = -1;
|
|
1504
|
+
this.totalStatus = 'off';
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
getPropUrl(cs, pn) {
|
|
1508
|
+
for (let p of cs.propertyDefinitions()) {
|
|
1509
|
+
if (pn == p.code) {
|
|
1510
|
+
return p.uri;
|
|
1511
|
+
}
|
|
1512
|
+
}
|
|
1513
|
+
return undefined;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
class ExpandWorker extends TerminologyWorker {
|
|
1520
|
+
/**
|
|
1521
|
+
* @param {OperationContext} opContext - Operation context
|
|
1522
|
+
* @param {Logger} log - Logger instance
|
|
1523
|
+
* @param {Provider} provider - Provider for code systems and resources
|
|
1524
|
+
* @param {LanguageDefinitions} languages - Language definitions
|
|
1525
|
+
* @param {I18nSupport} i18n - Internationalization support
|
|
1526
|
+
*/
|
|
1527
|
+
constructor(opContext, log, provider, languages, i18n) {
|
|
1528
|
+
super(opContext, log, provider, languages, i18n);
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
/**
|
|
1532
|
+
* Get operation name
|
|
1533
|
+
* @returns {string}
|
|
1534
|
+
*/
|
|
1535
|
+
opName() {
|
|
1536
|
+
return 'expand';
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
/**
|
|
1540
|
+
* Handle a type-level $expand request
|
|
1541
|
+
* GET/POST /ValueSet/$expand
|
|
1542
|
+
* @param {express.Request} req - Express request
|
|
1543
|
+
* @param {express.Response} res - Express response
|
|
1544
|
+
*/
|
|
1545
|
+
async handle(req, res) {
|
|
1546
|
+
try {
|
|
1547
|
+
await this.handleTypeLevelExpand(req, res);
|
|
1548
|
+
} catch (error) {
|
|
1549
|
+
req.logInfo = this.usedSources.join("|")+" - error"+(error.msgId ? " "+error.msgId : "");
|
|
1550
|
+
this.log.error(error);
|
|
1551
|
+
const statusCode = error.statusCode || 500;
|
|
1552
|
+
if (error instanceof Issue) {
|
|
1553
|
+
let oo = new OperationOutcome();
|
|
1554
|
+
oo.addIssue(error);
|
|
1555
|
+
return res.status(error.statusCode || 500).json(this.fixForVersion(oo.jsonObj));
|
|
1556
|
+
} else {
|
|
1557
|
+
const issueCode = error.issueCode || 'exception';
|
|
1558
|
+
return res.status(statusCode).json(this.fixForVersion({
|
|
1559
|
+
resourceType: 'OperationOutcome',
|
|
1560
|
+
issue: [{
|
|
1561
|
+
severity: 'error',
|
|
1562
|
+
code: issueCode,
|
|
1563
|
+
details: {
|
|
1564
|
+
text: error.message
|
|
1565
|
+
},
|
|
1566
|
+
diagnostics: error.message
|
|
1567
|
+
}]
|
|
1568
|
+
}));
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
|
|
1573
|
+
/**
|
|
1574
|
+
* Handle an instance-level $expand request
|
|
1575
|
+
* GET/POST /ValueSet/{id}/$expand
|
|
1576
|
+
* @param {express.Request} req - Express request
|
|
1577
|
+
* @param {express.Response} res - Express response
|
|
1578
|
+
*/
|
|
1579
|
+
async handleInstance(req, res) {
|
|
1580
|
+
try {
|
|
1581
|
+
await this.handleInstanceLevelExpand(req, res);
|
|
1582
|
+
} catch (error) {
|
|
1583
|
+
req.logInfo = this.usedSources.join("|")+" - error"+(error.msgId ? " "+error.msgId : "");
|
|
1584
|
+
this.log.error(error);
|
|
1585
|
+
const statusCode = error.statusCode || 500;
|
|
1586
|
+
const issueCode = error.issueCode || 'exception';
|
|
1587
|
+
return res.status(statusCode).json(this.fixForVersion({
|
|
1588
|
+
resourceType: 'OperationOutcome',
|
|
1589
|
+
issue: [{
|
|
1590
|
+
severity: 'error',
|
|
1591
|
+
code: issueCode,
|
|
1592
|
+
details: {
|
|
1593
|
+
text : error.message
|
|
1594
|
+
},
|
|
1595
|
+
diagnostics: error.message
|
|
1596
|
+
}]
|
|
1597
|
+
}));
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
/**
|
|
1602
|
+
* Handle type-level expand: /ValueSet/$expand
|
|
1603
|
+
* ValueSet identified by url, or provided directly in body
|
|
1604
|
+
*/
|
|
1605
|
+
async handleTypeLevelExpand(req, res) {
|
|
1606
|
+
this.deadCheck('expand-type-level');
|
|
1607
|
+
|
|
1608
|
+
// Determine how the request is structured
|
|
1609
|
+
let valueSet = null;
|
|
1610
|
+
let params = null;
|
|
1611
|
+
|
|
1612
|
+
if (req.method === 'POST' && req.body) {
|
|
1613
|
+
if (req.body.resourceType === 'ValueSet') {
|
|
1614
|
+
// Body is directly a ValueSet resource
|
|
1615
|
+
valueSet = new ValueSet(req.body);
|
|
1616
|
+
params = this.queryToParameters(req.query);
|
|
1617
|
+
this.seeSourceVS(valueSet);
|
|
1618
|
+
|
|
1619
|
+
} else if (req.body.resourceType === 'Parameters') {
|
|
1620
|
+
// Body is a Parameters resource
|
|
1621
|
+
params = req.body;
|
|
1622
|
+
|
|
1623
|
+
// Check for valueSet parameter
|
|
1624
|
+
const valueSetParam = this.findParameter(params, 'valueSet');
|
|
1625
|
+
if (valueSetParam && valueSetParam.resource) {
|
|
1626
|
+
valueSet = new ValueSet(valueSetParam.resource);
|
|
1627
|
+
this.seeSourceVS(valueSet);
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
} else {
|
|
1631
|
+
// Assume form body - convert to Parameters
|
|
1632
|
+
params = this.formToParameters(req.body, req.query);
|
|
1633
|
+
}
|
|
1634
|
+
} else {
|
|
1635
|
+
// GET request - convert query to Parameters
|
|
1636
|
+
params = this.queryToParameters(req.query);
|
|
1637
|
+
}
|
|
1638
|
+
this.addHttpParams(req, params);
|
|
1639
|
+
|
|
1640
|
+
// Check for context parameter - not supported yet
|
|
1641
|
+
const contextParam = this.findParameter(params, 'context');
|
|
1642
|
+
if (contextParam) {
|
|
1643
|
+
return res.status(400).json(this.operationOutcome('error', 'not-supported',
|
|
1644
|
+
'The context parameter is not yet supported'));
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
// Handle tx-resource and cache-id parameters
|
|
1648
|
+
this.setupAdditionalResources(params);
|
|
1649
|
+
const logExtraOutput = this.findParameter(params, 'logExtraOutput');
|
|
1650
|
+
|
|
1651
|
+
let txp = new TxParameters(this.opContext.i18n.languageDefinitions, this.opContext.i18n, false);
|
|
1652
|
+
txp.readParams(params);
|
|
1653
|
+
|
|
1654
|
+
// If no valueSet yet, try to find by url
|
|
1655
|
+
if (!valueSet) {
|
|
1656
|
+
const urlParam = this.findParameter(params, 'url');
|
|
1657
|
+
const versionParam = this.findParameter(params, 'valueSetVersion');
|
|
1658
|
+
|
|
1659
|
+
if (!urlParam) {
|
|
1660
|
+
return res.status(400).json(this.operationOutcome('error', 'invalid',
|
|
1661
|
+
'Must provide either a ValueSet resource or a url parameter'));
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
const url = this.getParameterValue(urlParam);
|
|
1665
|
+
const version = versionParam ? this.getParameterValue(versionParam) : null;
|
|
1666
|
+
|
|
1667
|
+
valueSet = await this.findValueSet(url, version);
|
|
1668
|
+
this.seeSourceVS(valueSet, url);
|
|
1669
|
+
if (!valueSet) {
|
|
1670
|
+
return res.status(404).json(this.operationOutcome('error', 'not-found',
|
|
1671
|
+
version ? `ValueSet not found: ${url} version ${version}` : `ValueSet not found: ${url}`));
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1675
|
+
// Perform the expansion
|
|
1676
|
+
const result = await this.doExpand(valueSet, txp, logExtraOutput);
|
|
1677
|
+
req.logInfo = this.usedSources.join("|")+txp.logInfo();
|
|
1678
|
+
return res.json(this.fixForVersion(result));
|
|
1679
|
+
}
|
|
1680
|
+
|
|
1681
|
+
/**
|
|
1682
|
+
* Handle instance-level expand: /ValueSet/{id}/$expand
|
|
1683
|
+
* ValueSet identified by resource ID
|
|
1684
|
+
*/
|
|
1685
|
+
async handleInstanceLevelExpand(req, res) {
|
|
1686
|
+
this.deadCheck('expand-instance-level');
|
|
1687
|
+
|
|
1688
|
+
const { id } = req.params;
|
|
1689
|
+
|
|
1690
|
+
// Find the ValueSet by ID
|
|
1691
|
+
const valueSet = await this.provider.getValueSetById(this.opContext, id);
|
|
1692
|
+
|
|
1693
|
+
if (!valueSet) {
|
|
1694
|
+
return res.status(404).json(this.operationOutcome('error', 'not-found',
|
|
1695
|
+
`ValueSet/${id} not found`));
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
// Parse parameters
|
|
1699
|
+
let params;
|
|
1700
|
+
if (req.method === 'POST' && req.body) {
|
|
1701
|
+
if (req.body.resourceType === 'Parameters') {
|
|
1702
|
+
params = req.body;
|
|
1703
|
+
} else {
|
|
1704
|
+
// Form body
|
|
1705
|
+
params = this.formToParameters(req.body, req.query);
|
|
1706
|
+
}
|
|
1707
|
+
} else {
|
|
1708
|
+
params = this.queryToParameters(req.query);
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
// Check for context parameter - not supported yet
|
|
1712
|
+
const contextParam = this.findParameter(params, 'context');
|
|
1713
|
+
if (contextParam) {
|
|
1714
|
+
return res.status(400).json(this.operationOutcome('error', 'not-supported',
|
|
1715
|
+
'The context parameter is not yet supported'));
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
// Handle tx-resource and cache-id parameters
|
|
1719
|
+
this.setupAdditionalResources(params);
|
|
1720
|
+
const logExtraOutput = this.findParameter(params, 'logExtraOutput');
|
|
1721
|
+
|
|
1722
|
+
let txp = new TxParameters(this.opContext.i18n.languageDefinitions, this.opContext.i18n, false);
|
|
1723
|
+
txp.readParams(params);
|
|
1724
|
+
|
|
1725
|
+
// Perform the expansion
|
|
1726
|
+
const result = await this.doExpand(valueSet, txp, logExtraOutput);
|
|
1727
|
+
req.logInfo = this.usedSources.join("|")+txp.logInfo();
|
|
1728
|
+
return res.json(this.fixForVersion(result));
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
// Note: setupAdditionalResources, queryToParameters, formToParameters,
|
|
1732
|
+
// findParameter, getParameterValue, and wrapRawResource are inherited
|
|
1733
|
+
// from TerminologyWorker base class
|
|
1734
|
+
|
|
1735
|
+
/**
|
|
1736
|
+
* Perform the actual expansion operation
|
|
1737
|
+
* Uses expansion cache for expensive operations
|
|
1738
|
+
* @param {Object} valueSet - ValueSet resource to expand
|
|
1739
|
+
* @param {Object} params - Parameters resource with expansion options
|
|
1740
|
+
* @returns {Object} Expanded ValueSet resource
|
|
1741
|
+
*/
|
|
1742
|
+
async doExpand(valueSet, params, logExtraOutput) {
|
|
1743
|
+
this.deadCheck('doExpand');
|
|
1744
|
+
|
|
1745
|
+
const expansionCache = this.opContext.expansionCache;
|
|
1746
|
+
// Compute cache key (only if caching is available and not debugging)
|
|
1747
|
+
let cacheKey = null;
|
|
1748
|
+
if (expansionCache && (CACHE_WHEN_DEBUGGING || !this.opContext.debugging)) {
|
|
1749
|
+
cacheKey = expansionCache.computeKey(valueSet, params, this.additionalResources);
|
|
1750
|
+
|
|
1751
|
+
// Check for cached expansion
|
|
1752
|
+
const cached = expansionCache.get(cacheKey);
|
|
1753
|
+
if (cached) {
|
|
1754
|
+
this.log.debug('Using cached expansion');
|
|
1755
|
+
return cached;
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
|
|
1759
|
+
// Perform the actual expansion
|
|
1760
|
+
const startTime = performance.now();
|
|
1761
|
+
const result = await this.performExpansion(valueSet, params, logExtraOutput);
|
|
1762
|
+
const durationMs = performance.now() - startTime;
|
|
1763
|
+
|
|
1764
|
+
// Cache if it took long enough (and not debugging)
|
|
1765
|
+
if (cacheKey && expansionCache && (CACHE_WHEN_DEBUGGING || !this.opContext.debugging)) {
|
|
1766
|
+
const wasCached = expansionCache.set(cacheKey, result, durationMs);
|
|
1767
|
+
if (wasCached) {
|
|
1768
|
+
this.log.debug(`Cached expansion (took ${Math.round(durationMs)}ms)`);
|
|
1769
|
+
}
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
return result;
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
/**
|
|
1776
|
+
* Perform the actual expansion logic
|
|
1777
|
+
* @param {Object} valueSet - ValueSet resource to expand
|
|
1778
|
+
* @param {Object} params - Parameters resource with expansion options
|
|
1779
|
+
* @returns {Object} Expanded ValueSet resource
|
|
1780
|
+
*/
|
|
1781
|
+
async performExpansion(valueSet, params, logExtraOutput) {
|
|
1782
|
+
this.deadCheck('performExpansion');
|
|
1783
|
+
|
|
1784
|
+
// Store params for worker methods
|
|
1785
|
+
this.params = params;
|
|
1786
|
+
|
|
1787
|
+
if (params.limit < -1) {
|
|
1788
|
+
params.limit = -1;
|
|
1789
|
+
} else if (params.limit > UPPER_LIMIT_TEXT) {
|
|
1790
|
+
params.limit = UPPER_LIMIT_TEXT; // can't ask for more than this externally, though you can internally
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
const filter = new SearchFilterText(params.filter);
|
|
1794
|
+
//txResources = processAdditionalResources(context, manager, nil, params);
|
|
1795
|
+
// Create expander and run expansion
|
|
1796
|
+
const expander = new ValueSetExpander(this, params);
|
|
1797
|
+
expander.logExtraOutput = logExtraOutput;
|
|
1798
|
+
return await expander.expand(valueSet, filter);
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
/**
|
|
1802
|
+
* Generate a UUID
|
|
1803
|
+
* @returns {string} UUID
|
|
1804
|
+
*/
|
|
1805
|
+
generateUuid() {
|
|
1806
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
1807
|
+
const r = Math.random() * 16 | 0;
|
|
1808
|
+
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
1809
|
+
return v.toString(16);
|
|
1810
|
+
});
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
/**
|
|
1814
|
+
* Build an OperationOutcome
|
|
1815
|
+
* @param {string} severity - error, warning, information
|
|
1816
|
+
* @param {string} code - Issue code
|
|
1817
|
+
* @param {string} message - Diagnostic message
|
|
1818
|
+
* @returns {Object} OperationOutcome resource
|
|
1819
|
+
*/
|
|
1820
|
+
operationOutcome(severity, code, message) {
|
|
1821
|
+
return {
|
|
1822
|
+
resourceType: 'OperationOutcome',
|
|
1823
|
+
issue: [{
|
|
1824
|
+
severity,
|
|
1825
|
+
code,
|
|
1826
|
+
diagnostics: message
|
|
1827
|
+
}]
|
|
1828
|
+
};
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
module.exports = {
|
|
1834
|
+
ExpandWorker,
|
|
1835
|
+
ValueSetExpander,
|
|
1836
|
+
ValueSetCounter,
|
|
1837
|
+
ImportedValueSet,
|
|
1838
|
+
ValueSetFilterContext,
|
|
1839
|
+
EmptyFilterContext,
|
|
1840
|
+
TotalStatus,
|
|
1841
|
+
UPPER_LIMIT_NO_TEXT,
|
|
1842
|
+
UPPER_LIMIT_TEXT,
|
|
1843
|
+
INTERNAL_LIMIT,
|
|
1844
|
+
EXPANSION_DEAD_TIME_SECS
|
|
1845
|
+
};
|