fhirsmith 0.11.2 → 0.13.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 +377 -2
- package/README.md +1 -1
- package/config-template.json +31 -1
- package/library/languages.js +191 -34
- package/library/path-safety.js +51 -0
- package/library/request-token.js +56 -0
- package/package.json +2 -2
- package/packages/packages.js +17 -118
- package/publisher/publisher.js +208 -30
- package/publisher/validation.js +210 -0
- package/server.js +24 -15
- package/stats-db.js +127 -0
- package/stats.js +172 -6
- package/translations/Messages.properties +5 -1
- package/tx/README.md +21 -0
- package/tx/cs/cs-api.js +30 -1
- package/tx/cs/cs-api.md +1 -1
- package/tx/cs/cs-cs.js +1 -1
- package/tx/cs/cs-icd11.js +805 -0
- package/tx/cs/cs-lang.js +331 -85
- package/tx/cs/cs-loinc.js +45 -4
- package/tx/cs/cs-mimetypes.js +492 -21
- package/tx/cs/cs-snomed.js +141 -58
- package/tx/cs/cs-ucum.js +33 -3
- package/tx/cs/icd11-expressions.js +243 -0
- package/tx/importers/export-icd11-codesystem.js +823 -0
- package/tx/importers/icd11-schema.md +285 -0
- package/tx/importers/import-icd11.js +861 -0
- package/tx/importers/import-loinc.module.js +14 -7
- package/tx/importers/import-sct.module.js +10 -1
- package/tx/library/codesystem.js +21 -27
- package/tx/library/conceptmap.js +39 -0
- package/tx/library/nesting-limit.js +79 -0
- package/tx/library/operation-outcome.js +97 -3
- package/tx/library/parameter-check.js +139 -0
- package/tx/library/valueset.js +16 -20
- package/tx/library.js +23 -1
- package/tx/ocl/cache/cache-utils.cjs +5 -3
- package/tx/ocl/cs-ocl.cjs +73 -17
- package/tx/ocl/jobs/background-queue.cjs +6 -3
- package/tx/ocl/mappers/concept-mapper.cjs +9 -1
- package/tx/ocl/shared/patches.cjs +48 -1
- package/tx/ocl/vs-ocl.cjs +15 -13
- package/tx/operation-context.js +220 -10
- package/tx/params.js +226 -175
- package/tx/sct/expressions.js +677 -15
- package/tx/tx.fhir.org.yml +3 -2
- package/tx/tx.js +72 -92
- package/tx/workers/batch-validate.js +11 -18
- package/tx/workers/batch.js +16 -28
- package/tx/workers/cache-control.js +87 -17
- package/tx/workers/closure.js +3 -8
- package/tx/workers/compare.js +6 -34
- package/tx/workers/expand.js +108 -60
- package/tx/workers/lookup.js +35 -40
- package/tx/workers/read.js +6 -40
- package/tx/workers/search.js +2 -8
- package/tx/workers/subsumes.js +58 -41
- package/tx/workers/translate.js +472 -47
- package/tx/workers/validate.js +83 -78
- package/tx/workers/worker.js +92 -11
- package/tx/xml/xml-base.js +264 -77
- package/tx/xversion/xv-conceptmap.js +81 -0
- package/tx/xversion/xv-parameters.js +8 -0
- package/tx/xversion/xv-valueset.js +39 -1
- package/utilities/merge-audit.py +178 -0
- package/utilities/why-merge-audit.md +39 -0
- package/tx/data/snomed-subset.txt +0 -84
package/tx/tx.fhir.org.yml
CHANGED
|
@@ -26,17 +26,18 @@ sources:
|
|
|
26
26
|
- snomed:sct_us_20230301.cache
|
|
27
27
|
- snomed:sct_us_20240301.cache
|
|
28
28
|
- snomed:sct_us_20250901.cache
|
|
29
|
-
- snomed:
|
|
29
|
+
- snomed:sct_test_20250909b.cache
|
|
30
30
|
- cpt:CodeSystem-cpt.db|cpt-2023-fragment-0.1.db
|
|
31
31
|
- omop:omop_v20250227.db
|
|
32
32
|
- npm:hl7.terminology
|
|
33
|
-
- npm:fhir.tx.support
|
|
33
|
+
- npm:fhir.tx.support
|
|
34
34
|
- npm:ihe.formatcode.fhir
|
|
35
35
|
- npm:fhir.dicom
|
|
36
36
|
- npm:hl7.fhir.us.core
|
|
37
37
|
- npm:us.cdc.phinvads
|
|
38
38
|
- npm:hl7.fhir.uv.sdc
|
|
39
39
|
- internal:vsac
|
|
40
|
+
- icd11:icd11-2026-01.db
|
|
40
41
|
|
|
41
42
|
ignored:
|
|
42
43
|
- urn:iso:std:iso:3166#20210120
|
package/tx/tx.js
CHANGED
|
@@ -51,6 +51,7 @@ const ClosureWorker = require("./workers/closure");
|
|
|
51
51
|
const {BundleXML} = require("./xml/bundle-xml");
|
|
52
52
|
const ConceptUsageTracker = require("./usage-tracker");
|
|
53
53
|
const ProblemFinder = require("./problems");
|
|
54
|
+
const { buildOperationOutcome } = require('./library/operation-outcome');
|
|
54
55
|
// const {writeFileSync} = require("fs");
|
|
55
56
|
|
|
56
57
|
class TXModule {
|
|
@@ -249,6 +250,10 @@ class TXModule {
|
|
|
249
250
|
// cacheTimeout is in minutes, default to 30 minutes
|
|
250
251
|
const cacheTimeoutMs = cacheTimeoutMinutes * 60 * 1000;
|
|
251
252
|
const pruneIntervalMs = 5 * 60 * 1000; // Run every 5 minutes
|
|
253
|
+
// Tell the cache what timeout it is being pruned against, so $cache-control?mode=check
|
|
254
|
+
// can report it and a client can size its keepalive interval to this server rather
|
|
255
|
+
// than guessing at a number it has no way to see.
|
|
256
|
+
endpointInfo.resourceCache.setIdleTimeout(cacheTimeoutMs);
|
|
252
257
|
if (this.stats) {
|
|
253
258
|
this.stats.addTask("Client Cache", "5 min");
|
|
254
259
|
}
|
|
@@ -433,14 +438,7 @@ class TXModule {
|
|
|
433
438
|
}
|
|
434
439
|
} catch (e) {
|
|
435
440
|
this.log.error(`JSON parse error: ${e.message}`);
|
|
436
|
-
return res.status(400).json({
|
|
437
|
-
resourceType: 'OperationOutcome',
|
|
438
|
-
issue: [{
|
|
439
|
-
severity: 'error',
|
|
440
|
-
code: 'invalid',
|
|
441
|
-
diagnostics: `Invalid JSON: ${e.message}`
|
|
442
|
-
}]
|
|
443
|
-
});
|
|
441
|
+
return res.status(400).json(buildOperationOutcome('error', 'invalid', `Invalid JSON: ${e.message}`));
|
|
444
442
|
}
|
|
445
443
|
}
|
|
446
444
|
|
|
@@ -461,25 +459,11 @@ class TXModule {
|
|
|
461
459
|
req.body = this.convertXmlToResource(xmlStr);
|
|
462
460
|
} catch (e) {
|
|
463
461
|
this.log.error(`XML parse error: ${e.message}`);
|
|
464
|
-
return res.status(400).json({
|
|
465
|
-
resourceType: 'OperationOutcome',
|
|
466
|
-
issue: [{
|
|
467
|
-
severity: 'error',
|
|
468
|
-
code: 'invalid',
|
|
469
|
-
diagnostics: `Invalid XML: ${e.message}`
|
|
470
|
-
}]
|
|
471
|
-
});
|
|
462
|
+
return res.status(400).json(buildOperationOutcome('error', 'invalid', `Invalid XML: ${e.message}`));
|
|
472
463
|
}
|
|
473
464
|
}
|
|
474
465
|
} else if (contentType != 'application/x-www-form-urlencoded') {
|
|
475
|
-
return res.status(415).json({
|
|
476
|
-
resourceType: 'OperationOutcome',
|
|
477
|
-
issue: [{
|
|
478
|
-
severity: 'error',
|
|
479
|
-
code: 'invalid',
|
|
480
|
-
diagnostics: `Unsupported Media Type: ${contentType}`
|
|
481
|
-
}]
|
|
482
|
-
});
|
|
466
|
+
return res.status(415).json(buildOperationOutcome('error', 'invalid', `Unsupported Media Type: ${contentType}`));
|
|
483
467
|
}
|
|
484
468
|
|
|
485
469
|
if (req.body) {
|
|
@@ -491,7 +475,7 @@ class TXModule {
|
|
|
491
475
|
app.use(express.urlencoded({ extended: true }));
|
|
492
476
|
|
|
493
477
|
// Set up routes
|
|
494
|
-
this.setupRoutes(router);
|
|
478
|
+
this.setupRoutes(router, endpointInfo.path);
|
|
495
479
|
|
|
496
480
|
// Redirect /r5 → /r5/
|
|
497
481
|
app.use((req, res, next) => {
|
|
@@ -512,8 +496,11 @@ class TXModule {
|
|
|
512
496
|
/**
|
|
513
497
|
* Set up routes for an endpoint
|
|
514
498
|
* @param {express.Router} router - Express router
|
|
499
|
+
* @param {string} endpointPath - the endpoint these routes belong to, e.g.
|
|
500
|
+
* '/tx/r4'. Captured by the handlers below so that every request is
|
|
501
|
+
* counted against the endpoint that served it, not just the operation.
|
|
515
502
|
*/
|
|
516
|
-
setupRoutes(router) {
|
|
503
|
+
setupRoutes(router, endpointPath) {
|
|
517
504
|
const resourceTypes = ['CodeSystem', 'ValueSet', 'ConceptMap'];
|
|
518
505
|
|
|
519
506
|
// ===== Operations =====
|
|
@@ -526,7 +513,7 @@ class TXModule {
|
|
|
526
513
|
let worker = new LookupWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
527
514
|
await worker.handle(req, res);
|
|
528
515
|
} finally {
|
|
529
|
-
this.countRequest('$lookup', Date.now() - start);
|
|
516
|
+
this.countRequest(endpointPath, '$lookup', Date.now() - start);
|
|
530
517
|
}
|
|
531
518
|
});
|
|
532
519
|
router.post('/CodeSystem/\\$lookup', async (req, res) => {
|
|
@@ -535,7 +522,7 @@ class TXModule {
|
|
|
535
522
|
let worker = new LookupWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
536
523
|
await worker.handle(req, res);
|
|
537
524
|
} finally {
|
|
538
|
-
this.countRequest('$lookup', Date.now() - start);
|
|
525
|
+
this.countRequest(endpointPath, '$lookup', Date.now() - start);
|
|
539
526
|
}
|
|
540
527
|
});
|
|
541
528
|
|
|
@@ -546,7 +533,7 @@ class TXModule {
|
|
|
546
533
|
let worker = new SubsumesWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
547
534
|
await worker.handle(req, res);
|
|
548
535
|
} finally {
|
|
549
|
-
this.countRequest('$subsumes', Date.now() - start);
|
|
536
|
+
this.countRequest(endpointPath, '$subsumes', Date.now() - start);
|
|
550
537
|
}
|
|
551
538
|
});
|
|
552
539
|
router.post('/CodeSystem/\\$subsumes', async (req, res) => {
|
|
@@ -555,7 +542,7 @@ class TXModule {
|
|
|
555
542
|
let worker = new SubsumesWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
556
543
|
await worker.handle(req, res);
|
|
557
544
|
} finally {
|
|
558
|
-
this.countRequest('$subsumes', Date.now() - start);
|
|
545
|
+
this.countRequest(endpointPath, '$subsumes', Date.now() - start);
|
|
559
546
|
}
|
|
560
547
|
});
|
|
561
548
|
|
|
@@ -566,7 +553,7 @@ class TXModule {
|
|
|
566
553
|
let worker = new ValidateWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
567
554
|
await worker.handleCodeSystem(req, res);
|
|
568
555
|
} finally {
|
|
569
|
-
this.countRequest('$validate', Date.now() - start);
|
|
556
|
+
this.countRequest(endpointPath, '$validate', Date.now() - start);
|
|
570
557
|
}
|
|
571
558
|
});
|
|
572
559
|
router.post('/CodeSystem/\\$validate-code', async (req, res) => {
|
|
@@ -575,7 +562,7 @@ class TXModule {
|
|
|
575
562
|
let worker = new ValidateWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
576
563
|
await worker.handleCodeSystem(req, res);
|
|
577
564
|
} finally {
|
|
578
|
-
this.countRequest('$validate', Date.now() - start);
|
|
565
|
+
this.countRequest(endpointPath, '$validate', Date.now() - start);
|
|
579
566
|
}
|
|
580
567
|
});
|
|
581
568
|
|
|
@@ -586,7 +573,7 @@ class TXModule {
|
|
|
586
573
|
let worker = new BatchValidateWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
587
574
|
await worker.handleCodeSystem(req, res);
|
|
588
575
|
} finally {
|
|
589
|
-
this.countRequest('$batch', Date.now() - start);
|
|
576
|
+
this.countRequest(endpointPath, '$batch', Date.now() - start);
|
|
590
577
|
}
|
|
591
578
|
});
|
|
592
579
|
router.post('/CodeSystem/\\$batch-validate-code', async (req, res) => {
|
|
@@ -595,7 +582,7 @@ class TXModule {
|
|
|
595
582
|
let worker = new BatchValidateWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
596
583
|
await worker.handleCodeSystem(req, res);
|
|
597
584
|
} finally {
|
|
598
|
-
this.countRequest('$batch', Date.now() - start);
|
|
585
|
+
this.countRequest(endpointPath, '$batch', Date.now() - start);
|
|
599
586
|
}
|
|
600
587
|
});
|
|
601
588
|
// ValueSet/$validate-code (GET and POST)
|
|
@@ -605,7 +592,7 @@ class TXModule {
|
|
|
605
592
|
let worker = new ValidateWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
606
593
|
await worker.handleValueSet(req, res);
|
|
607
594
|
} finally {
|
|
608
|
-
this.countRequest('$validate', Date.now() - start);
|
|
595
|
+
this.countRequest(endpointPath, '$validate', Date.now() - start);
|
|
609
596
|
}
|
|
610
597
|
});
|
|
611
598
|
router.post('/ValueSet/\\$validate-code', async (req, res) => {
|
|
@@ -614,7 +601,7 @@ class TXModule {
|
|
|
614
601
|
let worker = new ValidateWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
615
602
|
await worker.handleValueSet(req, res);
|
|
616
603
|
} finally {
|
|
617
|
-
this.countRequest('$validate', Date.now() - start);
|
|
604
|
+
this.countRequest(endpointPath, '$validate', Date.now() - start);
|
|
618
605
|
}
|
|
619
606
|
});
|
|
620
607
|
|
|
@@ -625,7 +612,7 @@ class TXModule {
|
|
|
625
612
|
let worker = new CompareWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
626
613
|
await worker.handle(req, res);
|
|
627
614
|
} finally {
|
|
628
|
-
this.countRequest('$compare', Date.now() - start);
|
|
615
|
+
this.countRequest(endpointPath, '$compare', Date.now() - start);
|
|
629
616
|
}
|
|
630
617
|
});
|
|
631
618
|
router.post('/ValueSet/\\$compare', async (req, res) => {
|
|
@@ -634,7 +621,7 @@ class TXModule {
|
|
|
634
621
|
let worker = new CompareWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
635
622
|
await worker.handle(req, res);
|
|
636
623
|
} finally {
|
|
637
|
-
this.countRequest('$compare', Date.now() - start);
|
|
624
|
+
this.countRequest(endpointPath, '$compare', Date.now() - start);
|
|
638
625
|
}
|
|
639
626
|
});
|
|
640
627
|
|
|
@@ -645,7 +632,7 @@ class TXModule {
|
|
|
645
632
|
let worker = new BatchValidateWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
646
633
|
await worker.handleValueSet(req, res);
|
|
647
634
|
} finally {
|
|
648
|
-
this.countRequest('$batch', Date.now() - start);
|
|
635
|
+
this.countRequest(endpointPath, '$batch', Date.now() - start);
|
|
649
636
|
}
|
|
650
637
|
});
|
|
651
638
|
router.post('/ValueSet/\\$batch-validate-code', async (req, res) => {
|
|
@@ -654,7 +641,7 @@ class TXModule {
|
|
|
654
641
|
let worker = new BatchValidateWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
655
642
|
await worker.handleValueSet(req, res);
|
|
656
643
|
} finally {
|
|
657
|
-
this.countRequest('validate', Date.now() - start);
|
|
644
|
+
this.countRequest(endpointPath, 'validate', Date.now() - start);
|
|
658
645
|
}
|
|
659
646
|
});
|
|
660
647
|
|
|
@@ -665,7 +652,7 @@ class TXModule {
|
|
|
665
652
|
let worker = new ExpandWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n, this.internalLimit(req), this.externalLimit(req));
|
|
666
653
|
await worker.handle(req, res, this.log);
|
|
667
654
|
} finally {
|
|
668
|
-
this.countRequest('$expand', Date.now() - start);
|
|
655
|
+
this.countRequest(endpointPath, '$expand', Date.now() - start);
|
|
669
656
|
}
|
|
670
657
|
});
|
|
671
658
|
router.post('/ValueSet/\\$expand', async (req, res) => {
|
|
@@ -674,7 +661,7 @@ class TXModule {
|
|
|
674
661
|
let worker = new ExpandWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n, this.internalLimit(req), this.externalLimit(req));
|
|
675
662
|
await worker.handle(req, res, this.log);
|
|
676
663
|
} finally {
|
|
677
|
-
this.countRequest('$expand', Date.now() - start);
|
|
664
|
+
this.countRequest(endpointPath, '$expand', Date.now() - start);
|
|
678
665
|
}
|
|
679
666
|
});
|
|
680
667
|
|
|
@@ -685,7 +672,7 @@ class TXModule {
|
|
|
685
672
|
let worker = new CacheControlWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
686
673
|
await worker.handle(req, res, this.log);
|
|
687
674
|
} finally {
|
|
688
|
-
this.countRequest('$cache-control', Date.now() - start);
|
|
675
|
+
this.countRequest(endpointPath, '$cache-control', Date.now() - start);
|
|
689
676
|
}
|
|
690
677
|
});
|
|
691
678
|
router.post('/\\$cache-control', async (req, res) => {
|
|
@@ -694,7 +681,7 @@ class TXModule {
|
|
|
694
681
|
let worker = new CacheControlWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
695
682
|
await worker.handle(req, res, this.log);
|
|
696
683
|
} finally {
|
|
697
|
-
this.countRequest('$cache-control', Date.now() - start);
|
|
684
|
+
this.countRequest(endpointPath, '$cache-control', Date.now() - start);
|
|
698
685
|
}
|
|
699
686
|
});
|
|
700
687
|
|
|
@@ -705,7 +692,7 @@ class TXModule {
|
|
|
705
692
|
let worker = new TranslateWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
706
693
|
await worker.handle(req, res, this.log);
|
|
707
694
|
} finally {
|
|
708
|
-
this.countRequest('$translate', Date.now() - start);
|
|
695
|
+
this.countRequest(endpointPath, '$translate', Date.now() - start);
|
|
709
696
|
}
|
|
710
697
|
});
|
|
711
698
|
router.post('/ConceptMap/\\$translate', async (req, res) => {
|
|
@@ -714,7 +701,7 @@ class TXModule {
|
|
|
714
701
|
let worker = new TranslateWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
715
702
|
await worker.handle(req, res, this.log);
|
|
716
703
|
} finally {
|
|
717
|
-
this.countRequest('$translate', Date.now() - start);
|
|
704
|
+
this.countRequest(endpointPath, '$translate', Date.now() - start);
|
|
718
705
|
}
|
|
719
706
|
});
|
|
720
707
|
|
|
@@ -725,7 +712,7 @@ class TXModule {
|
|
|
725
712
|
let worker = new ClosureWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
726
713
|
await worker.handle(req, res, this.log);
|
|
727
714
|
} finally {
|
|
728
|
-
this.countRequest('$closure', Date.now() - start);
|
|
715
|
+
this.countRequest(endpointPath, '$closure', Date.now() - start);
|
|
729
716
|
}
|
|
730
717
|
});
|
|
731
718
|
router.post('/ConceptMap/\\$closure', async (req, res) => {
|
|
@@ -734,7 +721,7 @@ class TXModule {
|
|
|
734
721
|
let worker = new ClosureWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
735
722
|
await worker.handle(req, res, this.log);
|
|
736
723
|
} finally {
|
|
737
|
-
this.countRequest('$closure', Date.now() - start);
|
|
724
|
+
this.countRequest(endpointPath, '$closure', Date.now() - start);
|
|
738
725
|
}
|
|
739
726
|
});
|
|
740
727
|
|
|
@@ -747,7 +734,7 @@ class TXModule {
|
|
|
747
734
|
let worker = new LookupWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
748
735
|
await worker.handleInstance(req, res);
|
|
749
736
|
} finally {
|
|
750
|
-
this.countRequest('$lookup', Date.now() - start);
|
|
737
|
+
this.countRequest(endpointPath, '$lookup', Date.now() - start);
|
|
751
738
|
}
|
|
752
739
|
});
|
|
753
740
|
router.post('/CodeSystem/:id/\\$lookup', async (req, res) => {
|
|
@@ -756,7 +743,7 @@ class TXModule {
|
|
|
756
743
|
let worker = new LookupWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
757
744
|
await worker.handleInstance(req, res);
|
|
758
745
|
} finally {
|
|
759
|
-
this.countRequest('$lookup', Date.now() - start);
|
|
746
|
+
this.countRequest(endpointPath, '$lookup', Date.now() - start);
|
|
760
747
|
}
|
|
761
748
|
});
|
|
762
749
|
|
|
@@ -767,7 +754,7 @@ class TXModule {
|
|
|
767
754
|
let worker = new SubsumesWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
768
755
|
await worker.handleInstance(req, res);
|
|
769
756
|
} finally {
|
|
770
|
-
this.countRequest('$subsumes', Date.now() - start);
|
|
757
|
+
this.countRequest(endpointPath, '$subsumes', Date.now() - start);
|
|
771
758
|
}
|
|
772
759
|
});
|
|
773
760
|
router.post('/CodeSystem/:id/\\$subsumes', async (req, res) => {
|
|
@@ -776,7 +763,7 @@ class TXModule {
|
|
|
776
763
|
let worker = new SubsumesWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
777
764
|
await worker.handleInstance(req, res);
|
|
778
765
|
} finally {
|
|
779
|
-
this.countRequest('$subsumes', Date.now() - start);
|
|
766
|
+
this.countRequest(endpointPath, '$subsumes', Date.now() - start);
|
|
780
767
|
}
|
|
781
768
|
});
|
|
782
769
|
|
|
@@ -787,7 +774,7 @@ class TXModule {
|
|
|
787
774
|
let worker = new ValidateWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
788
775
|
await worker.handleCodeSystemInstance(req, res, this.log);
|
|
789
776
|
} finally {
|
|
790
|
-
this.countRequest('$validate', Date.now() - start);
|
|
777
|
+
this.countRequest(endpointPath, '$validate', Date.now() - start);
|
|
791
778
|
}
|
|
792
779
|
});
|
|
793
780
|
router.post('/CodeSystem/:id/\\$validate-code', async (req, res) => {
|
|
@@ -796,7 +783,7 @@ class TXModule {
|
|
|
796
783
|
let worker = new ValidateWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
797
784
|
await worker.handleCodeSystemInstance(req, res, this.log);
|
|
798
785
|
} finally {
|
|
799
|
-
this.countRequest('$validate', Date.now() - start);
|
|
786
|
+
this.countRequest(endpointPath, '$validate', Date.now() - start);
|
|
800
787
|
}
|
|
801
788
|
|
|
802
789
|
});
|
|
@@ -808,7 +795,7 @@ class TXModule {
|
|
|
808
795
|
let worker = new ValidateWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
809
796
|
await worker.handleValueSetInstance(req, res, this.log);
|
|
810
797
|
} finally {
|
|
811
|
-
this.countRequest('$validate', Date.now() - start);
|
|
798
|
+
this.countRequest(endpointPath, '$validate', Date.now() - start);
|
|
812
799
|
}
|
|
813
800
|
});
|
|
814
801
|
router.post('/ValueSet/:id/\\$validate-code', async (req, res) => {
|
|
@@ -817,7 +804,7 @@ class TXModule {
|
|
|
817
804
|
let worker = new ValidateWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
818
805
|
await worker.handleValueSetInstance(req, res, this.log);
|
|
819
806
|
} finally {
|
|
820
|
-
this.countRequest('$validate', Date.now() - start);
|
|
807
|
+
this.countRequest(endpointPath, '$validate', Date.now() - start);
|
|
821
808
|
}
|
|
822
809
|
});
|
|
823
810
|
|
|
@@ -829,7 +816,7 @@ class TXModule {
|
|
|
829
816
|
let worker = new CompareWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
830
817
|
await worker.handleInstance(req, res, this.log);
|
|
831
818
|
} finally {
|
|
832
|
-
this.countRequest('$compare', Date.now() - start);
|
|
819
|
+
this.countRequest(endpointPath, '$compare', Date.now() - start);
|
|
833
820
|
}
|
|
834
821
|
});
|
|
835
822
|
router.post('/ValueSet/:id/\\$compare', async (req, res) => {
|
|
@@ -838,7 +825,7 @@ class TXModule {
|
|
|
838
825
|
let worker = new CompareWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
839
826
|
await worker.handleInstance(req, res, this.log);
|
|
840
827
|
} finally {
|
|
841
|
-
this.countRequest('$compare', Date.now() - start);
|
|
828
|
+
this.countRequest(endpointPath, '$compare', Date.now() - start);
|
|
842
829
|
}
|
|
843
830
|
});
|
|
844
831
|
|
|
@@ -849,7 +836,7 @@ class TXModule {
|
|
|
849
836
|
let worker = new ExpandWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n, this.internalLimit(req), this.externalLimit(req));
|
|
850
837
|
await worker.handleInstance(req, res, this.log);
|
|
851
838
|
} finally {
|
|
852
|
-
this.countRequest('$expand', Date.now() - start);
|
|
839
|
+
this.countRequest(endpointPath, '$expand', Date.now() - start);
|
|
853
840
|
}
|
|
854
841
|
});
|
|
855
842
|
router.post('/ValueSet/:id/\\$expand', async (req, res) => {
|
|
@@ -858,7 +845,7 @@ class TXModule {
|
|
|
858
845
|
let worker = new ExpandWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n, this.internalLimit(req), this.externalLimit(req));
|
|
859
846
|
await worker.handleInstance(req, res, this.log);
|
|
860
847
|
} finally {
|
|
861
|
-
this.countRequest('$expand', Date.now() - start);
|
|
848
|
+
this.countRequest(endpointPath, '$expand', Date.now() - start);
|
|
862
849
|
}
|
|
863
850
|
});
|
|
864
851
|
|
|
@@ -869,7 +856,7 @@ class TXModule {
|
|
|
869
856
|
let worker = new TranslateWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
870
857
|
await worker.handleInstance(req, res, this.log);
|
|
871
858
|
} finally {
|
|
872
|
-
this.countRequest('$translate', Date.now() - start);
|
|
859
|
+
this.countRequest(endpointPath, '$translate', Date.now() - start);
|
|
873
860
|
}
|
|
874
861
|
});
|
|
875
862
|
router.post('/ConceptMap/:id/\\$translate', async (req, res) => {
|
|
@@ -878,7 +865,7 @@ class TXModule {
|
|
|
878
865
|
let worker = new TranslateWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
879
866
|
await worker.handleInstance(req, res, this.log);
|
|
880
867
|
} finally {
|
|
881
|
-
this.countRequest('$translate', Date.now() - start);
|
|
868
|
+
this.countRequest(endpointPath, '$translate', Date.now() - start);
|
|
882
869
|
}
|
|
883
870
|
});
|
|
884
871
|
|
|
@@ -900,7 +887,7 @@ class TXModule {
|
|
|
900
887
|
let worker = new ReadWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
901
888
|
await worker.handle(req, res, resourceType);
|
|
902
889
|
} finally {
|
|
903
|
-
this.countRequest('read', Date.now() - start);
|
|
890
|
+
this.countRequest(endpointPath, 'read', Date.now() - start);
|
|
904
891
|
}
|
|
905
892
|
});
|
|
906
893
|
}
|
|
@@ -913,7 +900,7 @@ class TXModule {
|
|
|
913
900
|
let worker = new SearchWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
914
901
|
await worker.handle(req, res, resourceType);
|
|
915
902
|
} finally {
|
|
916
|
-
this.countRequest('search', Date.now() - start);
|
|
903
|
+
this.countRequest(endpointPath, 'search', Date.now() - start);
|
|
917
904
|
}
|
|
918
905
|
});
|
|
919
906
|
router.post(`/${resourceType}/_search`, async (req, res) => {
|
|
@@ -922,7 +909,7 @@ class TXModule {
|
|
|
922
909
|
let worker = new SearchWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
923
910
|
await worker.handle(req, res, resourceType);
|
|
924
911
|
} finally {
|
|
925
|
-
this.countRequest('search', Date.now() - start);
|
|
912
|
+
this.countRequest(endpointPath, 'search', Date.now() - start);
|
|
926
913
|
}
|
|
927
914
|
});
|
|
928
915
|
}
|
|
@@ -940,7 +927,7 @@ class TXModule {
|
|
|
940
927
|
));
|
|
941
928
|
}
|
|
942
929
|
} finally {
|
|
943
|
-
this.countRequest('$read', Date.now() - start);
|
|
930
|
+
this.countRequest(endpointPath, '$read', Date.now() - start);
|
|
944
931
|
}
|
|
945
932
|
});
|
|
946
933
|
}
|
|
@@ -951,7 +938,7 @@ class TXModule {
|
|
|
951
938
|
let worker = new OperationsWorker(req.txOpContext, this.log, req.txProvider, this.languages, this.i18n);
|
|
952
939
|
await worker.handle(req, res);
|
|
953
940
|
} finally {
|
|
954
|
-
this.countRequest('$op', Date.now() - start);
|
|
941
|
+
this.countRequest(endpointPath, '$op', Date.now() - start);
|
|
955
942
|
}
|
|
956
943
|
});
|
|
957
944
|
|
|
@@ -965,7 +952,7 @@ class TXModule {
|
|
|
965
952
|
res.setHeader('Content-Type', 'text/html');
|
|
966
953
|
res.send(html);
|
|
967
954
|
} finally {
|
|
968
|
-
this.countRequest('problems', Date.now() - start);
|
|
955
|
+
this.countRequest(endpointPath, 'problems', Date.now() - start);
|
|
969
956
|
}
|
|
970
957
|
});
|
|
971
958
|
|
|
@@ -980,7 +967,7 @@ class TXModule {
|
|
|
980
967
|
res.status(500).json(this.operationOutcome('error', 'exception', error.message));
|
|
981
968
|
}
|
|
982
969
|
} finally {
|
|
983
|
-
this.countRequest('metadata', Date.now() - start);
|
|
970
|
+
this.countRequest(endpointPath, 'metadata', Date.now() - start);
|
|
984
971
|
}
|
|
985
972
|
});
|
|
986
973
|
|
|
@@ -995,7 +982,7 @@ class TXModule {
|
|
|
995
982
|
res.status(500).json(this.operationOutcome('error', 'exception', error.message));
|
|
996
983
|
}
|
|
997
984
|
} finally {
|
|
998
|
-
this.countRequest('$versions', Date.now() - start);
|
|
985
|
+
this.countRequest(endpointPath, '$versions', Date.now() - start);
|
|
999
986
|
}
|
|
1000
987
|
});
|
|
1001
988
|
|
|
@@ -1003,16 +990,9 @@ class TXModule {
|
|
|
1003
990
|
router.get('/', async (req, res) => {
|
|
1004
991
|
const start = Date.now();
|
|
1005
992
|
try {
|
|
1006
|
-
await res.json({
|
|
1007
|
-
resourceType: 'OperationOutcome',
|
|
1008
|
-
issue: [{
|
|
1009
|
-
severity: 'information',
|
|
1010
|
-
code: 'informational',
|
|
1011
|
-
diagnostics: `FHIR Terminology Server - FHIR v${req.txEndpoint.fhirVersion}`
|
|
1012
|
-
}]
|
|
1013
|
-
});
|
|
993
|
+
await res.json(buildOperationOutcome('information', 'informational', `FHIR Terminology Server - FHIR v${req.txEndpoint.fhirVersion}`));
|
|
1014
994
|
} finally {
|
|
1015
|
-
this.countRequest('home', Date.now() - start);
|
|
995
|
+
this.countRequest(endpointPath, 'home', Date.now() - start);
|
|
1016
996
|
}
|
|
1017
997
|
});
|
|
1018
998
|
|
|
@@ -1037,7 +1017,7 @@ class TXModule {
|
|
|
1037
1017
|
this.log.error(`Error rendering info page for ${req.params.id}: ${error.message}`);
|
|
1038
1018
|
res.status(500).send('Internal server error');
|
|
1039
1019
|
} finally {
|
|
1040
|
-
this.countRequest('info', Date.now() - start);
|
|
1020
|
+
this.countRequest(endpointPath, 'info', Date.now() - start);
|
|
1041
1021
|
}
|
|
1042
1022
|
};
|
|
1043
1023
|
router.get('/info/:id', infoHandler);
|
|
@@ -1118,17 +1098,17 @@ class TXModule {
|
|
|
1118
1098
|
}
|
|
1119
1099
|
|
|
1120
1100
|
/**
|
|
1121
|
-
* Build an OperationOutcome
|
|
1101
|
+
* Build an OperationOutcome. Delegates to the shared builder so that every outcome this
|
|
1102
|
+
* server emits has details.text and a tx-issue-type coding: diagnostics is stripped by
|
|
1103
|
+
* the test harness, so nothing a caller needs may live there.
|
|
1104
|
+
* @param {string} severity - error, warning, information
|
|
1105
|
+
* @param {string} code - FHIR issue type
|
|
1106
|
+
* @param {string} message - the human readable account of the problem
|
|
1107
|
+
* @param {string} [txIssueType] - tx-issue-type; defaulted from code when not given
|
|
1108
|
+
* @returns {Object} OperationOutcome resource
|
|
1122
1109
|
*/
|
|
1123
|
-
operationOutcome(severity, code, message) {
|
|
1124
|
-
return
|
|
1125
|
-
resourceType: 'OperationOutcome',
|
|
1126
|
-
issue: [{
|
|
1127
|
-
severity,
|
|
1128
|
-
code,
|
|
1129
|
-
diagnostics: message
|
|
1130
|
-
}]
|
|
1131
|
-
};
|
|
1110
|
+
operationOutcome(severity, code, message, txIssueType = null) {
|
|
1111
|
+
return buildOperationOutcome(severity, code, message, txIssueType);
|
|
1132
1112
|
}
|
|
1133
1113
|
|
|
1134
1114
|
/**
|
|
@@ -1214,9 +1194,9 @@ class TXModule {
|
|
|
1214
1194
|
return data;
|
|
1215
1195
|
}
|
|
1216
1196
|
|
|
1217
|
-
countRequest(name, tat) {
|
|
1197
|
+
countRequest(endpoint, name, tat) {
|
|
1218
1198
|
if (this.stats) {
|
|
1219
|
-
this.stats.countRequest(name, tat);
|
|
1199
|
+
this.stats.countRequest(name, tat, endpoint);
|
|
1220
1200
|
}
|
|
1221
1201
|
}
|
|
1222
1202
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
//
|
|
14
14
|
|
|
15
15
|
const { TerminologyWorker } = require('./worker');
|
|
16
|
-
const {OperationOutcome, Issue} = require(
|
|
16
|
+
const { OperationOutcome, Issue, buildOperationOutcome, outcomeFromError } = require('../library/operation-outcome');
|
|
17
17
|
const {Parameters} = require("../library/parameters");
|
|
18
18
|
const {ValidateWorker} = require("./validate");
|
|
19
19
|
const {debugLog} = require("../operation-context");
|
|
@@ -137,7 +137,7 @@ class BatchValidateWorker extends TerminologyWorker {
|
|
|
137
137
|
op.addIssue(error);
|
|
138
138
|
output.push({name: "validation", resource : op.jsonObj});
|
|
139
139
|
} else {
|
|
140
|
-
output.push({name: "validation", resource :
|
|
140
|
+
output.push({name: "validation", resource : outcomeFromError(error) } );
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
}
|
|
@@ -156,8 +156,7 @@ class BatchValidateWorker extends TerminologyWorker {
|
|
|
156
156
|
oo.addIssue(error);
|
|
157
157
|
return res.status(error.statusCode || 500).json(oo.jsonObj);
|
|
158
158
|
}
|
|
159
|
-
return res.status(error.statusCode || 500).json(
|
|
160
|
-
'error', error.issueCode || 'exception', error.message));
|
|
159
|
+
return res.status(error.statusCode || 500).json(outcomeFromError(error));
|
|
161
160
|
}
|
|
162
161
|
}
|
|
163
162
|
|
|
@@ -187,8 +186,11 @@ class BatchValidateWorker extends TerminologyWorker {
|
|
|
187
186
|
return false;
|
|
188
187
|
}
|
|
189
188
|
if (!cache.has(cacheId)) {
|
|
190
|
-
|
|
191
|
-
|
|
189
|
+
// describeMissing names which fate the id met (never issued here / closed by
|
|
190
|
+
// the client / idle-expired); the coding stays cache-id-unknown either way.
|
|
191
|
+
const { messageId, params: msgParams } = cache.describeMissing(cacheId);
|
|
192
|
+
throw new Issue('error', 'not-found', null, messageId,
|
|
193
|
+
this.i18n.translate(messageId, this.opContext.langs, msgParams),
|
|
192
194
|
'cache-id-unknown', 404);
|
|
193
195
|
}
|
|
194
196
|
if (cache.isSealed(cacheId)) {
|
|
@@ -215,18 +217,9 @@ class BatchValidateWorker extends TerminologyWorker {
|
|
|
215
217
|
/**
|
|
216
218
|
* Build an OperationOutcome
|
|
217
219
|
*/
|
|
218
|
-
operationOutcome(severity, code, message) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
issue: [{
|
|
222
|
-
severity,
|
|
223
|
-
code,
|
|
224
|
-
details: {
|
|
225
|
-
text: message
|
|
226
|
-
},
|
|
227
|
-
diagnostics: message
|
|
228
|
-
}]
|
|
229
|
-
};
|
|
220
|
+
operationOutcome(severity, code, message, txIssueType = null) {
|
|
221
|
+
// the shared builder, so that every outcome has details.text and a tx-issue-type coding
|
|
222
|
+
return buildOperationOutcome(severity, code, message, txIssueType);
|
|
230
223
|
}
|
|
231
224
|
|
|
232
225
|
hasValueSet(parameter) {
|