fhirsmith 0.10.1 → 0.11.1
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 +70 -0
- package/changelog-template.md +22 -0
- package/library/logger.js +8 -1
- package/library/package-manager.js +40 -2
- package/npmprojector/watcher.js +1 -1
- package/package.json +3 -2
- package/packages/packages.js +28 -15
- package/publisher/publisher.js +8 -8
- package/registry/api.js +92 -31
- package/registry/crawler.js +15 -1
- package/registry/model.js +158 -8
- package/registry/readme.md +5 -13
- package/registry/registry.js +87 -46
- package/registry/test-runner.js +1 -1
- package/server.js +8 -1
- package/stats.js +5 -1
- package/token/token.js +1 -1
- package/translations/Messages.properties +3 -1
- package/translations/rendering-phrases.properties +1227 -1218
- package/translations/rendering-phrases_ar.properties +1097 -1091
- package/translations/rendering-phrases_fr.properties +626 -624
- package/translations/rendering-phrases_nl.properties +976 -970
- package/translations/rendering-phrases_pt.properties +1026 -1020
- package/translations/rendering-phrases_ru.properties +1100 -1094
- package/tsconfig.eslint.json +9 -0
- package/tx/cs/cs-api.js +57 -4
- package/tx/cs/cs-cs.js +98 -25
- package/tx/cs/cs-hgvs.js +4 -0
- package/tx/cs/cs-lang.js +8 -0
- package/tx/cs/cs-mimetypes.js +4 -0
- package/tx/cs/cs-snomed.js +530 -75
- package/tx/cs/cs-uri.js +4 -0
- package/tx/data/snomed-subset.txt +40 -3
- package/tx/html/home-metrics.liquid +16 -1
- package/tx/importers/import-sct.module.js +18 -0
- package/tx/library/designations.js +3 -5
- package/tx/library/renderer.js +227 -30
- package/tx/library.js +5 -1
- package/tx/operation-context.js +131 -5
- package/tx/provider.js +38 -9
- package/tx/sct/ecl.js +30 -11
- package/tx/sct/expressions.js +28 -1
- package/tx/sct/structures.js +5 -5
- package/tx/tx-html.js +24 -15
- package/tx/tx.fhir.org.yml +1 -4
- package/tx/tx.js +18 -2
- package/tx/vs/vs-package.js +52 -0
- package/tx/workers/batch-validate.js +125 -18
- package/tx/workers/cache-control.js +31 -2
- package/tx/workers/expand.js +150 -76
- package/tx/workers/metadata.js +27 -6
- package/tx/workers/search.js +7 -1
- package/tx/workers/validate.js +30 -18
- package/tx/workers/worker.js +39 -7
- package/tx/xversion/xv-valueset.js +5 -2
- package/xig/xig.js +3 -3
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
//
|
|
2
|
-
// Validate Worker - Handles $validate-code operations
|
|
2
|
+
// Batch Validate Worker - Handles $batch-validate-code operations
|
|
3
3
|
//
|
|
4
|
-
// GET /
|
|
5
|
-
// POST /CodeSystem/$validate-code
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
4
|
+
// GET/POST /ValueSet/$batch-validate-code - batch of ValueSet $validate-code
|
|
5
|
+
// GET/POST /CodeSystem/$batch-validate-code - batch of CodeSystem $validate-code
|
|
6
|
+
//
|
|
7
|
+
// A batch is a Parameters whose `validation` entries each carry a single
|
|
8
|
+
// $validate-code request (as a nested Parameters). Shared inputs may be supplied
|
|
9
|
+
// once at the top level ("globals") and are applied to every entry. Entries are
|
|
10
|
+
// dispatched per-shape (a url/valueSet entry validates against a value set; a
|
|
11
|
+
// system/codeSystem entry validates against a code system), so both batch routes
|
|
12
|
+
// share one implementation and even a mixed batch is handled correctly.
|
|
12
13
|
//
|
|
13
14
|
|
|
14
15
|
const { TerminologyWorker } = require('./worker');
|
|
@@ -30,12 +31,20 @@ class BatchValidateWorker extends TerminologyWorker {
|
|
|
30
31
|
*/
|
|
31
32
|
constructor(opContext, log, provider, languages, i18n) {
|
|
32
33
|
super(opContext, log, provider, languages, i18n);
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
this.
|
|
38
|
-
|
|
34
|
+
// "Global" parameters may be supplied once at the top level and are applied to
|
|
35
|
+
// every entry that doesn't override them. tx-resource is always shared. The
|
|
36
|
+
// primary differs by operation: a ValueSet batch shares url/valueSet, a
|
|
37
|
+
// CodeSystem batch shares system/codeSystem.
|
|
38
|
+
this.valueSetGlobalNames = new Set([
|
|
39
|
+
"tx-resource", "url", "valueSet", "lenient-display-validation",
|
|
40
|
+
"__Accept-Language", "__Content-Language"
|
|
41
|
+
]);
|
|
42
|
+
this.codeSystemGlobalNames = new Set([
|
|
43
|
+
"tx-resource", "system", "codeSystem", "lenient-display-validation",
|
|
44
|
+
"__Accept-Language", "__Content-Language"
|
|
45
|
+
]);
|
|
46
|
+
// Back-compat: `globalNames` was the (ValueSet) set before CodeSystem batches.
|
|
47
|
+
this.globalNames = this.valueSetGlobalNames;
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
/**
|
|
@@ -46,18 +55,43 @@ class BatchValidateWorker extends TerminologyWorker {
|
|
|
46
55
|
return 'batch-validate-code';
|
|
47
56
|
}
|
|
48
57
|
|
|
58
|
+
/** ValueSet/$batch-validate-code: shared globals are url/valueSet. */
|
|
49
59
|
async handleValueSet(req, res) {
|
|
60
|
+
return this.processBatch(req, res, this.valueSetGlobalNames);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** CodeSystem/$batch-validate-code: shared globals are system/codeSystem. */
|
|
64
|
+
async handleCodeSystem(req, res) {
|
|
65
|
+
return this.processBatch(req, res, this.codeSystemGlobalNames);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Run a batch of $validate-code requests. Two passes: frontLoadBatch() pools all
|
|
70
|
+
* supplied resources into an unsealed cache first (see that method); then each
|
|
71
|
+
* `validation` entry is evaluated, inheriting the top-level globals it does not
|
|
72
|
+
* override and dispatched by shape to the ValueSet or CodeSystem validator.
|
|
73
|
+
*
|
|
74
|
+
* @param {express.Request} req
|
|
75
|
+
* @param {express.Response} res
|
|
76
|
+
* @param {Set<string>} globalNames - which top-level params are shared globals
|
|
77
|
+
*/
|
|
78
|
+
async processBatch(req, res, globalNames) {
|
|
50
79
|
try {
|
|
51
80
|
let params = req.body;
|
|
52
81
|
this.addHttpParams(req, params);
|
|
53
82
|
|
|
54
83
|
let globalParams = [];
|
|
55
84
|
for (const p of params.parameter) {
|
|
56
|
-
if (
|
|
85
|
+
if (globalNames.has(p.name)) {
|
|
57
86
|
globalParams.push(p);
|
|
58
87
|
}
|
|
59
88
|
}
|
|
60
89
|
|
|
90
|
+
// Pass 1: front-load every resource the batch supplies into the (unsealed)
|
|
91
|
+
// session cache before any entry is evaluated. When this happens, pass 2
|
|
92
|
+
// below drops per-entry tx-resource processing (they're already cached).
|
|
93
|
+
const frontLoaded = this.frontLoadBatch(params);
|
|
94
|
+
|
|
61
95
|
let output = [];
|
|
62
96
|
|
|
63
97
|
for (const p of params.parameter) {
|
|
@@ -65,12 +99,26 @@ class BatchValidateWorker extends TerminologyWorker {
|
|
|
65
99
|
let op = new Parameters();
|
|
66
100
|
op.jsonObj.parameter = [];
|
|
67
101
|
for (const gp of globalParams) {
|
|
102
|
+
if (gp.name == 'tx-resource') {
|
|
103
|
+
// Pass 2: when the batch was front-loaded into an unsealed cache, the
|
|
104
|
+
// tx-resources are already in the cache and every entry resolves them
|
|
105
|
+
// by reference - don't re-inject or re-process them per entry. Without
|
|
106
|
+
// front-loading (no cache, or a sealed cache) keep the original
|
|
107
|
+
// behaviour: the global tx-resources apply to every entry.
|
|
108
|
+
if (!frontLoaded) {
|
|
109
|
+
op.jsonObj.parameter.push(gp);
|
|
110
|
+
}
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
68
113
|
let exists = p.resource.parameter.find(pp => gp.name == pp.name);
|
|
69
|
-
if (
|
|
114
|
+
if (!exists) {
|
|
70
115
|
op.jsonObj.parameter.push(gp);
|
|
71
116
|
}
|
|
72
117
|
}
|
|
73
|
-
|
|
118
|
+
const entryParams = frontLoaded
|
|
119
|
+
? p.resource.parameter.filter(pp => pp.name !== 'tx-resource')
|
|
120
|
+
: p.resource.parameter;
|
|
121
|
+
op.jsonObj.parameter.push(...entryParams);
|
|
74
122
|
|
|
75
123
|
let worker = new ValidateWorker(this.opContext.copy(), this.log, this.provider, this.languages, this.i18n);
|
|
76
124
|
try {
|
|
@@ -100,11 +148,70 @@ class BatchValidateWorker extends TerminologyWorker {
|
|
|
100
148
|
} catch (error) {
|
|
101
149
|
this.log.error(error);
|
|
102
150
|
debugLog(error);
|
|
151
|
+
// A batch-level failure (e.g. an unknown cache-id from pass 1) applies to the
|
|
152
|
+
// whole batch. Preserve a coded Issue (like cache-id-unknown) so the client
|
|
153
|
+
// gets the same coded OperationOutcome + status it would on a single op.
|
|
154
|
+
if (error instanceof Issue) {
|
|
155
|
+
const oo = new OperationOutcome();
|
|
156
|
+
oo.addIssue(error);
|
|
157
|
+
return res.status(error.statusCode || 500).json(oo.jsonObj);
|
|
158
|
+
}
|
|
103
159
|
return res.status(error.statusCode || 500).json(this.operationOutcome(
|
|
104
160
|
'error', error.issueCode || 'exception', error.message));
|
|
105
161
|
}
|
|
106
162
|
}
|
|
107
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Pass 1 of batch processing: front-load every resource the batch supplies
|
|
166
|
+
* (each `tx-resource`, plus each entry's primary `valueSet`/`codeSystem`) into
|
|
167
|
+
* the session cache before any entry is evaluated. This makes the batch
|
|
168
|
+
* order-independent - an entry may refer by url to a resource another entry
|
|
169
|
+
* supplied - and means a failing entry does not withhold the resources it
|
|
170
|
+
* carried, because population happens up front and as one step.
|
|
171
|
+
*
|
|
172
|
+
* Front-loading is only relevant for an *unsealed* cache: that is what grows, so
|
|
173
|
+
* that is how one entry's resources become visible to the others. A sealed cache
|
|
174
|
+
* does not grow (each entry stays self-contained), and with no cache there is
|
|
175
|
+
* nowhere to pool; in both cases this returns false and pass 2 keeps the original
|
|
176
|
+
* per-entry tx-resource handling.
|
|
177
|
+
*
|
|
178
|
+
* The unknown-cache-id check is done here, once, for the whole batch.
|
|
179
|
+
*
|
|
180
|
+
* @param {Object} params - the batch Parameters (req.body)
|
|
181
|
+
* @returns {boolean} true if resources were front-loaded into an unsealed cache
|
|
182
|
+
*/
|
|
183
|
+
frontLoadBatch(params) {
|
|
184
|
+
const cacheId = this.opContext ? this.opContext.cacheId : null;
|
|
185
|
+
const cache = this.opContext ? this.opContext.resourceCache : null;
|
|
186
|
+
if (!cacheId || !cache) {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
if (!cache.has(cacheId)) {
|
|
190
|
+
throw new Issue('error', 'not-found', null, 'CACHE_ID_UNKNOWN',
|
|
191
|
+
this.i18n.translate('CACHE_ID_UNKNOWN', this.opContext.langs, [cacheId]),
|
|
192
|
+
'cache-id-unknown', 404);
|
|
193
|
+
}
|
|
194
|
+
if (cache.isSealed(cacheId)) {
|
|
195
|
+
return false;
|
|
196
|
+
}
|
|
197
|
+
// Flatten the top-level params and every entry's params into one list, then let
|
|
198
|
+
// collectSuppliedResources pick out the tx-resource + primary valueSet/codeSystem.
|
|
199
|
+
const allParams = [];
|
|
200
|
+
for (const p of params.parameter) {
|
|
201
|
+
if (p.name === 'validation' && p.resource && Array.isArray(p.resource.parameter)) {
|
|
202
|
+
allParams.push(...p.resource.parameter);
|
|
203
|
+
} else {
|
|
204
|
+
allParams.push(p);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
const { txResources, primaryResources } = this.collectSuppliedResources({ parameter: allParams });
|
|
208
|
+
const pool = txResources.concat(primaryResources);
|
|
209
|
+
if (pool.length > 0) {
|
|
210
|
+
cache.add(cacheId, pool);
|
|
211
|
+
}
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
|
|
108
215
|
/**
|
|
109
216
|
* Build an OperationOutcome
|
|
110
217
|
*/
|
|
@@ -124,17 +124,46 @@ class CacheControlWorker extends TerminologyWorker {
|
|
|
124
124
|
const { txResources, primaryResources } = this.collectSuppliedResources(params.jsonObj);
|
|
125
125
|
const resources = txResources.concat(primaryResources);
|
|
126
126
|
|
|
127
|
+
// `sealed` controls whether the cache may grow after creation. When sealed,
|
|
128
|
+
// the cache holds only the resources front-loaded here; when unsealed, later
|
|
129
|
+
// operations accumulate every resource they see into it (see
|
|
130
|
+
// setupAdditionalResources).
|
|
131
|
+
//
|
|
132
|
+
// NOTE: the protocol default is `true`, but the server default here is
|
|
133
|
+
// deliberately `false` during the transition: existing clients that rely on
|
|
134
|
+
// incremental population and do not yet send `sealed` must keep working.
|
|
135
|
+
// Flip this to default-true once all clients send an explicit value.
|
|
136
|
+
const sealed = this.readSealed(params.jsonObj);
|
|
137
|
+
|
|
127
138
|
const cacheId = crypto.randomUUID();
|
|
128
|
-
cache.set(cacheId, resources);
|
|
139
|
+
cache.set(cacheId, resources, sealed);
|
|
129
140
|
|
|
130
141
|
return res.status(200).json({
|
|
131
142
|
resourceType: 'Parameters',
|
|
132
143
|
parameter: [
|
|
133
|
-
{ name: 'cache-id', valueId: cacheId }
|
|
144
|
+
{ name: 'cache-id', valueId: cacheId },
|
|
145
|
+
{ name: 'sealed', valueBoolean: sealed }
|
|
134
146
|
]
|
|
135
147
|
});
|
|
136
148
|
}
|
|
137
149
|
|
|
150
|
+
/**
|
|
151
|
+
* Read the `sealed` boolean from the start request's Parameters.
|
|
152
|
+
*
|
|
153
|
+
* Server-side default is FALSE (transitional — see start()): a cache is only
|
|
154
|
+
* sealed if the client explicitly asks for it. Accepts a real JSON boolean or
|
|
155
|
+
* the string "true"/"false" for robustness across clients.
|
|
156
|
+
*
|
|
157
|
+
* @param {Object} params - Parameters resource (jsonObj)
|
|
158
|
+
* @returns {boolean}
|
|
159
|
+
*/
|
|
160
|
+
readSealed(params) {
|
|
161
|
+
const p = this.findParameter(params, 'sealed');
|
|
162
|
+
if (!p) return false;
|
|
163
|
+
const v = this.getParameterValue(p);
|
|
164
|
+
return v === true || v === 'true';
|
|
165
|
+
}
|
|
166
|
+
|
|
138
167
|
/**
|
|
139
168
|
* mode=end: release the cache named by the `${CACHE_ID_HEADER}` header so the
|
|
140
169
|
* server can reclaim it now rather than waiting for the idle timeout.
|