fhirsmith 0.5.5 → 0.6.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/tx-html.js CHANGED
@@ -9,6 +9,15 @@ const htmlServer = require('../library/html-server');
9
9
  const Logger = require('../library/logger');
10
10
  const packageJson = require("../package.json");
11
11
  const escape = require('escape-html');
12
+ const {ExpandWorker} = require("./workers/expand");
13
+ const ValueSet = require("./library/valueset");
14
+ const {CodeSystemXML} = require("./xml/codesystem-xml");
15
+ const {ValueSetXML} = require("./xml/valueset-xml");
16
+ const {BundleXML} = require("./xml/bundle-xml");
17
+ const {CapabilityStatementXML} = require("./xml/capabilitystatement-xml");
18
+ const {TerminologyCapabilitiesXML} = require("./xml/terminologycapabilities-xml");
19
+ const {ParametersXML} = require("./xml/parameters-xml");
20
+ const {OperationOutcomeXML} = require("./xml/operationoutcome-xml");
12
21
 
13
22
  const txHtmlLog = Logger.getInstance().child({ module: 'tx-html' });
14
23
 
@@ -57,10 +66,16 @@ function loadTemplate() {
57
66
  class TxHtmlRenderer {
58
67
  renderer;
59
68
  liquid;
69
+ languages;
70
+ i18n;
71
+ path;
60
72
 
61
- constructor(renderer, liquid) {
73
+ constructor(renderer, liquid, languages, i18n, path) {
62
74
  this.renderer = renderer;
63
75
  this.liquid = liquid;
76
+ this.languages = languages;
77
+ this.i18n = i18n;
78
+ this.path = path;
64
79
  }
65
80
 
66
81
  /**
@@ -85,7 +100,7 @@ class TxHtmlRenderer {
85
100
  if (_fmt && typeof _fmt !== 'string') {
86
101
  _fmt = null;
87
102
  }
88
- if (_fmt && _fmt == 'html') {
103
+ if (_fmt && (_fmt == 'html' || _fmt.startsWith('html/'))) {
89
104
  return true;
90
105
  }
91
106
  if (!_fmt) {
@@ -106,6 +121,14 @@ class TxHtmlRenderer {
106
121
  } else {
107
122
  const resourceType = json.resourceType || 'Response';
108
123
 
124
+ let pfx = resourceType;
125
+ if (req.path.includes('$')) {
126
+ let s = req.path.substring(req.path.indexOf('$') + 1).replace(/[^a-zA-Z].*$/, '');
127
+ switch (s) {
128
+ case 'expand': pfx = "Expansion for "+resourceType;
129
+ }
130
+ }
131
+
109
132
  if (resourceType === 'Bundle' && json.type === 'searchset') {
110
133
  // Extract the resource type being searched from self link or entries
111
134
  const selfLink = json.link?.find(l => l.relation === 'self')?.url || '';
@@ -124,11 +147,11 @@ class TxHtmlRenderer {
124
147
  }
125
148
 
126
149
  if (json.id) {
127
- return `${resourceType}/${json.id}`;
150
+ return `${pfx} ${json.id}`;
128
151
  }
129
152
 
130
153
  if (json.name) {
131
- return `${resourceType}: ${json.name}`;
154
+ return `${pfx} ${json.name}`;
132
155
  }
133
156
 
134
157
  return resourceType;
@@ -267,15 +290,27 @@ class TxHtmlRenderer {
267
290
  return await this.buildHomePage(req);
268
291
  } else {
269
292
  try {
293
+ const _fmt = req?.query?._format || req?.query?.format || req?.body?._format;
294
+ const op = req ? req.path.includes("$") : false;
270
295
  const resourceType = json.resourceType;
271
296
 
272
297
  switch (resourceType) {
273
298
  case 'Parameters':
274
299
  return await this.renderParameters(json);
275
300
  case 'CodeSystem':
276
- return await this.renderCodeSystem(json, inBundle);
277
- case 'ValueSet':
278
- return await this.renderValueSet(json, inBundle);
301
+ return await this.renderCodeSystem(json, inBundle, _fmt, op);
302
+ case 'ValueSet': {
303
+ let exp = undefined;
304
+ if (!inBundle && !op && (!_fmt || _fmt == 'html')) {
305
+ try {
306
+ let worker = new ExpandWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
307
+ exp = new ValueSet(await worker.handleInternalExpand(json, req));
308
+ } catch (error) {
309
+ exp = error;
310
+ }
311
+ }
312
+ return await this.renderValueSet(json, inBundle, _fmt, op, exp);
313
+ }
279
314
  case 'ConceptMap':
280
315
  return await this.renderConceptMap(json, inBundle);
281
316
  case 'CapabilityStatement':
@@ -575,35 +610,88 @@ class TxHtmlRenderer {
575
610
  /**
576
611
  * Render CodeSystem resource
577
612
  */
578
- async renderCodeSystem(json, inBundle) {
579
- let html = await this.renderResourceWithNarrative(json, await this.renderer.renderCodeSystem(json));
613
+ async renderCodeSystem(json, inBundle, _fmt) {
614
+ if (inBundle) {
615
+ return await this.renderResourceWithNarrative(json, await this.renderer.renderCodeSystem(json));
616
+ } else {
617
+ let html = `<ul class="nav nav-tabs">`;
618
+ html += this.tab(!_fmt || _fmt == 'html', json.resourceType, json.resourceType, 'html', json.id);
619
+ html += this.tab(_fmt && _fmt == 'html/json', 'JSON', json.resourceType, 'html/json', json.id);
620
+ html += this.tab(_fmt && _fmt == 'html/xml', 'XML', json.resourceType, 'html/xml', json.id);
621
+ html += this.tab(_fmt && _fmt == 'html/narrative', 'Original Narrative', json.resourceType, 'html/narrative', json.id);
622
+ html += this.tab(_fmt && _fmt == 'html/ops', 'LookUp / Subsumes', json.resourceType, 'html/ops', json.id);
623
+ html += `</ul>`;
624
+
625
+ if (!_fmt || _fmt == 'html') {
626
+ html += await this.renderResourceWithNarrative(json, await this.renderer.renderCodeSystem(json));
627
+ } else if (_fmt == "html/json") {
628
+ html += await this.renderResourceJson(json);
629
+ } else if (_fmt == "html/xml") {
630
+ html += await this.renderResourceXml(json);
631
+ } else if (_fmt == "html/narrative") {
632
+ html += await this.renderResourceWithNarrative(json, json.text?.div);
633
+ } else if (_fmt == "html/ops") {
634
+ html += await this.liquid.renderFile('codesystem-operations', {
635
+ opsId: this.generateResourceId(),
636
+ vcSystemId: this.generateResourceId(),
637
+ inferSystemId: this.generateResourceId(),
638
+ url: escape(json.url || '')
639
+ });
640
+ }
580
641
 
581
- if (!inBundle) {
582
- html += await this.liquid.renderFile('codesystem-operations', {
583
- opsId: this.generateResourceId(),
584
- url: escape(json.url || '')
585
- });
586
- }
587
642
 
588
- return html;
643
+ return html;
644
+ }
589
645
  }
590
646
 
647
+ tab(b, name, rtype, type, id) {
648
+ if (b) {
649
+ return `<li class="active"><a href="#">${name}</a></li>`;
650
+ } else {
651
+ return `<li><a href="${this.path}/${rtype}/${id}?_format=${type}">${name}</a></li>`;
652
+ }
653
+ }
591
654
  /**
592
655
  * Render ValueSet resource
593
656
  */
594
- async renderValueSet(json, inBundle) {
595
- let html = await this.renderResourceWithNarrative(json, await this.renderer.renderValueSet(json));
596
-
597
- if (!inBundle) {
598
- html += await this.liquid.renderFile('valueset-operations', {
599
- opsId: this.generateResourceId(),
600
- vcSystemId: this.generateResourceId(),
601
- inferSystemId: this.generateResourceId(),
602
- url: escape(json.url || '')
603
- });
657
+ async renderValueSet(json, inBundle, _fmt, op, exp) {
658
+ if (inBundle || op) {
659
+ return await this.renderResourceWithNarrative(json, await this.renderer.renderValueSet(json));
660
+ } else {
661
+ let html = `<ul class="nav nav-tabs">`;
662
+ html += this.tab(!_fmt || _fmt == 'html', json.resourceType, json.resourceType, 'html', json.id);
663
+ html += this.tab(_fmt && _fmt == 'html/json', 'JSON', json.resourceType, 'html/json', json.id);
664
+ html += this.tab(_fmt && _fmt == 'html/xml', 'XML', json.resourceType, 'html/xml', json.id);
665
+ html += this.tab(_fmt && _fmt == 'html/narrative', 'Original Narrative', json.resourceType, 'html/narrative', json.id);
666
+ html += this.tab(_fmt && _fmt == 'html/ops', 'Expand / Validate', json.resourceType, 'html/ops', json.id);
667
+ html += `</ul>`;
668
+
669
+ if (!_fmt || _fmt == 'html') {
670
+ html += await this.renderResourceWithNarrative(json, await this.renderer.renderValueSet(json));
671
+ if (exp) {
672
+ html += "<h2>Expansion</h2>";
673
+ if (exp instanceof ValueSet) {
674
+ html += await this.renderer.renderVSExpansion(exp.jsonObj, false)
675
+ } else {
676
+ html += `<p>Error: `+exp.message+`</p>`;
677
+ }
678
+ }
679
+ } else if (_fmt == "html/json") {
680
+ html += await this.renderResourceJson(json);
681
+ } else if (_fmt == "html/xml") {
682
+ html += await this.renderResourceXml(json);
683
+ } else if (_fmt == "html/narrative") {
684
+ html += await this.renderResourceWithNarrative(json, json.text?.div);
685
+ } else if (_fmt == "html/ops") {
686
+ html += await this.liquid.renderFile('valueset-operations', {
687
+ opsId: this.generateResourceId(),
688
+ vcSystemId: this.generateResourceId(),
689
+ inferSystemId: this.generateResourceId(),
690
+ url: escape(json.url || '')
691
+ });
692
+ }
693
+ return html;
604
694
  }
605
-
606
- return html;
607
695
  }
608
696
 
609
697
  /**
@@ -1101,9 +1189,7 @@ class TxHtmlRenderer {
1101
1189
  * Render resource with text/div narrative and collapsible JSON source
1102
1190
  */
1103
1191
  async renderResourceWithNarrative(json, rendered) {
1104
- const resourceId = this.generateResourceId();
1105
-
1106
- let html = "";
1192
+ let html = '';
1107
1193
 
1108
1194
  // Show text/div narrative if present
1109
1195
  if (rendered) {
@@ -1113,30 +1199,37 @@ class TxHtmlRenderer {
1113
1199
  } else {
1114
1200
  html += '<div class="narrative">(No Narrative)</div>';
1115
1201
  }
1116
- if (json.text && json.text.div) {
1117
- // Collapsible JSON source
1118
- html += '<div class="xhtml">';
1119
- html += `<button type="button" class="btn btn-sm btn-outline-secondary" onclick="toggleOriginalNarrative('${resourceId}x')">`;
1120
- html += 'Show Original Narrative</button>';
1121
- html += `<div id="${resourceId}x" class="original-narrative" style="display: none; margin-top: 10px;">`;
1122
-
1123
- html += '<div class="narrative">';
1124
- html += json.text.div; // Already HTML, render as-is
1125
- html += '</div>';
1126
- }
1127
- html += '</div>';
1128
- html += '</div>';
1129
1202
 
1203
+ return html;
1204
+ }
1130
1205
 
1131
- // Collapsible JSON source
1132
- html += '<div class="json-source">';
1133
- html += `<button type="button" class="btn btn-sm btn-outline-secondary" onclick="toggleJsonSource('${resourceId}')">`;
1134
- html += 'Show JSON Source</button>';
1135
- html += `<div id="${resourceId}" class="json-content" style="display: none; margin-top: 10px;">`;
1206
+ async renderResourceJson(json) {
1207
+ let html = "";
1208
+ html += `<div class="json-content" style="margin-top: 10px;">`;
1136
1209
  html += `<pre>${escape(JSON.stringify(json, null, 2))}</pre>`;
1137
1210
  html += '</div>';
1138
- html += '</div>';
1211
+ return html;
1212
+ }
1213
+
1214
+ convertResourceToXml(res) {
1215
+ switch (res.resourceType) {
1216
+ case "CodeSystem" : return CodeSystemXML.toXml(res);
1217
+ case "ValueSet" : return ValueSetXML.toXml(res);
1218
+ case "Bundle" : return BundleXML.toXml(res, this.fhirVersion);
1219
+ case "CapabilityStatement" : return CapabilityStatementXML.toXml(res, "R5");
1220
+ case "TerminologyCapabilities" : return TerminologyCapabilitiesXML.toXml(res, "R5");
1221
+ case "Parameters": return ParametersXML.toXml(res, this.fhirVersion);
1222
+ case "OperationOutcome": return OperationOutcomeXML.toXml(res, this.fhirVersion);
1223
+ }
1224
+ throw new Error(`Resource type ${res.resourceType} not supported in XML`);
1225
+ }
1139
1226
 
1227
+ async renderResourceXml(json) {
1228
+ let xml = this.convertResourceToXml(json);
1229
+ let html = "";
1230
+ html += `<div class="xml-content" style="margin-top: 10px;">`;
1231
+ html += `<pre>${escape(xml)}</pre>`;
1232
+ html += '</div>';
1140
1233
  return html;
1141
1234
  }
1142
1235
 
package/tx/tx.js CHANGED
@@ -300,7 +300,7 @@ class TXModule {
300
300
  // Wrap res.json to intercept and convert to HTML if browser requests it, and log the request
301
301
  const originalJson = res.json.bind(res);
302
302
 
303
- let txhtml = new TxHtmlRenderer(new Renderer(opContext, endpointInfo.provider), this.liquid);
303
+ let txhtml = new TxHtmlRenderer(new Renderer(opContext, endpointInfo.provider), this.liquid, this.languages, this.i18n, endpointInfo.path);
304
304
  res.json = async (data) => {
305
305
  try {
306
306
  const duration = Date.now() - req.txStartTime;
@@ -897,7 +897,7 @@ class TXModule {
897
897
  router.get('/problems.html', async (req, res) => {
898
898
  const start = Date.now();
899
899
  try {
900
- let txhtml = new TxHtmlRenderer(new Renderer(req.txOpContext, req.txProvider), this.liquid);
900
+ let txhtml = new TxHtmlRenderer(new Renderer(req.txOpContext, req.txProvider), this.liquid, this.languages, this.i18n, req.txEndpoint.path);
901
901
  const problemFinder = new ProblemFinder();
902
902
  const content = await problemFinder.scanValueSets(req.txProvider);
903
903
  const html = await txhtml.renderPage('Problems', '<h3>ValueSet dependencies on unknown CodeSystem/Versions</h3>'+content, req.txEndpoint, req.txStartTime);
@@ -632,7 +632,7 @@ class ValueSetExpander {
632
632
  } else if (cs.contentMode() === 'supplement') {
633
633
  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');
634
634
  } else {
635
- this.addParamUri(exp, cs.contentMode(), cs.system + '|' + cs.version);
635
+ this.addParamUri(exp, cs.contentMode(), cs.system() + '|' + cs.version());
636
636
  Extensions.addString(exp, "http://hl7.org/fhir/StructureDefinition/valueset-unclosed",
637
637
  "This extension is based on a fragment of the code system " + cset.system);
638
638
  }
@@ -1388,17 +1388,28 @@ class ValueSetExpander {
1388
1388
  }
1389
1389
 
1390
1390
  checkCanonicalStatus(exp, vurl, status, standardsStatus, experimental, source) {
1391
+ let sourceStatus = source ? source.status : undefined;
1392
+ let sourceStandardsStatus= source ? Extensions.readString(source, 'http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status') : undefined;
1391
1393
  if (standardsStatus == 'deprecated') {
1392
- this.addParamUri(exp, 'warning-deprecated', vurl);
1394
+ if (sourceStandardsStatus != 'deprecated') {
1395
+ this.addParamUri(exp, 'warning-deprecated', vurl);
1396
+ }
1393
1397
  } else if (standardsStatus == 'withdrawn') {
1394
- this.addParamUri(exp, 'warning-withdrawn', vurl);
1398
+ if (sourceStandardsStatus != 'withdrawn') {
1399
+ this.addParamUri(exp, 'warning-withdrawn', vurl);
1400
+ }
1395
1401
  } else if (status == 'retired') {
1396
- this.addParamUri(exp, 'warning-retired', vurl);
1397
- } else if (experimental && !source.experimental) {
1398
- this.addParamUri(exp, 'warning-experimental', vurl)
1399
- } else if (((status == 'draft') || (standardsStatus == 'draft')) &&
1400
- !((source.status == 'draft') || (Extensions.readString(source, 'http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status') == 'draft'))) {
1401
- this.addParamUri(exp, 'warning-draft', vurl)
1402
+ if (sourceStatus != 'retired') {
1403
+ this.addParamUri(exp, 'warning-retired', vurl);
1404
+ }
1405
+ } else if (experimental) {
1406
+ if (!source.experimental) {
1407
+ this.addParamUri(exp, 'warning-experimental', vurl);
1408
+ }
1409
+ } else if (((status == 'draft') || (standardsStatus == 'draft'))) {
1410
+ if (!((source.status == 'draft') || (Extensions.readString(source, 'http://hl7.org/fhir/StructureDefinition/structuredefinition-standards-status') == 'draft'))) {
1411
+ this.addParamUri(exp, 'warning-draft', vurl)
1412
+ }
1402
1413
  }
1403
1414
  }
1404
1415
 
@@ -1797,7 +1808,48 @@ class ExpandWorker extends TerminologyWorker {
1797
1808
  req.logInfo = this.usedSources.join("|")+txp.logInfo();
1798
1809
  return res.json(result);
1799
1810
  }
1800
-
1811
+
1812
+ /**
1813
+ * Handle type-level expand: /ValueSet/$expand
1814
+ * ValueSet identified by url, or provided directly in body
1815
+ */
1816
+ async handleInternalExpand(valueSet, req) {
1817
+ this.deadCheck('expand-internal');
1818
+
1819
+ if (!valueSet.jsonObj) {
1820
+ valueSet = new ValueSet(valueSet);
1821
+ }
1822
+ // Determine how the request is structured
1823
+ let params = null;
1824
+ this.seeSourceVS(valueSet);
1825
+
1826
+ if (req.method === 'POST' && req.body) {
1827
+ if (req.body.resourceType === 'ValueSet') {
1828
+ params = this.queryToParameters(req.query);
1829
+ } else if (req.body.resourceType === 'Parameters') {
1830
+ // Body is a Parameters resource
1831
+ params = req.body;
1832
+ } else {
1833
+ // Assume form body - convert to Parameters
1834
+ params = this.formToParameters(req.body, req.query);
1835
+ }
1836
+ } else {
1837
+ // GET request - convert query to Parameters
1838
+ params = this.queryToParameters(req.query);
1839
+ }
1840
+ this.addHttpParams(req, params);
1841
+
1842
+ // Handle tx-resource and cache-id parameters
1843
+ this.setupAdditionalResources(params);
1844
+ const logExtraOutput = this.findParameter(params, 'logExtraOutput');
1845
+
1846
+ let txp = new TxParameters(this.opContext.i18n.languageDefinitions, this.opContext.i18n, false);
1847
+ txp.readParams(params);
1848
+
1849
+ // Perform the expansion
1850
+ return await this.doExpand(valueSet, txp, logExtraOutput);
1851
+ }
1852
+
1801
1853
  /**
1802
1854
  * Handle instance-level expand: /ValueSet/{id}/$expand
1803
1855
  * ValueSet identified by resource ID
@@ -325,9 +325,10 @@ class MetadataHandler {
325
325
  for (const cs of provider.codeSystems.values()) {
326
326
  const url = cs.url || (cs.jsonObj && cs.jsonObj.url);
327
327
  const version = cs.version || (cs.jsonObj && cs.jsonObj.version);
328
+ const content = cs.content || cs.jsonObj.content;
328
329
 
329
330
  if (url) {
330
- this.addCodeSystemEntry(seenSystems, url, version);
331
+ this.addCodeSystemEntry(seenSystems, url, version, content);
331
332
  }
332
333
  }
333
334
  }
@@ -339,7 +340,7 @@ class MetadataHandler {
339
340
  const version = factory.version();
340
341
 
341
342
  if (url) {
342
- this.addCodeSystemEntry(seenSystems, url, version);
343
+ this.addCodeSystemEntry(seenSystems, url, version, factory.content());
343
344
  }
344
345
  }
345
346
  }
@@ -357,13 +358,16 @@ class MetadataHandler {
357
358
  * @param {string} url - Code system URL
358
359
  * @param {string} version - Code system version (may be null)
359
360
  */
360
- addCodeSystemEntry(seenSystems, url, version) {
361
+ addCodeSystemEntry(seenSystems, url, version, content) {
361
362
  if (!seenSystems.has(url)) {
362
363
  // Create new entry
363
364
  const entry = { uri: url };
364
365
  if (version) {
365
366
  entry.version = [{ code: version }];
366
367
  }
368
+ if (content) {
369
+ entry.content = content;
370
+ }
367
371
  seenSystems.set(url, entry);
368
372
  } else if (version) {
369
373
  // Add version to existing entry
@@ -371,6 +375,9 @@ class MetadataHandler {
371
375
  if (!entry.version) {
372
376
  entry.version = [];
373
377
  }
378
+ if (content) {
379
+ entry.content = content;
380
+ }
374
381
  // Check if version already exists
375
382
  if (!entry.version.some(v => v.code === version)) {
376
383
  entry.version.push({ code: version });
@@ -137,7 +137,7 @@ class SearchWorker extends TerminologyWorker {
137
137
  const searchParams = {};
138
138
  for (const [key, value] of Object.entries(params)) {
139
139
  if (!key.startsWith('_') && value && SearchWorker.ALLOWED_PARAMS.includes(key)) {
140
- searchParams[key] = value.toLowerCase();
140
+ searchParams[key] = key == 'url' ? value : value.toLowerCase();
141
141
  }
142
142
  }
143
143
 
@@ -146,6 +146,9 @@ class SearchWorker extends TerminologyWorker {
146
146
 
147
147
  for (const [key, cs] of this.provider.codeSystems) {
148
148
  this.deadCheck('searchCodeSystems');
149
+ if (cs.url == 'http://www.cms.gov/Medicare/Coding/HCPCSReleaseCodeSets') {
150
+ console.log("debug");
151
+ }
149
152
  if (key == cs.vurl) {
150
153
  const json = cs.jsonObj;
151
154
 
@@ -179,7 +182,7 @@ class SearchWorker extends TerminologyWorker {
179
182
  }
180
183
  } else if (param === 'url') { // exact match
181
184
  const propValue = json.url;
182
- if (propValue != searchValue) {
185
+ if (propValue !== searchValue) {
183
186
  isMatch = false;
184
187
  break;
185
188
  }
@@ -44,6 +44,7 @@ class ValueSetChecker {
44
44
  valueSet;
45
45
  params;
46
46
  others = new Map();
47
+ indentCount = 0;
47
48
 
48
49
  constructor(worker, valueSet, params) {
49
50
  validateParameter(worker, "worker", TerminologyWorker);
@@ -302,8 +303,8 @@ class ValueSetChecker {
302
303
  this.seeValueSet();
303
304
  this.worker.opContext.addNote(this.valueSet, 'Analysing ' + this.valueSet.vurl + ' for validation purposes', this.indentCount);
304
305
  if (this.indentCount === 0) {
305
- this.worker.opContext.addNote(this.valueSet, 'Parameters: ' + this.params.summary, this.indentCount);
306
- let vrs = this.params.verSummary;
306
+ this.worker.opContext.addNote(this.valueSet, 'Parameters: ' + this.params.summary(), this.indentCount);
307
+ let vrs = this.params.verSummary();
307
308
  if (vrs) {
308
309
  this.worker.opContext.addNote(this.valueSet, 'Version Rules: ' + vrs, this.indentCount);
309
310
  }
@@ -1731,7 +1732,7 @@ class ValueSetChecker {
1731
1732
  let list = [];
1732
1733
  for (let filter of cset.filter) {
1733
1734
  let s = cset.filter.length > 1 ? "(" : "";
1734
- s = filter.prop+" "+filter.op+" "+filter.value;
1735
+ s = filter.property+" "+filter.op+" "+filter.value;
1735
1736
  s = s + (cset.filter.length > 1 ? ")" : "");
1736
1737
  list.push(s)
1737
1738
  }
@@ -316,7 +316,7 @@ class TerminologyWorker {
316
316
  for (const ext of supplementExtensions) {
317
317
  const supplementUrl = ext.valueString || ext.valueUri;
318
318
  if (supplementUrl && !cs.hasSupplement(this.opContext, supplementUrl)) {
319
- throw new TerminologyError(`ValueSet depends on supplement '${supplementUrl}' on ${cs.systemUri} that is not known`);
319
+ throw new TerminologyError(`ValueSet depends on supplement '${supplementUrl}' on ${cs.system} that is not known`);
320
320
  }
321
321
  }
322
322
  }
@@ -1,4 +1,5 @@
1
1
  const {VersionUtilities} = require("../../library/version-utilities");
2
+ const {Extensions} = require("../library/extensions");
2
3
 
3
4
  /**
4
5
  * Converts input TerminologyCapabilities to R5 format (modifies input object for performance)
@@ -14,7 +15,17 @@ function terminologyCapabilitiesToR5(jsonObj, sourceVersion) {
14
15
  }
15
16
 
16
17
  if (VersionUtilities.isR4Ver(sourceVersion)) {
17
- // R4 to R5: No major structural changes needed for TerminologyCapabilities
18
+ for (const cs of jsonObj.codeSystem || []) {
19
+ if (cs.content) {
20
+ let cnt = Extensions.readString(cs, "http://hl7.org/fhir/5.0/StructureDefinition/extension-TerminologyCapabilities.codeSystem.content");
21
+ if (cnt) {
22
+ delete cs.extensions;
23
+ cs.content = cnt;
24
+ }
25
+ }
26
+ }
27
+
28
+
18
29
  return jsonObj;
19
30
  }
20
31
 
@@ -125,6 +136,15 @@ function terminologyCapabilitiesR5ToR4(r5Obj) {
125
136
  if (r5Obj.versionAlgorithmCoding) {
126
137
  delete r5Obj.versionAlgorithmCoding;
127
138
  }
139
+ for (const cs of r5Obj.codeSystem || []) {
140
+ if (cs.content) {
141
+ if (!cs.extension) {
142
+ cs.extension = [];
143
+ }
144
+ cs.extension.push({"url" : "http://hl7.org/fhir/5.0/StructureDefinition/extension-TerminologyCapabilities.codeSystem.content", valueCode : cs.content});
145
+ delete cs.content;
146
+ }
147
+ }
128
148
 
129
149
  return r5Obj;
130
150
  }