@onivoro/isomorphic-fhir 24.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +189 -0
- package/package.json +23 -0
- package/src/index.d.ts +3 -0
- package/src/index.js +6 -0
- package/src/index.js.map +1 -0
- package/src/lib/functions/fhir-test-config.d.ts +127 -0
- package/src/lib/functions/fhir-test-config.js +276 -0
- package/src/lib/functions/fhir-test-config.js.map +1 -0
- package/src/lib/functions/normalize-fhir-bundle.function.d.ts +3 -0
- package/src/lib/functions/normalize-fhir-bundle.function.js +1487 -0
- package/src/lib/functions/normalize-fhir-bundle.function.js.map +1 -0
- package/src/lib/functions/test-setup.d.ts +13 -0
- package/src/lib/functions/test-setup.js +81 -0
- package/src/lib/functions/test-setup.js.map +1 -0
- package/src/lib/interfaces/fhir-normalized-tables.interface.d.ts +594 -0
- package/src/lib/interfaces/fhir-normalized-tables.interface.js +3 -0
- package/src/lib/interfaces/fhir-normalized-tables.interface.js.map +1 -0
- package/src/lib/interfaces/fhir-resources.interface.d.ts +715 -0
- package/src/lib/interfaces/fhir-resources.interface.js +4 -0
- package/src/lib/interfaces/fhir-resources.interface.js.map +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Onivoro
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# @onivoro/isomorphic-fhir
|
|
2
|
+
|
|
3
|
+
A library for transforming FHIR (Fast Healthcare Interoperability Resources) bundles into normalized database tables following Third Normal Form (3NF).
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This library takes FHIR R4 bundles containing healthcare resources and transforms them into a set of normalized tables that can be used to populate a relational database. The transformation process:
|
|
8
|
+
|
|
9
|
+
- Eliminates data redundancy across FHIR resources
|
|
10
|
+
- Ensures referential integrity between healthcare entities
|
|
11
|
+
- Creates proper foreign key relationships
|
|
12
|
+
- Generates unique IDs for normalized records
|
|
13
|
+
- Preserves FHIR resource relationships through reference tables
|
|
14
|
+
- Handles complex nested structures like CodeableConcepts and Extensions
|
|
15
|
+
|
|
16
|
+
## Supported FHIR Resources
|
|
17
|
+
|
|
18
|
+
The library currently supports normalization of the following FHIR R4 resources:
|
|
19
|
+
|
|
20
|
+
- **Patient**: Demographics, identifiers, names, contacts, addresses
|
|
21
|
+
- **Practitioner**: Healthcare providers, qualifications, contact information
|
|
22
|
+
- **Organization**: Healthcare organizations, types, contact details
|
|
23
|
+
- **Encounter**: Healthcare encounters, participants, locations
|
|
24
|
+
- **Condition**: Medical conditions, clinical status, evidence
|
|
25
|
+
- **Observation**: Clinical measurements, vital signs, lab results
|
|
26
|
+
- **MedicationRequest**: Medication prescriptions, dosage instructions
|
|
27
|
+
- **Procedure**: Medical procedures, performers, outcomes
|
|
28
|
+
- **Immunization**: Vaccinations, reactions, protocols
|
|
29
|
+
- **AllergyIntolerance**: Allergies, reactions, manifestations
|
|
30
|
+
- **DiagnosticReport**: Diagnostic test results, conclusions
|
|
31
|
+
- **Location**: Healthcare facilities, positions, contact info
|
|
32
|
+
- **Appointment**: Scheduled appointments, participants
|
|
33
|
+
- **CareTeam**: Care team members, roles, periods
|
|
34
|
+
- **CarePlan**: Care plans, activities, goals
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
import { normalizeFhirBundle } from '@onivoro/isomorphic-fhir';
|
|
40
|
+
|
|
41
|
+
const fhirBundle = {
|
|
42
|
+
resourceType: 'Bundle',
|
|
43
|
+
type: 'searchset',
|
|
44
|
+
entry: [
|
|
45
|
+
{
|
|
46
|
+
resource: {
|
|
47
|
+
resourceType: 'Patient',
|
|
48
|
+
id: 'patient-123',
|
|
49
|
+
name: [{ family: 'Doe', given: ['John'] }],
|
|
50
|
+
gender: 'male',
|
|
51
|
+
birthDate: '1985-03-15'
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
resource: {
|
|
56
|
+
resourceType: 'Observation',
|
|
57
|
+
id: 'observation-456',
|
|
58
|
+
status: 'final',
|
|
59
|
+
code: { coding: [{ code: '8867-4', display: 'Heart rate' }] },
|
|
60
|
+
subject: { reference: 'Patient/patient-123' },
|
|
61
|
+
valueQuantity: { value: 72, unit: 'beats/min' }
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const normalizedData = normalizeFhirBundle(fhirBundle);
|
|
68
|
+
|
|
69
|
+
// normalizedData contains normalized tables:
|
|
70
|
+
console.log(normalizedData.patients); // Patient demographics
|
|
71
|
+
console.log(normalizedData.patientNames); // Patient names (separate table)
|
|
72
|
+
console.log(normalizedData.observations); // Clinical observations
|
|
73
|
+
console.log(normalizedData.addresses); // Addresses (deduplicated)
|
|
74
|
+
console.log(normalizedData.telecoms); // Contact points
|
|
75
|
+
console.log(normalizedData.references); // Resource relationships
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Normalized Tables Generated
|
|
79
|
+
|
|
80
|
+
The library generates 30+ normalized tables following healthcare data patterns:
|
|
81
|
+
|
|
82
|
+
### Core Resource Tables
|
|
83
|
+
- **bundles**: FHIR bundle metadata
|
|
84
|
+
- **bundleEntries**: Bundle entry information
|
|
85
|
+
- **patients**: Patient demographics
|
|
86
|
+
- **practitioners**: Healthcare provider information
|
|
87
|
+
- **organizations**: Healthcare organizations
|
|
88
|
+
- **encounters**: Healthcare encounters
|
|
89
|
+
- **conditions**: Medical conditions and diagnoses
|
|
90
|
+
- **observations**: Clinical measurements and findings
|
|
91
|
+
- **medicationRequests**: Medication prescriptions
|
|
92
|
+
- **procedures**: Medical procedures performed
|
|
93
|
+
- **immunizations**: Vaccination records
|
|
94
|
+
- **allergyIntolerances**: Allergy and intolerance information
|
|
95
|
+
- **diagnosticReports**: Diagnostic test reports
|
|
96
|
+
- **locations**: Healthcare facility information
|
|
97
|
+
- **appointments**: Scheduled appointments
|
|
98
|
+
- **careTeams**: Care team compositions
|
|
99
|
+
- **carePlans**: Patient care plans
|
|
100
|
+
|
|
101
|
+
### Relationship and Detail Tables
|
|
102
|
+
- **patientIdentifiers**: Patient identifier systems
|
|
103
|
+
- **patientNames**: Patient name variations
|
|
104
|
+
- **patientContacts**: Emergency/family contacts
|
|
105
|
+
- **practitionerQualifications**: Provider credentials
|
|
106
|
+
- **organizationTypes**: Organization classifications
|
|
107
|
+
- **encounterParticipants**: Encounter participants
|
|
108
|
+
- **observationComponents**: Multi-component observations
|
|
109
|
+
- **medicationDosages**: Medication dosing instructions
|
|
110
|
+
- **procedurePerformers**: Procedure performers
|
|
111
|
+
- **allergyReactions**: Allergic reactions
|
|
112
|
+
- **allergyManifestations**: Reaction manifestations
|
|
113
|
+
- **diagnosticReportResults**: Report result references
|
|
114
|
+
- **appointmentParticipants**: Appointment participants
|
|
115
|
+
- **careTeamParticipants**: Care team member details
|
|
116
|
+
- **carePlanActivities**: Care plan activity details
|
|
117
|
+
|
|
118
|
+
### Common Data Type Tables
|
|
119
|
+
- **addresses**: Physical addresses (deduplicated)
|
|
120
|
+
- **telecoms**: Contact points (phone, email, etc.)
|
|
121
|
+
- **codeableConcepts**: Coded concepts with text
|
|
122
|
+
- **codings**: Individual coding systems
|
|
123
|
+
- **notes**: Annotations and comments
|
|
124
|
+
- **attachments**: Binary attachments and documents
|
|
125
|
+
- **references**: Resource references and relationships
|
|
126
|
+
|
|
127
|
+
## Key Features
|
|
128
|
+
|
|
129
|
+
### FHIR Compliance
|
|
130
|
+
- Full FHIR R4 resource support
|
|
131
|
+
- Proper handling of FHIR data types (CodeableConcept, Reference, etc.)
|
|
132
|
+
- Preservation of clinical terminology and coding systems
|
|
133
|
+
- Support for complex nested structures
|
|
134
|
+
|
|
135
|
+
### Data Integrity
|
|
136
|
+
- Referential integrity through normalized foreign keys
|
|
137
|
+
- Unique constraint handling for identifiers
|
|
138
|
+
- Proper temporal data handling (effective periods, timestamps)
|
|
139
|
+
- Clinical status and verification status preservation
|
|
140
|
+
|
|
141
|
+
### Healthcare-Specific Patterns
|
|
142
|
+
- Support for multiple identifier systems per resource
|
|
143
|
+
- Proper handling of clinical hierarchies (conditions, observations)
|
|
144
|
+
- Care team and care plan relationship modeling
|
|
145
|
+
- Medication dosing and administration patterns
|
|
146
|
+
- Allergy and adverse reaction tracking
|
|
147
|
+
|
|
148
|
+
### Performance Optimized
|
|
149
|
+
- Efficient deduplication of common entities (addresses, codings)
|
|
150
|
+
- Bulk insert-friendly table structures
|
|
151
|
+
- Indexed foreign key relationships
|
|
152
|
+
- Minimal data redundancy
|
|
153
|
+
|
|
154
|
+
## Testing
|
|
155
|
+
|
|
156
|
+
The library includes comprehensive tests covering:
|
|
157
|
+
- Complete FHIR bundle normalization
|
|
158
|
+
- Individual resource type processing
|
|
159
|
+
- Complex nested structure handling
|
|
160
|
+
- Reference relationship preservation
|
|
161
|
+
- Edge cases and error conditions
|
|
162
|
+
|
|
163
|
+
Run tests with:
|
|
164
|
+
```bash
|
|
165
|
+
nx test lib-isomorphic-fhir
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## FHIR Standards Compliance
|
|
169
|
+
|
|
170
|
+
This library is designed to work with FHIR R4 resources and follows HL7 FHIR standards for:
|
|
171
|
+
- Resource structure and cardinality
|
|
172
|
+
- Data type handling and validation
|
|
173
|
+
- Reference resolution and integrity
|
|
174
|
+
- Clinical terminology bindings
|
|
175
|
+
- Healthcare workflow patterns
|
|
176
|
+
|
|
177
|
+
## Use Cases
|
|
178
|
+
|
|
179
|
+
Perfect for:
|
|
180
|
+
- **EHR Data Warehousing**: Normalize FHIR data for analytics
|
|
181
|
+
- **Clinical Data Lakes**: Store FHIR resources in relational format
|
|
182
|
+
- **Interoperability Solutions**: Transform FHIR for legacy systems
|
|
183
|
+
- **Healthcare Analytics**: Enable SQL-based analysis of clinical data
|
|
184
|
+
- **Compliance Reporting**: Structure data for regulatory requirements
|
|
185
|
+
- **Research Databases**: Prepare clinical data for research queries
|
|
186
|
+
|
|
187
|
+
## License
|
|
188
|
+
|
|
189
|
+
This library is licensed under the MIT License. See the LICENSE file in this package for details.
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@onivoro/isomorphic-fhir",
|
|
3
|
+
"version": "24.28.1",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"types": "./src/index.d.ts",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "https://github.com/onivoro/monorepo.git"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@nestjs/swagger": "*",
|
|
14
|
+
"tslib": "^2.3.0",
|
|
15
|
+
"uuid": "*"
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"**/*.js",
|
|
19
|
+
"**/*.d.ts",
|
|
20
|
+
"**/*.js.map",
|
|
21
|
+
"README.md"
|
|
22
|
+
]
|
|
23
|
+
}
|
package/src/index.d.ts
ADDED
package/src/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// FHIR Resource Interfaces
|
|
2
|
+
export * from './lib/interfaces/fhir-resources.interface';
|
|
3
|
+
export * from './lib/interfaces/fhir-normalized-tables.interface';
|
|
4
|
+
// Functions
|
|
5
|
+
export * from './lib/functions/normalize-fhir-bundle.function';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
package/src/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../libs/isomorphic/fhir/src/index.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,cAAc,2CAA2C,CAAC;AAC1D,cAAc,mDAAmD,CAAC;AAElE,YAAY;AACZ,cAAc,gDAAgD,CAAC"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { AxiosInstance } from 'axios';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration and utilities for FHIR API integration tests
|
|
4
|
+
*/
|
|
5
|
+
export declare class FhirTestConfig {
|
|
6
|
+
static readonly FHIR_BASE_URL = "http://hapi.fhir.org/baseR4";
|
|
7
|
+
static readonly TEST_TIMEOUT = 30000;
|
|
8
|
+
private static testAxiosInstance;
|
|
9
|
+
/**
|
|
10
|
+
* Get or create the test axios instance
|
|
11
|
+
*/
|
|
12
|
+
static getAxiosInstance(): AxiosInstance;
|
|
13
|
+
/**
|
|
14
|
+
* Clean up the axios instance
|
|
15
|
+
*/
|
|
16
|
+
static cleanup(): void;
|
|
17
|
+
/**
|
|
18
|
+
* Common FHIR resource types for testing
|
|
19
|
+
*/
|
|
20
|
+
static readonly RESOURCE_TYPES: {
|
|
21
|
+
readonly PATIENT: "Patient";
|
|
22
|
+
readonly PRACTITIONER: "Practitioner";
|
|
23
|
+
readonly ORGANIZATION: "Organization";
|
|
24
|
+
readonly ENCOUNTER: "Encounter";
|
|
25
|
+
readonly OBSERVATION: "Observation";
|
|
26
|
+
readonly CONDITION: "Condition";
|
|
27
|
+
readonly MEDICATION_REQUEST: "MedicationRequest";
|
|
28
|
+
readonly PROCEDURE: "Procedure";
|
|
29
|
+
readonly LOCATION: "Location";
|
|
30
|
+
readonly APPOINTMENT: "Appointment";
|
|
31
|
+
readonly CARE_TEAM: "CareTeam";
|
|
32
|
+
readonly CARE_PLAN: "CarePlan";
|
|
33
|
+
readonly IMMUNIZATION: "Immunization";
|
|
34
|
+
readonly ALLERGY_INTOLERANCE: "AllergyIntolerance";
|
|
35
|
+
readonly DIAGNOSTIC_REPORT: "DiagnosticReport";
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Common search parameters for testing
|
|
39
|
+
*/
|
|
40
|
+
static readonly SEARCH_PARAMS: {
|
|
41
|
+
readonly COUNT: "_count";
|
|
42
|
+
readonly FORMAT: "_format";
|
|
43
|
+
readonly INCLUDE: "_include";
|
|
44
|
+
readonly SORT: "_sort";
|
|
45
|
+
readonly TEXT: "_text";
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Initialize axios defaults for testing
|
|
49
|
+
*/
|
|
50
|
+
static initializeAxios(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Build a FHIR search URL with parameters
|
|
53
|
+
*/
|
|
54
|
+
static buildSearchUrl(resourceType: string, params?: Record<string, string | number>): string;
|
|
55
|
+
/**
|
|
56
|
+
* Get a resource by ID
|
|
57
|
+
*/
|
|
58
|
+
static buildResourceUrl(resourceType: string, id: string): string;
|
|
59
|
+
/**
|
|
60
|
+
* Common assertion helpers for FHIR resources
|
|
61
|
+
*/
|
|
62
|
+
static validateFhirResource(resource: any, expectedType: string): void;
|
|
63
|
+
/**
|
|
64
|
+
* Validate FHIR Bundle structure
|
|
65
|
+
*/
|
|
66
|
+
static validateFhirBundle(bundle: any): void;
|
|
67
|
+
/**
|
|
68
|
+
* Wait for a condition to be met (useful for async operations)
|
|
69
|
+
*/
|
|
70
|
+
static waitFor(condition: () => boolean | Promise<boolean>, timeout?: number): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Retry a function with exponential backoff
|
|
73
|
+
*/
|
|
74
|
+
static retry<T>(fn: () => Promise<T>, maxAttempts?: number, initialDelay?: number): Promise<T>;
|
|
75
|
+
/**
|
|
76
|
+
* Check if the FHIR server is available
|
|
77
|
+
*/
|
|
78
|
+
static checkServerAvailability(): Promise<boolean>;
|
|
79
|
+
/**
|
|
80
|
+
* Get server capability statement
|
|
81
|
+
*/
|
|
82
|
+
static getServerCapabilities(): Promise<any>;
|
|
83
|
+
/**
|
|
84
|
+
* Clean up test data (if the server supports it)
|
|
85
|
+
*/
|
|
86
|
+
/**
|
|
87
|
+
* Clean up test data (placeholder - HAPI test server doesn't support DELETE)
|
|
88
|
+
*/
|
|
89
|
+
static cleanupTestData(resourceType: string, identifiers: string[]): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Generate test data patterns
|
|
92
|
+
*/
|
|
93
|
+
static getTestPatterns(): {
|
|
94
|
+
searchPatterns: ({
|
|
95
|
+
params: {
|
|
96
|
+
_count: number;
|
|
97
|
+
_sort?: undefined;
|
|
98
|
+
};
|
|
99
|
+
} | {
|
|
100
|
+
params: {
|
|
101
|
+
_count: number;
|
|
102
|
+
_sort: string;
|
|
103
|
+
};
|
|
104
|
+
})[];
|
|
105
|
+
resourceCombinations: (("Patient" | "Encounter")[] | ("Practitioner" | "Organization")[] | ("Patient" | "Observation")[])[];
|
|
106
|
+
errorScenarios: {
|
|
107
|
+
url: string;
|
|
108
|
+
expectedStatus: number;
|
|
109
|
+
}[];
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Test utilities for performance monitoring
|
|
114
|
+
*/
|
|
115
|
+
export declare class FhirTestPerformance {
|
|
116
|
+
private static measurements;
|
|
117
|
+
static startMeasurement(name: string): () => number;
|
|
118
|
+
static recordMeasurement(name: string, duration: number): void;
|
|
119
|
+
static getStatistics(name: string): {
|
|
120
|
+
avg: number;
|
|
121
|
+
min: number;
|
|
122
|
+
max: number;
|
|
123
|
+
count: number;
|
|
124
|
+
} | null;
|
|
125
|
+
static reset(): void;
|
|
126
|
+
static logStatistics(): void;
|
|
127
|
+
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { __awaiter } from "tslib";
|
|
2
|
+
import axios from 'axios';
|
|
3
|
+
import * as http from 'http';
|
|
4
|
+
import * as https from 'https';
|
|
5
|
+
/**
|
|
6
|
+
* Configuration and utilities for FHIR API integration tests
|
|
7
|
+
*/
|
|
8
|
+
export class FhirTestConfig {
|
|
9
|
+
/**
|
|
10
|
+
* Get or create the test axios instance
|
|
11
|
+
*/
|
|
12
|
+
static getAxiosInstance() {
|
|
13
|
+
if (!this.testAxiosInstance) {
|
|
14
|
+
// Create HTTP/HTTPS agents that don't keep connections alive
|
|
15
|
+
const httpAgent = new http.Agent({
|
|
16
|
+
keepAlive: false,
|
|
17
|
+
maxSockets: 5
|
|
18
|
+
});
|
|
19
|
+
const httpsAgent = new https.Agent({
|
|
20
|
+
keepAlive: false,
|
|
21
|
+
maxSockets: 5
|
|
22
|
+
});
|
|
23
|
+
this.testAxiosInstance = axios.create({
|
|
24
|
+
timeout: this.TEST_TIMEOUT,
|
|
25
|
+
httpAgent,
|
|
26
|
+
httpsAgent,
|
|
27
|
+
headers: {
|
|
28
|
+
'Accept': 'application/fhir+json',
|
|
29
|
+
'Connection': 'close'
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return this.testAxiosInstance;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Clean up the axios instance
|
|
37
|
+
*/
|
|
38
|
+
static cleanup() {
|
|
39
|
+
if (this.testAxiosInstance) {
|
|
40
|
+
// Destroy any remaining connections
|
|
41
|
+
if (this.testAxiosInstance.defaults.httpAgent) {
|
|
42
|
+
this.testAxiosInstance.defaults.httpAgent.destroy();
|
|
43
|
+
}
|
|
44
|
+
if (this.testAxiosInstance.defaults.httpsAgent) {
|
|
45
|
+
this.testAxiosInstance.defaults.httpsAgent.destroy();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Initialize axios defaults for testing
|
|
51
|
+
*/
|
|
52
|
+
static initializeAxios() {
|
|
53
|
+
// Just ensure our test instance is created
|
|
54
|
+
this.getAxiosInstance();
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Build a FHIR search URL with parameters
|
|
58
|
+
*/
|
|
59
|
+
static buildSearchUrl(resourceType, params = {}) {
|
|
60
|
+
const url = new URL(`${this.FHIR_BASE_URL}/${resourceType}`);
|
|
61
|
+
// Add default format parameter
|
|
62
|
+
url.searchParams.set(this.SEARCH_PARAMS.FORMAT, 'json');
|
|
63
|
+
// Add custom parameters, filtering out undefined values
|
|
64
|
+
Object.entries(params).forEach(([key, value]) => {
|
|
65
|
+
if (value !== undefined) {
|
|
66
|
+
url.searchParams.set(key, value.toString());
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return url.toString();
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get a resource by ID
|
|
73
|
+
*/
|
|
74
|
+
static buildResourceUrl(resourceType, id) {
|
|
75
|
+
return `${this.FHIR_BASE_URL}/${resourceType}/${id}?_format=json`;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Common assertion helpers for FHIR resources
|
|
79
|
+
*/
|
|
80
|
+
static validateFhirResource(resource, expectedType) {
|
|
81
|
+
expect(resource).toBeDefined();
|
|
82
|
+
expect(resource.resourceType).toBe(expectedType);
|
|
83
|
+
expect(resource.id).toBeDefined();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Validate FHIR Bundle structure
|
|
87
|
+
*/
|
|
88
|
+
static validateFhirBundle(bundle) {
|
|
89
|
+
expect(bundle).toBeDefined();
|
|
90
|
+
expect(bundle.resourceType).toBe('Bundle');
|
|
91
|
+
expect(bundle.type).toBeDefined();
|
|
92
|
+
expect(['searchset', 'collection', 'document', 'message', 'transaction', 'transaction-response', 'batch', 'batch-response', 'history'].includes(bundle.type)).toBe(true);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Wait for a condition to be met (useful for async operations)
|
|
96
|
+
*/
|
|
97
|
+
static waitFor(condition_1) {
|
|
98
|
+
return __awaiter(this, arguments, void 0, function* (condition, timeout = 5000) {
|
|
99
|
+
const start = Date.now();
|
|
100
|
+
while (Date.now() - start < timeout) {
|
|
101
|
+
if (yield condition()) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
yield new Promise(resolve => setTimeout(resolve, 100));
|
|
105
|
+
}
|
|
106
|
+
throw new Error(`Condition not met within ${timeout}ms`);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Retry a function with exponential backoff
|
|
111
|
+
*/
|
|
112
|
+
static retry(fn_1) {
|
|
113
|
+
return __awaiter(this, arguments, void 0, function* (fn, maxAttempts = 3, initialDelay = 1000) {
|
|
114
|
+
let lastError;
|
|
115
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
116
|
+
try {
|
|
117
|
+
return yield fn();
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
lastError = error;
|
|
121
|
+
if (attempt === maxAttempts) {
|
|
122
|
+
throw lastError;
|
|
123
|
+
}
|
|
124
|
+
const delay = initialDelay * Math.pow(2, attempt - 1);
|
|
125
|
+
yield new Promise(resolve => setTimeout(resolve, delay));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
throw lastError;
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Check if the FHIR server is available
|
|
133
|
+
*/
|
|
134
|
+
static checkServerAvailability() {
|
|
135
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
136
|
+
try {
|
|
137
|
+
const axiosInstance = this.getAxiosInstance();
|
|
138
|
+
const response = yield axiosInstance.get(`${this.FHIR_BASE_URL}/metadata`, { timeout: 5000 });
|
|
139
|
+
return response.status === 200 && response.data.resourceType === 'CapabilityStatement';
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Get server capability statement
|
|
148
|
+
*/
|
|
149
|
+
static getServerCapabilities() {
|
|
150
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
151
|
+
const axiosInstance = this.getAxiosInstance();
|
|
152
|
+
const response = yield axiosInstance.get(`${this.FHIR_BASE_URL}/metadata?_format=json`);
|
|
153
|
+
return response.data;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Clean up test data (if the server supports it)
|
|
158
|
+
*/
|
|
159
|
+
/**
|
|
160
|
+
* Clean up test data (placeholder - HAPI test server doesn't support DELETE)
|
|
161
|
+
*/
|
|
162
|
+
static cleanupTestData(resourceType, identifiers) {
|
|
163
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
164
|
+
// Note: HAPI FHIR test server typically doesn't allow DELETE operations
|
|
165
|
+
// This is a placeholder for when testing against a server that does support cleanup
|
|
166
|
+
const axiosInstance = this.getAxiosInstance();
|
|
167
|
+
for (const id of identifiers) {
|
|
168
|
+
try {
|
|
169
|
+
yield axiosInstance.delete(`${this.FHIR_BASE_URL}/${resourceType}/${id}`);
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
// Ignore errors during cleanup as test servers may not support DELETE
|
|
173
|
+
console.warn(`Could not delete ${resourceType}/${id}:`, error);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Generate test data patterns
|
|
180
|
+
*/
|
|
181
|
+
static getTestPatterns() {
|
|
182
|
+
return {
|
|
183
|
+
// Common search patterns to test
|
|
184
|
+
searchPatterns: [
|
|
185
|
+
{ params: { [FhirTestConfig.SEARCH_PARAMS.COUNT]: 5 } },
|
|
186
|
+
{ params: { [FhirTestConfig.SEARCH_PARAMS.COUNT]: 1 } },
|
|
187
|
+
{ params: { [FhirTestConfig.SEARCH_PARAMS.COUNT]: 10, [FhirTestConfig.SEARCH_PARAMS.SORT]: 'name' } },
|
|
188
|
+
],
|
|
189
|
+
// Resource type combinations for mixed bundles
|
|
190
|
+
resourceCombinations: [
|
|
191
|
+
[FhirTestConfig.RESOURCE_TYPES.PATIENT, FhirTestConfig.RESOURCE_TYPES.ENCOUNTER],
|
|
192
|
+
[FhirTestConfig.RESOURCE_TYPES.PRACTITIONER, FhirTestConfig.RESOURCE_TYPES.ORGANIZATION],
|
|
193
|
+
[FhirTestConfig.RESOURCE_TYPES.PATIENT, FhirTestConfig.RESOURCE_TYPES.OBSERVATION],
|
|
194
|
+
],
|
|
195
|
+
// Error scenarios to test
|
|
196
|
+
errorScenarios: [
|
|
197
|
+
{ url: `${FhirTestConfig.FHIR_BASE_URL}/Patient/non-existent-id-123456`, expectedStatus: 404 },
|
|
198
|
+
{ url: `${FhirTestConfig.FHIR_BASE_URL}/InvalidResourceType`, expectedStatus: 404 },
|
|
199
|
+
]
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
FhirTestConfig.FHIR_BASE_URL = 'http://hapi.fhir.org/baseR4';
|
|
204
|
+
FhirTestConfig.TEST_TIMEOUT = 30000;
|
|
205
|
+
/**
|
|
206
|
+
* Common FHIR resource types for testing
|
|
207
|
+
*/
|
|
208
|
+
FhirTestConfig.RESOURCE_TYPES = {
|
|
209
|
+
PATIENT: 'Patient',
|
|
210
|
+
PRACTITIONER: 'Practitioner',
|
|
211
|
+
ORGANIZATION: 'Organization',
|
|
212
|
+
ENCOUNTER: 'Encounter',
|
|
213
|
+
OBSERVATION: 'Observation',
|
|
214
|
+
CONDITION: 'Condition',
|
|
215
|
+
MEDICATION_REQUEST: 'MedicationRequest',
|
|
216
|
+
PROCEDURE: 'Procedure',
|
|
217
|
+
LOCATION: 'Location',
|
|
218
|
+
APPOINTMENT: 'Appointment',
|
|
219
|
+
CARE_TEAM: 'CareTeam',
|
|
220
|
+
CARE_PLAN: 'CarePlan',
|
|
221
|
+
IMMUNIZATION: 'Immunization',
|
|
222
|
+
ALLERGY_INTOLERANCE: 'AllergyIntolerance',
|
|
223
|
+
DIAGNOSTIC_REPORT: 'DiagnosticReport'
|
|
224
|
+
};
|
|
225
|
+
/**
|
|
226
|
+
* Common search parameters for testing
|
|
227
|
+
*/
|
|
228
|
+
FhirTestConfig.SEARCH_PARAMS = {
|
|
229
|
+
COUNT: '_count',
|
|
230
|
+
FORMAT: '_format',
|
|
231
|
+
INCLUDE: '_include',
|
|
232
|
+
SORT: '_sort',
|
|
233
|
+
TEXT: '_text'
|
|
234
|
+
};
|
|
235
|
+
/**
|
|
236
|
+
* Test utilities for performance monitoring
|
|
237
|
+
*/
|
|
238
|
+
export class FhirTestPerformance {
|
|
239
|
+
static startMeasurement(name) {
|
|
240
|
+
const start = performance.now();
|
|
241
|
+
return () => {
|
|
242
|
+
const duration = performance.now() - start;
|
|
243
|
+
this.recordMeasurement(name, duration);
|
|
244
|
+
return duration;
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
static recordMeasurement(name, duration) {
|
|
248
|
+
if (!this.measurements.has(name)) {
|
|
249
|
+
this.measurements.set(name, []);
|
|
250
|
+
}
|
|
251
|
+
this.measurements.get(name).push(duration);
|
|
252
|
+
}
|
|
253
|
+
static getStatistics(name) {
|
|
254
|
+
const measurements = this.measurements.get(name);
|
|
255
|
+
if (!measurements || measurements.length === 0) {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
const avg = measurements.reduce((sum, val) => sum + val, 0) / measurements.length;
|
|
259
|
+
const min = Math.min(...measurements);
|
|
260
|
+
const max = Math.max(...measurements);
|
|
261
|
+
return { avg, min, max, count: measurements.length };
|
|
262
|
+
}
|
|
263
|
+
static reset() {
|
|
264
|
+
this.measurements.clear();
|
|
265
|
+
}
|
|
266
|
+
static logStatistics() {
|
|
267
|
+
for (const [name, measurements] of this.measurements.entries()) {
|
|
268
|
+
const stats = this.getStatistics(name);
|
|
269
|
+
if (stats) {
|
|
270
|
+
console.log(`${name}: avg=${stats.avg.toFixed(2)}ms, min=${stats.min.toFixed(2)}ms, max=${stats.max.toFixed(2)}ms, count=${stats.count}`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
FhirTestPerformance.measurements = new Map();
|
|
276
|
+
//# sourceMappingURL=fhir-test-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fhir-test-config.js","sourceRoot":"","sources":["../../../../../../../libs/isomorphic/fhir/src/lib/functions/fhir-test-config.ts"],"names":[],"mappings":"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"}
|