fhirsmith 0.8.0 → 0.8.3

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.
@@ -9,9 +9,6 @@
9
9
  <meta content="http://hl7.org/fhir" name="author"/>
10
10
  <meta charset="utf-8" http-equiv="X-UA-Compatible" content="IE=edge" />
11
11
 
12
- <link rel="stylesheet" href="/fhir.css"/>
13
-
14
-
15
12
  <!-- Bootstrap core CSS -->
16
13
  <link rel="stylesheet" href="/assets/css/bootstrap.css"/>
17
14
  <link rel="stylesheet" href="/assets/css/bootstrap-fhir.css"/>
package/security.md CHANGED
@@ -29,4 +29,7 @@ A typical NGINX configuration would be:
29
29
  limit_conn perip 50;
30
30
  limit_conn perserver 500;
31
31
  ```
32
+ ## SSL
33
+
34
+ This server doesn't provide SSL support - use an NGINX reverse proxy for that.
32
35
 
package/server.js CHANGED
@@ -11,6 +11,7 @@ const express = require('express');
11
11
  const path = require('path');
12
12
  const fs = require('fs');
13
13
  const os = require('os');
14
+ const v8 = require('v8');
14
15
  const folders = require('./library/folder-setup'); // <-- ADD: load early
15
16
  const { statSync, readdirSync } = require('fs');
16
17
  const escape = require('escape-html');
@@ -41,8 +42,8 @@ serverLog.info(`Data directory: ${folders.dataDir()}`);
41
42
  serverLog.info(`========================================`);
42
43
 
43
44
  const activeModules = config.modules ? Object.keys(config.modules)
44
- .filter(mod => config.modules[mod].enabled)
45
- .join(', ') : [];
45
+ .filter(mod => config.modules[mod].enabled)
46
+ .join(', ') : [];
46
47
  serverLog.info(`Loaded Configuration. Active modules = ${activeModules}`);
47
48
 
48
49
  // Import modules
@@ -541,20 +542,21 @@ app.get('/dashboard', async (req, res) => {
541
542
 
542
543
  // Memory usage
543
544
  const memUsage = process.memoryUsage();
544
- const heapUsedPCT = (memUsage.heapUsed * 100) / memUsage.heapTotal;
545
- const freeMemMB = (os.freemem() / 1024 / 1024).toFixed(0);
546
- const totalMemMB = (os.totalmem() / 1024 / 1024).toFixed(0);
547
- const usedMemPCT = 100 - ((freeMemMB * 100) / totalMemMB);
545
+ const heapStats = v8.getHeapStatistics();
546
+ const nodeMemPCT = (memUsage.heapUsed * 100) / heapStats.heap_size_limit; // % of Node.js memory limit used
547
+ const totalMemBytes = os.totalmem();
548
+ const freeMemBytes = os.freemem();
549
+ const sysMemPCT = ((totalMemBytes - freeMemBytes) * 100) / totalMemBytes; // % of system memory used
548
550
  const fstats = fs.statfsSync(folders.logsDir());
549
- const diskPCT = (fstats.bavail * 100) / fstats.blocks;
551
+ const diskPCT = 100 - ((fstats.bavail * 100) / fstats.blocks); // % of disk used
550
552
 
551
- let content = '';
552
- content += '<table border="1">';
553
+ let content = '<style>table.grid{margin-bottom:10px;border:1px solid black;margin-right:auto}table.grid th,table.grid td{border:1px solid silver;padding:3px 7px 2px;font-size:12px;line-height:1.4em;font-family:verdana;vertical-align:top}table.grid th{font-weight:bold}table.grid td{font-weight:normal}</style>';
554
+ content += '<table class="grid">';
553
555
  content += '<tr>';
554
556
  content += `<td><strong>Uptime:</strong> ${escape(uptimeStr)}</td>`;
555
557
  content += `<td><strong>Request Count:</strong> ${stats.requestCount} (static: ${stats.staticRequestCount})</td>`;
556
- content += `<td style="background-color:${pctColor(usedMemPCT)}"><strong>Memory:</strong> ${usedMemPCT.toFixed(0)}%</td>`;
557
- content += `<td style="background-color:${pctColor(heapUsedPCT)}"><strong>Heap:</strong> ${heapUsedPCT.toFixed(0)}%</td>`;
558
+ content += `<td style="background-color:${pctColor(nodeMemPCT)}"><strong>Node Memory:</strong> ${nodeMemPCT.toFixed(0)}%</td>`;
559
+ content += `<td style="background-color:${pctColor(sysMemPCT)}"><strong>System Memory:</strong> ${sysMemPCT.toFixed(0)}%</td>`;
558
560
  content += `<td style="background-color:${pctColor(diskPCT)}"><strong>Disk:</strong> ${diskPCT.toFixed(0)}%</td>`;
559
561
  content += '</tr>';
560
562
  content += '</table>';
@@ -590,9 +592,12 @@ app.get('/dashboard', async (req, res) => {
590
592
  });
591
593
 
592
594
  function pctColor(pct) {
593
- const r = Math.round(pct * 2.55);
594
- const g = Math.round((100 - pct) * 2.55);
595
- return `rgb(${r}, ${g}, 100)`; // the 100 keeps it pastel/light
595
+ // Gradient from green (#deffe0) at 0% to red (#ffd3d1) at 100%
596
+ const t = Math.max(0, Math.min(100, pct)) / 100;
597
+ const r = Math.round(222 + 33 * t); // 222 -> 255
598
+ const g = Math.round(255 - 44 * t); // 255 -> 211
599
+ const b = Math.round(224 - 15 * t); // 224 -> 209
600
+ return `rgb(${r}, ${g}, ${b})`;
596
601
  }
597
602
 
598
603
  // Health check endpoint
@@ -659,10 +664,10 @@ function getLogStats() {
659
664
  const limitInfo = `${maxFiles} files × ${maxSize} each`;
660
665
 
661
666
  return '<tr>'
662
- + `<td><strong>Existing Logs:</strong> ${files.length} (${sizeMB} MB)</td>`
663
- + `<td><strong>Retention Policy:</strong> ${limitInfo}</td>`
664
- + diskInfo
665
- + '</tr>';
667
+ + `<td><strong>Existing Logs:</strong> ${files.length} (${sizeMB} MB)</td>`
668
+ + `<td><strong>Retention Policy:</strong> ${limitInfo}</td>`
669
+ + diskInfo
670
+ + '</tr>';
666
671
  } catch (e) {
667
672
  return `<tr><td colspan="3"><strong>Logs:</strong> unable to read (${e.message})</td></tr>`;
668
673
  }
@@ -793,12 +798,12 @@ async function serveFhirsmithHome(req, res) {
793
798
  endpoints: {
794
799
  health: '/health',
795
800
  ...Object.fromEntries(
796
- Object.keys(enabledModules)
797
- .filter(m => m !== 'tx')
798
- .map(m => [
799
- m,
800
- m === 'vcl' ? '/VCL' : `/${m}`
801
- ])
801
+ Object.keys(enabledModules)
802
+ .filter(m => m !== 'tx')
803
+ .map(m => [
804
+ m,
805
+ m === 'vcl' ? '/VCL' : `/${m}`
806
+ ])
802
807
  ),
803
808
  // Add TX endpoints separately
804
809
  ...(enabledModules.tx ? {
package/shl/readme.md ADDED
@@ -0,0 +1,27 @@
1
+ To generate a certificate:
2
+
3
+ ```shell
4
+ # Generate EC P-256 private key
5
+ openssl ecparam -name prime256v1 -genkey -noout -out private-key.pem
6
+
7
+ # Extract the public key / self-signed cert
8
+ openssl req -new -x509 -key private-key.pem -out public-key.pem -days 3650 \
9
+ -subj "/CN=fhirsmith-shl"
10
+
11
+ node -e "
12
+ const crypto = require('crypto');
13
+ const fs = require('fs');
14
+ const key = crypto.createPrivateKey(fs.readFileSync('private-key.pem'));
15
+ const jwk = key.export({format:'jwk'});
16
+ const thumbprint = crypto.createHash('sha256')
17
+ .update(JSON.stringify({crv:jwk.crv,kty:jwk.kty,x:jwk.x,y:jwk.y}))
18
+ .digest('base64url');
19
+ console.log('kid: '+thumbprint);
20
+ "
21
+
22
+ ```
23
+
24
+ Config:
25
+
26
+ All files will be in {data}/shl. the file paths in the config are relative to
27
+ that location
package/shl/shl.js CHANGED
@@ -396,14 +396,8 @@ class SHLModule {
396
396
 
397
397
  loadCertificates() {
398
398
  try {
399
- const certPath = path.resolve(__dirname, this.config.certificates.certFile);
400
- const keyPath = path.resolve(__dirname, this.config.certificates.keyFile);
401
-
402
- // Validate paths to prevent directory traversal
403
- if (!certPath.startsWith(path.resolve(__dirname)) ||
404
- !keyPath.startsWith(path.resolve(__dirname))) {
405
- throw new Error('Certificate paths outside allowed directory');
406
- }
399
+ const certPath = folders.filePath('shl', this.config.certificates.certFile);
400
+ const keyPath = folders.filePath('shl', this.config.certificates.keyFile);
407
401
 
408
402
  const certPem = fs.readFileSync(certPath, 'utf8');
409
403
  const keyPem = fs.readFileSync(keyPath, 'utf8');
package/stats.js CHANGED
@@ -118,7 +118,7 @@ class ServerStats {
118
118
  if (this.taskMap.size == 0) {
119
119
  return "";
120
120
  }
121
- let html = '<table class="grid">';
121
+ let html = '<table class="grid" >';
122
122
  html += "<tr><th>Background Task</th><th>Status</th><th>Frequency</th><th>Last Seen</th></tr>";
123
123
  for (let m of this.taskMap.keys()) {
124
124
  let mm = this.taskMap.get(m);
package/tx/cs/cs-api.js CHANGED
@@ -673,11 +673,13 @@ class CodeSystemProvider {
673
673
  /**
674
674
  * register the concept maps that are implicitly defined as part of the code system
675
675
  *
676
+ * @param {ConceptMap} map the map (this will have been returned from findImplicitConceptMap)
676
677
  * @param {Coding} coding the coding to translate
677
- * @param {String} target
678
- * @returns {CodeTranslation[]} the list of translations
678
+ * @param {String} target the target code system
679
+ * @param {boolean} reverse - if the translation is being run backwards
680
+ * @returns {CodeTranslation[]} the list of translations, each CodeTranslation has map, code, system, version, display, and relationship
679
681
  */
680
- async getTranslations(coding, target) { return null;}
682
+ async getTranslations(map, coding, target, reverse) { return null;}
681
683
 
682
684
  // ==== Parameter checking methods =========
683
685
  _ensureLanguages(param) {
@@ -853,7 +855,7 @@ class CodeSystemFactoryProvider {
853
855
  }
854
856
 
855
857
  /**
856
- * see comemnts for registerSupplements()
858
+ * see comments for registerSupplements()
857
859
  *
858
860
  * @param {CodeSystem} supplement - the supplement to flesh out
859
861
  * @returns void
@@ -865,6 +867,8 @@ class CodeSystemFactoryProvider {
865
867
  /**
866
868
  * build and return a known concept map from the URL, if there is one.
867
869
  *
870
+ * the conceptmap is never visible to a user; if it has an implicitSource, then
871
+ * provider.getTranslations will be called when it's actually used
868
872
  * @param url
869
873
  * @param version
870
874
  * @returns {ConceptMap}
package/tx/cs/cs-omop.js CHANGED
@@ -660,8 +660,10 @@ class OMOPServices extends BaseCSServices {
660
660
  }
661
661
 
662
662
  // Translation support
663
- async getTranslations(coding, target) {
664
-
663
+ async getTranslations(map, coding, target) {
664
+ if (map == null) {
665
+ return;
666
+ }
665
667
 
666
668
  const vocabId = getVocabId(target);
667
669
  if (vocabId === -1) {
@@ -680,7 +682,7 @@ class OMOPServices extends BaseCSServices {
680
682
  reject(err);
681
683
  } else {
682
684
  const translations = rows.map(row => ({
683
- uri: target,
685
+ system: target,
684
686
  code: row.concept_code,
685
687
  display: row.concept_name,
686
688
  relationship: 'equivalent',
@@ -395,24 +395,24 @@ class RxNormServices extends CodeSystemProvider {
395
395
  } else if (this.rels.includes(prop)) {
396
396
  if (value.startsWith('CUI:')) {
397
397
  const cui = value.substring(4);
398
- sql = `AND (${this.getCodeField()} IN (SELECT ${this.getCodeField()} FROM rxnconso WHERE RXCUI IN (SELECT RXCUI1 FROM rxnrel WHERE REL = $rel AND RXCUI2 = $cui2)))`;
398
+ sql = `AND (${this.getCodeField()} IN (SELECT ${this.getCodeField()} FROM rxnconso WHERE RXCUI IN (SELECT RXCUI2 FROM rxnrel WHERE REL = $rel AND RXCUI1 = $cui2)))`;
399
399
  params.rel = this.#sqlWrapString(prop);
400
400
  params.cui2 = this.#sqlWrapString(cui);
401
401
  } else if (value.startsWith('AUI:')) {
402
402
  const aui = value.substring(4);
403
- sql = `AND (${this.getCodeField()} IN (SELECT ${this.getCodeField()} FROM rxnconso WHERE RXAUI IN (SELECT RXAUI1 FROM rxnrel WHERE REL = $rel AND RXAUI2 = $aui2)))`;
403
+ sql = `AND (${this.getCodeField()} IN (SELECT ${this.getCodeField()} FROM rxnconso WHERE RXAUI IN (SELECT RXAUI2 FROM rxnrel WHERE REL = $rel AND RXAUI1 = $aui2)))`;
404
404
  params.rel = this.#sqlWrapString(prop);
405
405
  params.aui2 = this.#sqlWrapString(aui);
406
406
  }
407
407
  } else if (this.reltypes.includes(prop)) {
408
408
  if (value.startsWith('CUI:')) {
409
409
  const cui = value.substring(4);
410
- sql = `AND (${this.getCodeField()} IN (SELECT ${this.getCodeField()} FROM rxnconso WHERE RXCUI IN (SELECT RXCUI1 FROM rxnrel WHERE RELA = $rela AND RXCUI2 = $cui2)))`;
410
+ sql = `AND (${this.getCodeField()} IN (SELECT ${this.getCodeField()} FROM rxnconso WHERE RXCUI IN (SELECT RXCUI2 FROM rxnrel WHERE RELA = $rela AND RXCUI1 = $cui2)))`;
411
411
  params.rela = this.#sqlWrapString(prop);
412
412
  params.cui2 = this.#sqlWrapString(cui);
413
413
  } else if (value.startsWith('AUI:')) {
414
414
  const aui = value.substring(4);
415
- sql = `AND (${this.getCodeField()} IN (SELECT ${this.getCodeField()} FROM rxnconso WHERE RXAUI IN (SELECT RXAUI1 FROM rxnrel WHERE RELA = $rela AND RXAUI2 = $aui2)))`;
415
+ sql = `AND (${this.getCodeField()} IN (SELECT ${this.getCodeField()} FROM rxnconso WHERE RXAUI IN (SELECT RXAUI2 FROM rxnrel WHERE RELA = $rela AND RXAUI1 = $aui2)))`;
416
416
  params.rela = this.#sqlWrapString(prop);
417
417
  params.aui2 = this.#sqlWrapString(aui);
418
418
  }
@@ -498,8 +498,6 @@ class RxNormServices extends CodeSystemProvider {
498
498
  }
499
499
 
500
500
  async filterSize(filterContext, set) {
501
-
502
-
503
501
  if (!set.executed) {
504
502
  await this.#executeFilter(set);
505
503
  }
@@ -12,6 +12,7 @@ const {
12
12
  const {DesignationUse} = require("../library/designations");
13
13
  const {BaseCSServices} = require("./cs-base");
14
14
  const {formatDateMMDDYYYY} = require("../../library/utilities");
15
+ const {ConceptMap} = require("../library/conceptmap");
15
16
 
16
17
  // Context kinds matching Pascal enum
17
18
  const SnomedProviderContextKind = {
@@ -1213,6 +1214,64 @@ class SnomedProvider extends BaseCSServices {
1213
1214
  (cd.use.code === '900000000000013009' || cd.use.code === '900000000000003001');
1214
1215
  }
1215
1216
 
1217
+ async getTranslations(map, coding, target, reverse) {
1218
+ if (!map || (target && target !== this.system()) || reverse) {
1219
+ return [];
1220
+ }
1221
+ let ref = this.sct.concepts.findConcept(map.id);
1222
+ if (!ref.found) {
1223
+ return [];
1224
+ }
1225
+ let rref = this.sct.refSetIndex.getRefSetByConcept(ref.index);
1226
+ if (rref == -1) {
1227
+ return [];
1228
+ }
1229
+ let refSetRecord = this.sct.refSetIndex.getReferenceSet(rref);
1230
+ let members = this.sct.refSetMembers.getMembers(refSetRecord.membersByRef);
1231
+ let srcConcept = this.sct.concepts.findConcept(coding.code);
1232
+ if (!srcConcept.found) {
1233
+ return [];
1234
+ }
1235
+
1236
+ let result = [];
1237
+ let L = 0;
1238
+ let H = members.length - 1;
1239
+ while (L <= H) {
1240
+ const I = Math.floor((L + H) / 2);
1241
+ const ref = members[I].ref;
1242
+ if (ref < srcConcept.index) {
1243
+ L = I + 1;
1244
+ } else if (ref > srcConcept.index) {
1245
+ H = I - 1;
1246
+ } else {
1247
+ // Found — but scan left for first match in case of duplicates
1248
+ let first = I;
1249
+ while (first > 0 && members[first - 1].ref === srcConcept.index) {
1250
+ first--;
1251
+ }
1252
+ // Process all matching members
1253
+ for (let i = first; i < members.length && members[i].ref === srcConcept.index; i++) {
1254
+ let values = this.sct.refs.getReferences(members[i].values);
1255
+ if (values && values.length >= 1) {
1256
+ let tgtId = String(this.sct.concepts.getConceptId(values[0]));
1257
+ let ct = {
1258
+ map: map.vurl,
1259
+ code: tgtId,
1260
+ system: this.system(),
1261
+ version : this.version(),
1262
+ display: await this.display(tgtId),
1263
+ relationship: map.jsonObj.relationship
1264
+ }
1265
+ result.push(ct);
1266
+ }
1267
+ }
1268
+ break;
1269
+ }
1270
+ }
1271
+
1272
+ return result;
1273
+ }
1274
+
1216
1275
  }
1217
1276
 
1218
1277
  /**
@@ -1339,7 +1398,7 @@ class SnomedServicesFactory extends CodeSystemFactoryProvider {
1339
1398
  return null;
1340
1399
  }
1341
1400
  let rref = this.snomedServices.refSetIndex.getRefSetByConcept(ref.index);
1342
- if (rref == 0) {
1401
+ if (rref == -1) {
1343
1402
  return null;
1344
1403
  }
1345
1404
  return {
@@ -1448,6 +1507,59 @@ class SnomedServicesFactory extends CodeSystemFactoryProvider {
1448
1507
 
1449
1508
  return edition + ' ' + formatDateMMDDYYYY(match[2].substring(4, 6) + match[2].substring(6, 8) + match[2].substring(0, 4));
1450
1509
  }
1510
+
1511
+ async findImplicitConceptMap(url, version) {
1512
+ if (version && (version !== this.version())) {
1513
+ return null;
1514
+ }
1515
+ if (!url || !url.startsWith(this.system()+"?fhir_cm=")) {
1516
+ return null;
1517
+ }
1518
+ let id = url.substring(url.indexOf("=")+1);
1519
+ if (['900000000000523009', '900000000000526001', '900000000000527005', '900000000000530003'].includes(id)) {
1520
+ let name = '';
1521
+ let relationship = '';
1522
+ switch (id) {
1523
+ case '900000000000523009':
1524
+ name = 'POSSIBLY EQUIVALENT TO';
1525
+ relationship = 'inexact';
1526
+ break;
1527
+ case '900000000000526001':
1528
+ name = 'REPLACED BY';
1529
+ relationship = 'equivalent';
1530
+ break;
1531
+ case '900000000000527005':
1532
+ name = 'SAME AS';
1533
+ relationship = 'equal';
1534
+ break;
1535
+ case '900000000000530003':
1536
+ name = 'ALTERNATIVE';
1537
+ relationship = 'inexact';
1538
+ break;
1539
+ }
1540
+ let cm = {
1541
+ resourceType: 'ConceptMap',
1542
+ internalSource : this,
1543
+ relationship: relationship,
1544
+ id : id,
1545
+ url: `${this.system}?fhir_cm=${id}`,
1546
+ version: this.version(),
1547
+ name: `SNOMED CT ${name} Concept Map`,
1548
+ description: `The concept map implicitly defined by the ${name} Association Reference Set`,
1549
+ copyright: 'This value set includes content from SNOMED CT, which is copyright © 2002+ International Health Terminology Standards Development Organisation (SNOMED International), and distributed by agreement between SNOMED International and HL7',
1550
+ status: 'active',
1551
+ sourceUri: `${this.system}?fhir_vs`,
1552
+ targetUri: `${this.system}?fhir_vs`,
1553
+ group: [{
1554
+ source: 'http://snomed.info/sct',
1555
+ target: 'http://snomed.info/sct'
1556
+ }]
1557
+ }
1558
+ return new ConceptMap(cm);
1559
+ } else {
1560
+ return null;
1561
+ }
1562
+ }
1451
1563
  }
1452
1564
 
1453
1565
  function getEditionName(edition) {
Binary file
@@ -0,0 +1,186 @@
1
+ {
2
+ "resourceType" : "CodeSystem",
3
+ "language" : "de",
4
+ "id" : "de-multi",
5
+ "url" : "http://hl7.org/fhir/test/CodeSystem/de-multi",
6
+ "name" : "TestCodeSystemMultiLangDE",
7
+ "title" : "Testcodesystem mit mehreren Sprachen",
8
+ "title:en" : "Test Code System with Multiple Languages",
9
+ "_title" : {
10
+ "extension" : [{
11
+ "url" : "http://hl7.org/fhir/StructureDefinition/translation",
12
+ "extension" : [{
13
+ "url" : "lang",
14
+ "valueCode" : "en"
15
+ },{
16
+ "url" : "content",
17
+ "valueString" : "Test Code System with Multiple Languages"
18
+ }]
19
+ }]
20
+ },
21
+ "status" : "active",
22
+ "experimental" : false,
23
+ "date" : "2023-04-01",
24
+ "publisher" : "FHIR Project",
25
+ "caseSensitive" : true,
26
+ "content" : "complete",
27
+ "concept" : [{
28
+ "code" : "code1",
29
+ "display" : "Anzeige 1",
30
+ "definition" : "Mein erster Code",
31
+ "definition:en" : "My first code",
32
+ "_definition" : {
33
+ "extension" : [{
34
+ "url" : "http://hl7.org/fhir/StructureDefinition/translation",
35
+ "extension" : [{
36
+ "url" : "lang",
37
+ "valueCode" : "en"
38
+ },{
39
+ "url" : "content",
40
+ "valueString" : "My first code"
41
+ }]
42
+ }]
43
+ },
44
+ "designation" : [{
45
+ "language" : "en",
46
+ "value" : "Display 1"
47
+ }]
48
+ },
49
+ {
50
+ "code" : "code2",
51
+ "display" : "Anzeige 2",
52
+ "definition" : "Mein zweiter Code, mit Kindern",
53
+ "definition:en" : "My second code, with children",
54
+ "_definition" : {
55
+ "extension" : [{
56
+ "url" : "http://hl7.org/fhir/StructureDefinition/translation",
57
+ "extension" : [{
58
+ "url" : "lang",
59
+ "valueCode" : "en"
60
+ },{
61
+ "url" : "content",
62
+ "valueString" : "My second code, with children"
63
+ }]
64
+ }]
65
+ },
66
+ "designation" : [{
67
+ "language" : "de-CH",
68
+ "value" : "Anzeige 2"
69
+ },{
70
+ "language" : "es",
71
+ "value" : "Mostrar 2"
72
+ },{
73
+ "language" : "en",
74
+ "value" : "Display 2"
75
+ }],
76
+ "concept" : [{
77
+ "code" : "code2a",
78
+ "display" : "Anzeige 2a",
79
+ "definition" : "Mein erster Second-Level-Code",
80
+ "definition:en" : "My first second level code",
81
+ "_definition" : {
82
+ "extension" : [{
83
+ "url" : "http://hl7.org/fhir/StructureDefinition/translation",
84
+ "extension" : [{
85
+ "url" : "lang",
86
+ "valueCode" : "en"
87
+ },{
88
+ "url" : "content",
89
+ "valueString" : "My first second level code"
90
+ }]
91
+ }]
92
+ },
93
+ "designation" : [{
94
+ "language" : "en",
95
+ "value" : "Display 2a"
96
+ },{
97
+ "language" : "es",
98
+ "value" : "Mostrar 2a"
99
+ }],
100
+ "concept" : [{
101
+ "code" : "code2aI",
102
+ "display" : "Anzeige 2aI",
103
+ "definition" : "Mein erster Third-Level-Code",
104
+ "definition:en" : "My first third level code",
105
+ "_definition" : {
106
+ "extension" : [{
107
+ "url" : "http://hl7.org/fhir/StructureDefinition/translation",
108
+ "extension" : [{
109
+ "url" : "lang",
110
+ "valueCode" : "en"
111
+ },{
112
+ "url" : "content",
113
+ "valueString" : "My first third level code"
114
+ }]
115
+ }]
116
+ },
117
+ "designation" : [{
118
+ "language" : "es",
119
+ "value" : "Mostrar 2aI"
120
+ }]
121
+ },
122
+ {
123
+ "code" : "code2aII",
124
+ "display" : "Anzeige 2aII",
125
+ "definition" : "Mein zweiter Third-Level-Code",
126
+ "definition:en" : "My second third level code",
127
+ "_definition" : {
128
+ "extension" : [{
129
+ "url" : "http://hl7.org/fhir/StructureDefinition/translation",
130
+ "extension" : [{
131
+ "url" : "lang",
132
+ "valueCode" : "en"
133
+ },{
134
+ "url" : "content",
135
+ "valueString" : "My second third level code"
136
+ }]
137
+ }]
138
+ }
139
+ }]
140
+ },
141
+ {
142
+ "code" : "code2b",
143
+ "display" : "Anzeige 2b",
144
+ "definition" : "Mein zweiter Second-Level-Code",
145
+ "definition:en" : "My second second level code",
146
+ "_definition" : {
147
+ "extension" : [{
148
+ "url" : "http://hl7.org/fhir/StructureDefinition/translation",
149
+ "extension" : [{
150
+ "url" : "lang",
151
+ "valueCode" : "en"
152
+ },{
153
+ "url" : "content",
154
+ "valueString" : "My second second level code"
155
+ }]
156
+ }]
157
+ },
158
+ "designation" : [{
159
+ "language" : "en",
160
+ "value" : "Display 2b"
161
+ }]
162
+ }]
163
+ },
164
+ {
165
+ "code" : "code3",
166
+ "display" : "Anzeige 3",
167
+ "definition" : "Mein Third-Level-Code",
168
+ "definition:en" : "My Third Level Code",
169
+ "_definition" : {
170
+ "extension" : [{
171
+ "url" : "http://hl7.org/fhir/StructureDefinition/translation",
172
+ "extension" : [{
173
+ "url" : "lang",
174
+ "valueCode" : "en"
175
+ },{
176
+ "url" : "content",
177
+ "valueString" : "My Third Level Code"
178
+ }]
179
+ }]
180
+ },
181
+ "designation" : [{
182
+ "language" : "en",
183
+ "value" : "Display 3"
184
+ }]
185
+ }]
186
+ }