fhirsmith 0.11.2 → 0.12.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/tx/params.js CHANGED
@@ -61,6 +61,11 @@ class TxParameters {
61
61
  this.FValueSetVersionRules = null;
62
62
  this.FUid = '';
63
63
 
64
+ // names of single-valued parameters that have been set explicitly, so that
65
+ // parameters seen later with overwrite=false (i.e. from a ValueSet's
66
+ // embedded expansion parameters) don't clobber them. See seeParameter.
67
+ this.FSeen = new Set;
68
+
64
69
  this.FActiveOnly = false;
65
70
  this.FExcludeNested = false;
66
71
  this.FExcludeNotForUI = false;
@@ -104,173 +109,9 @@ class TxParameters {
104
109
  }
105
110
 
106
111
  for (let p of params.parameter) {
107
- switch (p.name) {
108
- // Version rules
109
- case 'system-version': {
110
- this.seeVersionRule(getValuePrimitive(p), false,'default');
111
- break;
112
- }
113
- case 'check-system-version': {
114
- this.seeVersionRule(getValuePrimitive(p), false, 'check');
115
- break;
116
- }
117
- case 'force-system-version': {
118
- this.seeVersionRule(getValuePrimitive(p), false, 'override');
119
- break;
120
- }
121
- case 'default-valueset-version': {
122
- this.seeVersionRule(getValuePrimitive(p), true, 'default');
123
- break;
124
- }
125
- case 'force-valueset-version': {
126
- this.seeVersionRule(getValuePrimitive(p), true, 'override');
127
- break;
128
- }
129
- case 'check-valueset-version': {
130
- this.seeVersionRule(getValuePrimitive(p), true, 'check');
131
- break;
132
- }
133
-
134
- case 'displayLanguage': {
135
- try {
136
- const lang = getValuePrimitive(p);
137
- this.DisplayLanguages = Languages.fromAcceptLanguage(lang, this.languageDefinitions, !this.validating);
138
- if (lang) this.FHasDisplayLanguages = true;
139
- } catch (error) {
140
- throw new Issue("error", "processing", null, 'INVALID_DISPLAY_NAME', this.i18n.translate('INVALID_DISPLAY_NAME', this.HTTPLanguages, [getValuePrimitive(p)]), "invalid-display").handleAsOO(400);
141
- }
142
- break;
143
- }
144
- case 'designation': {
145
- this.designations.push(getValuePrimitive(p));
146
- break;
147
- }
148
- case 'property': {
149
- this.properties.push(getValuePrimitive(p));
150
- break;
151
- }
152
- case 'no-cache': {
153
- // Write FUid (the field the cache key reads via hashSource); writing
154
- // `this.uid` was a no-op so no-cache=true never busted the cache.
155
- // Accept both the string ('true') and boolean (valueBoolean) forms.
156
- if (strToBool(getValuePrimitive(p), false)) this.FUid = crypto.randomUUID();
157
- break;
158
- }
159
- case '_incomplete':
160
- case 'limitedExpansion': {
161
- let value = getValuePrimitive(p);
162
- this.limitedExpansion = strToBool(value, false);
163
- break;
164
- }
165
- case 'includeDesignations': {
166
- let value = getValuePrimitive(p);
167
- this.includeDesignations = strToBool(value, false);
168
- break;
169
- }
170
- case 'includeDefinition': {
171
- let value = getValuePrimitive(p);
172
- this.includeDefinition = strToBool(value, false);
173
- break;
174
- }
175
- case 'activeOnly': {
176
- let value = getValuePrimitive(p);
177
- this.activeOnly = strToBool(value, false);
178
- break;
179
- }
180
- case 'excludeNested': {
181
- let value = getValuePrimitive(p);
182
- this.excludeNested = strToBool(value, false);
183
- break;
184
- }
185
- case 'excludeNotForUI': {
186
- let value = getValuePrimitive(p);
187
- this.excludeNotForUI = strToBool(value, false);
188
- break;
189
- }
190
- case 'excludePostCoordinated': {
191
- let value = getValuePrimitive(p);
192
- this.excludePostCoordinated = strToBool(value, false);
193
- break;
194
- }
195
- case 'default-to-latest-version': {
196
- let value = getValuePrimitive(p);
197
- this.defaultToLatestVersion = strToBool(value, false);
198
- break;
199
- }
200
- case 'incomplete-ok': {
201
- let value = getValuePrimitive(p);
202
- this.incompleteOK = strToBool(value, false);
203
- break;
204
- }
205
- case 'diagnostics': {
206
- let value = getValuePrimitive(p);
207
- this.diagnostics = strToBool(value, false);
208
- break;
209
- }
210
- case 'lenient-display-validation': {
211
- if (getValuePrimitive(p) == true) this.displayWarning = true;
212
- break;
213
- }
214
- case 'valueset-membership-only': {
215
- if (getValuePrimitive(p) == true) this.membershipOnly = true;
216
- break;
217
- }
218
- case 'versionsMatch' : {
219
- if (getValuePrimitive(p) == true) this.FVersionsMatch = true;
220
- break;
221
- }
222
- case 'profile' : {
223
- let value = p.resource;
224
- if (value && (value.resourceType === 'Parameters' || value.resourceType === 'ExpansionProfile')) {
225
- this.readParams(value);
226
- }
227
- break;
228
- }
229
- // eslint-disable-next-line no-fallthrough
230
- case 'term': // jQuery support
231
- case 'filter' : {
232
- this.filter = getValuePrimitive(p);
233
- break;
234
- }
235
- case 'count' : {
236
- this.count = Utilities.parseIntOrDefault(getValuePrimitive(p), -1);
237
- break;
238
- }
239
- case 'offset' : {
240
- this.offset = Utilities.parseIntOrDefault(getValuePrimitive(p), -1);
241
- break;
242
- }
243
-
244
- case 'limit' : {
245
- this.limit = Utilities.parseIntOrDefault(getValuePrimitive(p), -1);
246
- break;
247
- }
248
-
249
- case 'useSupplement' : {
250
- this.supplements.add(getValuePrimitive(p));
251
- break;
252
- }
253
-
254
- case 'abstract': {
255
- if (getValuePrimitive(p) == true) {
256
- this.abstractOk = true;
257
- } else if (getValuePrimitive(p) == false) {
258
- this.abstractOk = false;
259
- }
260
- break;
261
- }
262
- case 'inferSystem': {
263
- if (getValuePrimitive(p) == true) this.inferSystem = true;
264
- break;
265
- }
266
- case 'sort': {
267
- this.sort = getValuePrimitive(p);
268
- break;
269
- }
270
- case "exclude-system": {
271
- throw new Issue('error', 'not-supported', null, null, "The parameter 'exclude-system' is not supported by this system", null, 400);
272
- }
273
- }
112
+ // parameters that come from the request itself always win over anything
113
+ // seen earlier (e.g. from a ValueSet's embedded expansion parameters)
114
+ this.seeParameter(p.name, p, true);
274
115
  }
275
116
 
276
117
  }
@@ -415,7 +256,7 @@ class TxParameters {
415
256
  this.FVersionsMatch = value;
416
257
  this.hasVersionsMatch = true;
417
258
  }
418
- e
259
+
419
260
  get versionRules() {
420
261
  return this.FVersionRules;
421
262
  }
@@ -432,19 +273,225 @@ e
432
273
  return new TxParameters(langDefs);
433
274
  }
434
275
 
276
+ /**
277
+ * Process a single parameter. This is the single place where parameter names
278
+ * are interpreted; both the request Parameters resource (see readParams) and
279
+ * the valueset-expansion-parameter extension route through here.
280
+ *
281
+ * @param {string} name - the parameter name
282
+ * @param {Object} value - any element getValuePrimitive() understands: a
283
+ * Parameters.parameter entry, or the 'value' sub-extension of a
284
+ * valueset-expansion-parameter extension
285
+ * @param {boolean} overwrite - true when the parameter comes from the request
286
+ * (later values win); false when it comes from a ValueSet's embedded
287
+ * expansion parameters, which must not clobber what the request set
288
+ * explicitly. Parameters that accumulate (version rules, designations,
289
+ * properties, supplements) are unaffected - they always add.
290
+ */
435
291
  seeParameter(name, value, overwrite) {
436
- if (value) {
437
- if (name === 'displayLanguage' && (!this.FDisplayLanguages || overwrite)) {
438
- this.DisplayLanguages = Languages.fromAcceptLanguage(getValuePrimitive(value), this.languageDefinitions, !this.validating)
439
- if (getValuePrimitive(value)) this.FHasDisplayLanguages = true;
292
+ if (!name || !value) {
293
+ return;
294
+ }
295
+
296
+ // returns false if this single-valued parameter has already been set
297
+ // explicitly and we're not allowed to overwrite it
298
+ const claim = (key) => {
299
+ if (!overwrite && this.FSeen.has(key)) {
300
+ return false;
301
+ }
302
+ this.FSeen.add(key);
303
+ return true;
304
+ };
305
+ // string ('true') and boolean (valueBoolean) forms are both accepted, since
306
+ // GET parameters always arrive as valueString
307
+ const asBool = () => strToBool(getValuePrimitive(value), false);
308
+
309
+ switch (name) {
310
+ // -- accumulating parameters: version rules --------------------------
311
+ case 'system-version': {
312
+ this.seeVersionRule(getValuePrimitive(value), false, 'default');
313
+ break;
314
+ }
315
+ case 'check-system-version': {
316
+ this.seeVersionRule(getValuePrimitive(value), false, 'check');
317
+ break;
318
+ }
319
+ case 'force-system-version': {
320
+ this.seeVersionRule(getValuePrimitive(value), false, 'override');
321
+ break;
322
+ }
323
+ case 'default-valueset-version': {
324
+ this.seeVersionRule(getValuePrimitive(value), true, 'default');
325
+ break;
326
+ }
327
+ case 'force-valueset-version': {
328
+ this.seeVersionRule(getValuePrimitive(value), true, 'override');
329
+ break;
330
+ }
331
+ case 'check-valueset-version': {
332
+ this.seeVersionRule(getValuePrimitive(value), true, 'check');
333
+ break;
440
334
  }
441
335
 
442
- if (name === 'designation') {
336
+ // -- other accumulating parameters -----------------------------------
337
+ case 'designation': {
443
338
  this.designations.push(getValuePrimitive(value));
339
+ break;
340
+ }
341
+ case 'property': {
342
+ // properties can arrive from the request, from an expansion parameter
343
+ // extension, and (R6) from ValueSet.compose.property, so de-duplicate:
344
+ // a repeat would emit the property twice and change the cache key
345
+ const prop = getValuePrimitive(value);
346
+ if (prop && !this.properties.includes(prop)) {
347
+ this.properties.push(prop);
348
+ }
349
+ break;
350
+ }
351
+ case 'useSupplement': {
352
+ this.supplements.add(getValuePrimitive(value));
353
+ break;
444
354
  }
445
355
 
446
- if (name === 'versionsMatch') {
447
- this.versionsMatch = getValuePrimitive(value) === 'true';
356
+ // -- single valued parameters ----------------------------------------
357
+ case 'displayLanguage': {
358
+ if (claim('displayLanguage')) {
359
+ const lang = getValuePrimitive(value);
360
+ try {
361
+ this.DisplayLanguages = Languages.fromAcceptLanguage(lang, this.languageDefinitions, !this.validating);
362
+ if (lang) this.FHasDisplayLanguages = true;
363
+ } catch (error) {
364
+ throw new Issue("error", "processing", null, 'INVALID_DISPLAY_NAME', this.i18n.translate('INVALID_DISPLAY_NAME', this.HTTPLanguages, [lang]), "invalid-display").handleAsOO(400);
365
+ }
366
+ }
367
+ break;
368
+ }
369
+ case 'no-cache': {
370
+ // Write FUid (the field the cache key reads via hashSource); writing
371
+ // `this.uid` was a no-op so no-cache=true never busted the cache.
372
+ if (claim('no-cache') && asBool()) this.FUid = crypto.randomUUID();
373
+ break;
374
+ }
375
+ case '_incomplete':
376
+ case 'limitedExpansion': {
377
+ if (claim('limitedExpansion')) this.limitedExpansion = asBool();
378
+ break;
379
+ }
380
+ case 'includeDesignations': {
381
+ if (claim('includeDesignations')) this.includeDesignations = asBool();
382
+ break;
383
+ }
384
+ case 'includeDefinition': {
385
+ if (claim('includeDefinition')) this.includeDefinition = asBool();
386
+ break;
387
+ }
388
+ case 'activeOnly': {
389
+ if (claim('activeOnly')) this.activeOnly = asBool();
390
+ break;
391
+ }
392
+ case 'excludeNested': {
393
+ if (claim('excludeNested')) this.excludeNested = asBool();
394
+ break;
395
+ }
396
+ case 'excludeNotForUI': {
397
+ if (claim('excludeNotForUI')) this.excludeNotForUI = asBool();
398
+ break;
399
+ }
400
+ case 'excludePostCoordinated': {
401
+ if (claim('excludePostCoordinated')) this.excludePostCoordinated = asBool();
402
+ break;
403
+ }
404
+ case 'default-to-latest-version': {
405
+ if (claim('default-to-latest-version')) this.defaultToLatestVersion = asBool();
406
+ break;
407
+ }
408
+ case 'incomplete-ok': {
409
+ if (claim('incomplete-ok')) this.incompleteOK = asBool();
410
+ break;
411
+ }
412
+ case 'diagnostics': {
413
+ if (claim('diagnostics')) this.diagnostics = asBool();
414
+ break;
415
+ }
416
+ case 'lenient-display-validation': {
417
+ if (claim('lenient-display-validation')) this.displayWarning = asBool();
418
+ break;
419
+ }
420
+ case 'valueset-membership-only': {
421
+ if (claim('valueset-membership-only')) this.membershipOnly = asBool();
422
+ break;
423
+ }
424
+ case 'versionsMatch': {
425
+ if (claim('versionsMatch')) this.versionsMatch = asBool();
426
+ break;
427
+ }
428
+ case 'inferSystem': {
429
+ if (claim('inferSystem')) this.inferSystem = asBool();
430
+ break;
431
+ }
432
+ case 'abstract': {
433
+ // tri-state: only an explicit true/false changes the default (true)
434
+ const v = getValuePrimitive(value);
435
+ if (v === true || v === 'true') {
436
+ if (claim('abstract')) this.abstractOk = true;
437
+ } else if (v === false || v === 'false') {
438
+ if (claim('abstract')) this.abstractOk = false;
439
+ }
440
+ break;
441
+ }
442
+ case 'term': // jQuery support
443
+ case 'filter': {
444
+ if (claim('filter')) this.filter = getValuePrimitive(value);
445
+ break;
446
+ }
447
+ case 'count': {
448
+ if (claim('count')) this.count = Utilities.parseIntOrDefault(getValuePrimitive(value), -1);
449
+ break;
450
+ }
451
+ case 'offset': {
452
+ if (claim('offset')) this.offset = Utilities.parseIntOrDefault(getValuePrimitive(value), -1);
453
+ break;
454
+ }
455
+ case 'limit': {
456
+ if (claim('limit')) this.limit = Utilities.parseIntOrDefault(getValuePrimitive(value), -1);
457
+ break;
458
+ }
459
+ case 'sort': {
460
+ if (claim('sort')) this.sort = getValuePrimitive(value);
461
+ break;
462
+ }
463
+
464
+ // -- nested parameters -------------------------------------------------
465
+ case 'profile': {
466
+ // only meaningful from a Parameters.parameter, which can carry a resource
467
+ const res = value.resource;
468
+ if (res && (res.resourceType === 'Parameters' || res.resourceType === 'ExpansionProfile')) {
469
+ this.readParams(res);
470
+ }
471
+ break;
472
+ }
473
+
474
+ case 'exclude-system': {
475
+ throw new Issue('error', 'not-supported', null, null, "The parameter 'exclude-system' is not supported by this system", null, 400);
476
+ }
477
+ }
478
+ }
479
+
480
+ /**
481
+ * Read parameters a ValueSet declares on its own compose. Presently that's
482
+ * R6's ValueSet.compose.property (0..* code) - the properties to return in
483
+ * the expansion, named by the value set rather than by the request. We don't
484
+ * formally support R6, but the element is harmless in earlier versions and
485
+ * honouring it costs nothing.
486
+ *
487
+ * @param {Object} compose - ValueSet.compose (raw JSON)
488
+ */
489
+ seeCompose(compose) {
490
+ if (compose && Array.isArray(compose.property)) {
491
+ for (const prop of compose.property) {
492
+ if (typeof prop === 'string') {
493
+ this.seeParameter('property', {valueCode: prop}, false);
494
+ }
448
495
  }
449
496
  }
450
497
  }
@@ -639,6 +686,10 @@ e
639
686
  this.hasDisplayWarning = other.hasDisplayWarning;
640
687
  this.sort = other.sort;
641
688
 
689
+ if (other.FSeen) {
690
+ this.FSeen = new Set(other.FSeen);
691
+ }
692
+
642
693
  if (other.FProperties) {
643
694
  this.FProperties = [...other.FProperties];
644
695
  }