n8n-nodes-openmrs 1.0.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/LICENSE +21 -0
- package/README.md +367 -0
- package/dist/credentials/OpenMrsApi.credentials.d.ts +9 -0
- package/dist/credentials/OpenMrsApi.credentials.js +54 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -0
- package/dist/nodes/OpenMrs/OpenMrs.node.d.ts +5 -0
- package/dist/nodes/OpenMrs/OpenMrs.node.js +322 -0
- package/dist/nodes/OpenMrs/OpenMrs.svg +13 -0
- package/package.json +68 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Monfort N. Brian
|
|
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,367 @@
|
|
|
1
|
+
# n8n-nodes-openmrs
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
This is an n8n community node that provides seamless integration with OpenMRS through standardized FHIR R4 API endpoints. It enables healthcare workflows to access and process electronic medical records data for clinical decision support, data analysis, and interoperability.
|
|
8
|
+
|
|
9
|
+
[n8n](https://n8n.io/) is a fair-code licensed workflow automation platform.
|
|
10
|
+
|
|
11
|
+
## Features
|
|
12
|
+
|
|
13
|
+
**Comprehensive FHIR R4 Support**
|
|
14
|
+
|
|
15
|
+
- Access 6 core FHIR resources essential for clinical workflows
|
|
16
|
+
- Standardized healthcare data format for maximum interoperability
|
|
17
|
+
|
|
18
|
+
**Patient Data Management**
|
|
19
|
+
|
|
20
|
+
- Fetch complete patient demographics and identifiers
|
|
21
|
+
- Retrieve encounter history and visit timelines
|
|
22
|
+
- Access clinical observations (labs, vitals, diagnostics)
|
|
23
|
+
|
|
24
|
+
**Clinical Intelligence**
|
|
25
|
+
|
|
26
|
+
- Diagnostic reports and imaging data
|
|
27
|
+
- Condition/diagnosis tracking with staging
|
|
28
|
+
- Medication statements and treatment history
|
|
29
|
+
|
|
30
|
+
**Developer-Friendly**
|
|
31
|
+
|
|
32
|
+
- Simple authentication with OpenMRS credentials
|
|
33
|
+
- Paginated results with customizable limits
|
|
34
|
+
- Built-in error handling and retry logic
|
|
35
|
+
|
|
36
|
+
**Built for Low-Resource Settings**
|
|
37
|
+
|
|
38
|
+
- Optimized for limited bandwidth environments
|
|
39
|
+
- Works with standard OpenMRS installations
|
|
40
|
+
- Supports both cloud and on-premise deployments
|
|
41
|
+
|
|
42
|
+
## Installation
|
|
43
|
+
|
|
44
|
+
### Community Nodes (Recommended)
|
|
45
|
+
|
|
46
|
+
1. Open n8n
|
|
47
|
+
2. Go to **Settings** → **Community Nodes**
|
|
48
|
+
3. Click **Install a community node**
|
|
49
|
+
4. Enter: `n8n-nodes-openmrs`
|
|
50
|
+
5. Click **Install**
|
|
51
|
+
|
|
52
|
+
### Manual Installation
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm install n8n-nodes-openmrs
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
For Docker installations:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
docker run -it --rm \
|
|
62
|
+
--name n8n \
|
|
63
|
+
-p 5678:5678 \
|
|
64
|
+
-v ~/.n8n:/home/node/.n8n \
|
|
65
|
+
-e N8N_CUSTOM_EXTENSIONS="/home/node/.n8n/custom" \
|
|
66
|
+
n8nio/n8n
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Then install the package inside the container or mount it as a volume.
|
|
70
|
+
|
|
71
|
+
### Build from Source
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
git clone https://github.com/monfortbrian/n8n-nodes-OpenMRS.git
|
|
75
|
+
cd n8n-nodes-openmrs
|
|
76
|
+
npm install
|
|
77
|
+
npm run build
|
|
78
|
+
npm link
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Credentials
|
|
82
|
+
|
|
83
|
+
Before using the OpenMRS node, you need to configure credentials:
|
|
84
|
+
|
|
85
|
+
1. In n8n, go to **Credentials** → **New**
|
|
86
|
+
2. Search for **OpenMRS API**
|
|
87
|
+
3. Configure:
|
|
88
|
+
- **Base URL**: Your OpenMRS instance URL (e.g., `https://demo.openmrs.org/openmrs`)
|
|
89
|
+
- **Username**: Your OpenMRS username
|
|
90
|
+
- **Password**: Your OpenMRS password
|
|
91
|
+
|
|
92
|
+
The node uses HTTP Basic Authentication and automatically validates connectivity on save.
|
|
93
|
+
|
|
94
|
+
## Usage
|
|
95
|
+
|
|
96
|
+
### Supported Resources
|
|
97
|
+
|
|
98
|
+
The OpenMRS node provides access to 6 FHIR resources:
|
|
99
|
+
|
|
100
|
+
| Resource | Description | Use Cases |
|
|
101
|
+
| ------------------------ | ----------------------------- | -------------------------------------------- |
|
|
102
|
+
| **Patient** | Demographics, identifiers | Identity verification, cohort analysis |
|
|
103
|
+
| **Encounter** | Visits, admissions | Timeline reconstruction, visit patterns |
|
|
104
|
+
| **Observation** | Labs, vitals, diagnostics | Trend analysis, risk detection |
|
|
105
|
+
| **Diagnostic Report** | Imaging, pathology | Disease progression, treatment response |
|
|
106
|
+
| **Condition** | Diagnoses, problem lists | Differential diagnosis, comorbidity tracking |
|
|
107
|
+
| **Medication Statement** | Active/historical medications | Treatment history, drug interactions |
|
|
108
|
+
|
|
109
|
+
### Operations
|
|
110
|
+
|
|
111
|
+
Each resource supports:
|
|
112
|
+
|
|
113
|
+
- **Get a resource** - Retrieve a single resource by ID
|
|
114
|
+
- **Get all resources** - Retrieve all resources for a patient (with pagination)
|
|
115
|
+
|
|
116
|
+
## Examples
|
|
117
|
+
|
|
118
|
+
### Example 1: Fetch Patient Demographics
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
1. Add OpenMRS node
|
|
122
|
+
2. Select Resource: Patient
|
|
123
|
+
3. Select Operation: Get a resource
|
|
124
|
+
4. Enter Patient ID: abc-123-def-456
|
|
125
|
+
5. Execute
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Output:**
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"resourceType": "Patient",
|
|
133
|
+
"id": "abc-123-def-456",
|
|
134
|
+
"name": [
|
|
135
|
+
{
|
|
136
|
+
"given": ["John"],
|
|
137
|
+
"family": "Doe"
|
|
138
|
+
}
|
|
139
|
+
],
|
|
140
|
+
"gender": "male",
|
|
141
|
+
"birthDate": "1957-03-15"
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Example 2: Retrieve Patient Lab Results
|
|
146
|
+
|
|
147
|
+
```
|
|
148
|
+
1. Add OpenMRS node
|
|
149
|
+
2. Select Resource: Observation
|
|
150
|
+
3. Select Operation: Get all resources
|
|
151
|
+
4. Enter Patient ID: abc-123-def-456
|
|
152
|
+
5. Set Limit: 50
|
|
153
|
+
6. Execute
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Output:** Returns up to 50 lab observations with values, units, and reference ranges.
|
|
157
|
+
|
|
158
|
+
### Example 3: Get Patient Diagnosis History
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
1. Add OpenMRS node
|
|
162
|
+
2. Select Resource: Condition
|
|
163
|
+
3. Select Operation: Get all resources
|
|
164
|
+
4. Enter Patient ID: abc-123-def-456
|
|
165
|
+
5. Select Return All: true (for complete history)
|
|
166
|
+
6. Execute
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Example 4: Clinical Decision Support Workflow
|
|
170
|
+
|
|
171
|
+
Build an intelligent ASSESS workflow:
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
[OpenMRS Patient] ──┐
|
|
175
|
+
[OpenMRS Encounters] ├─→ [Normalize Data] → [LLM Analysis] → [Alert Doctor]
|
|
176
|
+
[OpenMRS Labs] ─────┘
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
This workflow:
|
|
180
|
+
|
|
181
|
+
1. Fetches patient demographics, encounters, and lab results
|
|
182
|
+
2. Normalizes FHIR data into unified format
|
|
183
|
+
3. Analyzes trends using AI
|
|
184
|
+
4. Generates clinical insights
|
|
185
|
+
|
|
186
|
+
## Options
|
|
187
|
+
|
|
188
|
+
### Common Parameters
|
|
189
|
+
|
|
190
|
+
| Parameter | Type | Description | Required |
|
|
191
|
+
| ---------- | -------- | ---------------------------- | ------------------------- |
|
|
192
|
+
| Resource | Dropdown | FHIR resource type | Yes |
|
|
193
|
+
| Operation | Dropdown | Get or Get All | Yes |
|
|
194
|
+
| Patient ID | String | UUID of the patient | Yes (for most operations) |
|
|
195
|
+
| Return All | Boolean | Fetch all results (no limit) | No |
|
|
196
|
+
| Limit | Number | Max results (1-100) | No (default: 50) |
|
|
197
|
+
|
|
198
|
+
### Resource-Specific Parameters
|
|
199
|
+
|
|
200
|
+
**Encounter:**
|
|
201
|
+
|
|
202
|
+
- Encounter ID (for Get operation)
|
|
203
|
+
|
|
204
|
+
**Condition:**
|
|
205
|
+
|
|
206
|
+
- Condition ID (for Get operation)
|
|
207
|
+
|
|
208
|
+
**Medication Statement:**
|
|
209
|
+
|
|
210
|
+
- Medication Statement ID (for Get operation)
|
|
211
|
+
|
|
212
|
+
### Pagination
|
|
213
|
+
|
|
214
|
+
For `Get all resources` operations:
|
|
215
|
+
|
|
216
|
+
- Set **Return All** to `true` for complete datasets
|
|
217
|
+
- Or set **Limit** to control result size (1-100)
|
|
218
|
+
- Results are returned in FHIR Bundle format
|
|
219
|
+
|
|
220
|
+
## API Endpoints
|
|
221
|
+
|
|
222
|
+
This node uses OpenMRS FHIR2 R4 endpoints:
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
GET /ws/fhir2/R4/Patient/{id}
|
|
226
|
+
GET /ws/fhir2/R4/Patient?_count=50
|
|
227
|
+
|
|
228
|
+
GET /ws/fhir2/R4/Encounter?patient={id}&_count=50
|
|
229
|
+
|
|
230
|
+
GET /ws/fhir2/R4/Observation?patient={id}&_count=50
|
|
231
|
+
|
|
232
|
+
GET /ws/fhir2/R4/DiagnosticReport?patient={id}&_count=50
|
|
233
|
+
|
|
234
|
+
GET /ws/fhir2/R4/Condition?patient={id}&_count=50
|
|
235
|
+
|
|
236
|
+
GET /ws/fhir2/R4/MedicationStatement?patient={id}&_count=50
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
## Use Cases
|
|
240
|
+
|
|
241
|
+
### Clinical Decision Support
|
|
242
|
+
|
|
243
|
+
- Reconstruct patient timelines
|
|
244
|
+
- Detect missed clinical signals
|
|
245
|
+
- Flag abnormal lab trends
|
|
246
|
+
- Track disease progression
|
|
247
|
+
|
|
248
|
+
### Data Analytics
|
|
249
|
+
|
|
250
|
+
- Population health analysis
|
|
251
|
+
- Cohort identification
|
|
252
|
+
- Treatment outcome tracking
|
|
253
|
+
- Quality metrics reporting
|
|
254
|
+
|
|
255
|
+
### Interoperability
|
|
256
|
+
|
|
257
|
+
- Sync data between systems
|
|
258
|
+
- Export for external analysis
|
|
259
|
+
- Integration with AI/ML pipelines
|
|
260
|
+
- REDCap or DHIS2 data flows
|
|
261
|
+
|
|
262
|
+
### Global Health
|
|
263
|
+
|
|
264
|
+
- Low-resource oncology workflows
|
|
265
|
+
- HIV/TB treatment tracking
|
|
266
|
+
- Maternal health monitoring
|
|
267
|
+
- Vaccine registry management
|
|
268
|
+
|
|
269
|
+
## Compatibility
|
|
270
|
+
|
|
271
|
+
- **n8n version:** 0.187.0 or higher
|
|
272
|
+
- **OpenMRS version:** 2.3+ with FHIR2 module installed
|
|
273
|
+
- **Node.js:** 18.0.0 or higher
|
|
274
|
+
|
|
275
|
+
## Development
|
|
276
|
+
|
|
277
|
+
### Building
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
npm run build
|
|
281
|
+
npm run dev
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Testing Locally
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
npm link
|
|
288
|
+
cd ~/.n8n/custom
|
|
289
|
+
npm link n8n-nodes-openmrs
|
|
290
|
+
n8n start
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Troubleshooting
|
|
294
|
+
|
|
295
|
+
### Node doesn't appear in n8n
|
|
296
|
+
|
|
297
|
+
1. Verify package is installed: `npm list -g n8n-nodes-openmrs`
|
|
298
|
+
2. Clear n8n cache: `rm -rf ~/.n8n/cache`
|
|
299
|
+
3. Restart n8n
|
|
300
|
+
4. Hard refresh browser (Ctrl+Shift+R)
|
|
301
|
+
|
|
302
|
+
### Authentication errors
|
|
303
|
+
|
|
304
|
+
- Verify Base URL includes `/openmrs` path (e.g., `https://demo.openmrs.org/openmrs`)
|
|
305
|
+
- Confirm username/password are correct
|
|
306
|
+
- Check OpenMRS FHIR2 module is installed and enabled
|
|
307
|
+
|
|
308
|
+
### Empty results
|
|
309
|
+
|
|
310
|
+
- Confirm patient UUID exists in your OpenMRS instance
|
|
311
|
+
- Verify patient has data for the requested resource type
|
|
312
|
+
- Check FHIR2 module configuration in OpenMRS
|
|
313
|
+
|
|
314
|
+
## Resources
|
|
315
|
+
|
|
316
|
+
- [OpenMRS Documentation](https://wiki.openmrs.org/)
|
|
317
|
+
- [FHIR R4 Specification](https://hl7.org/fhir/R4/)
|
|
318
|
+
- [n8n Community Nodes](https://docs.n8n.io/integrations/community-nodes/)
|
|
319
|
+
- [OpenMRS FHIR2 Module](https://wiki.openmrs.org/display/projects/FHIR+Module)
|
|
320
|
+
|
|
321
|
+
## Contributing
|
|
322
|
+
|
|
323
|
+
Contributions are welcome! Please:
|
|
324
|
+
|
|
325
|
+
1. Fork the repository
|
|
326
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
327
|
+
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
328
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
329
|
+
5. Open a Pull Request
|
|
330
|
+
|
|
331
|
+
## License
|
|
332
|
+
|
|
333
|
+
[MIT License](LICENSE)
|
|
334
|
+
|
|
335
|
+
Copyright (c) 2026 Monfort N. Brian
|
|
336
|
+
|
|
337
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
338
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
339
|
+
in the Software without restriction, including without limitation the rights
|
|
340
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
341
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
342
|
+
furnished to do so, subject to the following conditions:
|
|
343
|
+
|
|
344
|
+
The above copyright notice and this permission notice shall be included in all
|
|
345
|
+
copies or substantial portions of the Software.
|
|
346
|
+
|
|
347
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
348
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
349
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
350
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
351
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
352
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
353
|
+
SOFTWARE.
|
|
354
|
+
|
|
355
|
+
## Support
|
|
356
|
+
|
|
357
|
+
- **Issues:** [GitHub Issues](https://github.com/monfortbrian/n8n-nodes-OpenMRS/issues)
|
|
358
|
+
- **Discussions:** [GitHub Discussions](https://github.com/monfortbrian/n8n-nodes-OpenMRS/discussions)
|
|
359
|
+
- **n8n Community:** [n8n Community Forum](https://community.n8n.io/)
|
|
360
|
+
|
|
361
|
+
## Acknowledgments
|
|
362
|
+
|
|
363
|
+
Built for healthcare workers in low-resource settings. Designed to enable clinical decision support and improve patient outcomes through better data interoperability.
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
**Made with ❤️ for the global health community**
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare class OpenMrsApi implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
documentationUrl: string;
|
|
6
|
+
properties: INodeProperties[];
|
|
7
|
+
authenticate: IAuthenticateGeneric;
|
|
8
|
+
test: ICredentialTestRequest;
|
|
9
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OpenMrsApi = void 0;
|
|
4
|
+
class OpenMrsApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'openMrsApi';
|
|
7
|
+
this.displayName = 'OpenMRS API';
|
|
8
|
+
this.documentationUrl = 'https://wiki.openmrs.org/display/docs/REST+API';
|
|
9
|
+
this.properties = [
|
|
10
|
+
{
|
|
11
|
+
displayName: 'Base URL',
|
|
12
|
+
name: 'baseUrl',
|
|
13
|
+
type: 'string',
|
|
14
|
+
default: 'https://your-openmrs-instance.org',
|
|
15
|
+
placeholder: 'https://demo.openmrs.org/openmrs',
|
|
16
|
+
description: 'The base URL of your OpenMRS instance',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
displayName: 'Username',
|
|
20
|
+
name: 'username',
|
|
21
|
+
type: 'string',
|
|
22
|
+
default: '',
|
|
23
|
+
description: 'OpenMRS username',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
displayName: 'Password',
|
|
27
|
+
name: 'password',
|
|
28
|
+
type: 'string',
|
|
29
|
+
typeOptions: {
|
|
30
|
+
password: true,
|
|
31
|
+
},
|
|
32
|
+
default: '',
|
|
33
|
+
description: 'OpenMRS password',
|
|
34
|
+
},
|
|
35
|
+
];
|
|
36
|
+
this.authenticate = {
|
|
37
|
+
type: 'generic',
|
|
38
|
+
properties: {
|
|
39
|
+
auth: {
|
|
40
|
+
username: '={{$credentials.username}}',
|
|
41
|
+
password: '={{$credentials.password}}',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
this.test = {
|
|
46
|
+
request: {
|
|
47
|
+
baseURL: '={{$credentials.baseUrl}}',
|
|
48
|
+
url: '/ws/fhir2/R4/metadata',
|
|
49
|
+
method: 'GET',
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
exports.OpenMrsApi = OpenMrsApi;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./nodes/OpenMrs/OpenMrs.node"), exports);
|
|
18
|
+
__exportStar(require("./credentials/OpenMrsApi.credentials"), exports);
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OpenMrs = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
class OpenMrs {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.description = {
|
|
8
|
+
displayName: 'OpenMRS',
|
|
9
|
+
name: 'openMrs',
|
|
10
|
+
icon: 'file:OpenMrs.svg',
|
|
11
|
+
group: ['transform'],
|
|
12
|
+
version: 1,
|
|
13
|
+
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
|
14
|
+
description: 'Interact with OpenMRS FHIR API',
|
|
15
|
+
defaults: {
|
|
16
|
+
name: 'OpenMRS',
|
|
17
|
+
},
|
|
18
|
+
inputs: ['main'],
|
|
19
|
+
outputs: ['main'],
|
|
20
|
+
credentials: [
|
|
21
|
+
{
|
|
22
|
+
name: 'openMrsApi',
|
|
23
|
+
required: true,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
properties: [
|
|
27
|
+
{
|
|
28
|
+
displayName: 'Resource',
|
|
29
|
+
name: 'resource',
|
|
30
|
+
type: 'options',
|
|
31
|
+
noDataExpression: true,
|
|
32
|
+
options: [
|
|
33
|
+
{
|
|
34
|
+
name: 'Patient',
|
|
35
|
+
value: 'patient',
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'Encounter',
|
|
39
|
+
value: 'encounter',
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: 'Observation',
|
|
43
|
+
value: 'observation',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
name: 'Diagnostic Report',
|
|
47
|
+
value: 'diagnosticReport',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'Condition',
|
|
51
|
+
value: 'condition',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: 'Medication Statement',
|
|
55
|
+
value: 'medicationStatement',
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
default: 'patient',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
displayName: 'Operation',
|
|
62
|
+
name: 'operation',
|
|
63
|
+
type: 'options',
|
|
64
|
+
noDataExpression: true,
|
|
65
|
+
displayOptions: {
|
|
66
|
+
show: {
|
|
67
|
+
resource: [
|
|
68
|
+
'patient',
|
|
69
|
+
'encounter',
|
|
70
|
+
'observation',
|
|
71
|
+
'diagnosticReport',
|
|
72
|
+
'condition',
|
|
73
|
+
'medicationStatement',
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
options: [
|
|
78
|
+
{
|
|
79
|
+
name: 'Get',
|
|
80
|
+
value: 'get',
|
|
81
|
+
description: 'Get a resource by ID',
|
|
82
|
+
action: 'Get a resource',
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: 'Get All',
|
|
86
|
+
value: 'getAll',
|
|
87
|
+
description: 'Get all resources',
|
|
88
|
+
action: 'Get all resources',
|
|
89
|
+
},
|
|
90
|
+
],
|
|
91
|
+
default: 'get',
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
displayName: 'Patient ID',
|
|
95
|
+
name: 'patientId',
|
|
96
|
+
type: 'string',
|
|
97
|
+
required: true,
|
|
98
|
+
displayOptions: {
|
|
99
|
+
show: {
|
|
100
|
+
resource: ['patient'],
|
|
101
|
+
operation: ['get'],
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
default: '',
|
|
105
|
+
description: 'UUID of the patient',
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
displayName: 'Patient ID',
|
|
109
|
+
name: 'patientId',
|
|
110
|
+
type: 'string',
|
|
111
|
+
required: true,
|
|
112
|
+
displayOptions: {
|
|
113
|
+
show: {
|
|
114
|
+
resource: [
|
|
115
|
+
'encounter',
|
|
116
|
+
'observation',
|
|
117
|
+
'diagnosticReport',
|
|
118
|
+
'condition',
|
|
119
|
+
'medicationStatement',
|
|
120
|
+
],
|
|
121
|
+
operation: ['getAll'],
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
default: '',
|
|
125
|
+
description: 'UUID of the patient to filter by',
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
displayName: 'Encounter ID',
|
|
129
|
+
name: 'encounterId',
|
|
130
|
+
type: 'string',
|
|
131
|
+
required: true,
|
|
132
|
+
displayOptions: {
|
|
133
|
+
show: {
|
|
134
|
+
resource: ['encounter'],
|
|
135
|
+
operation: ['get'],
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
default: '',
|
|
139
|
+
description: 'UUID of the encounter',
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
displayName: 'Condition ID',
|
|
143
|
+
name: 'conditionId',
|
|
144
|
+
type: 'string',
|
|
145
|
+
required: true,
|
|
146
|
+
displayOptions: {
|
|
147
|
+
show: {
|
|
148
|
+
resource: ['condition'],
|
|
149
|
+
operation: ['get'],
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
default: '',
|
|
153
|
+
description: 'UUID of the condition',
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
displayName: 'Medication Statement ID',
|
|
157
|
+
name: 'medicationStatementId',
|
|
158
|
+
type: 'string',
|
|
159
|
+
required: true,
|
|
160
|
+
displayOptions: {
|
|
161
|
+
show: {
|
|
162
|
+
resource: ['medicationStatement'],
|
|
163
|
+
operation: ['get'],
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
default: '',
|
|
167
|
+
description: 'UUID of the medication statement',
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
displayName: 'Return All',
|
|
171
|
+
name: 'returnAll',
|
|
172
|
+
type: 'boolean',
|
|
173
|
+
displayOptions: {
|
|
174
|
+
show: {
|
|
175
|
+
operation: ['getAll'],
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
default: false,
|
|
179
|
+
description: 'Whether to return all results or only up to a given limit',
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
displayName: 'Limit',
|
|
183
|
+
name: 'limit',
|
|
184
|
+
type: 'number',
|
|
185
|
+
displayOptions: {
|
|
186
|
+
show: {
|
|
187
|
+
operation: ['getAll'],
|
|
188
|
+
returnAll: [false],
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
typeOptions: {
|
|
192
|
+
minValue: 1,
|
|
193
|
+
maxValue: 100,
|
|
194
|
+
},
|
|
195
|
+
default: 50,
|
|
196
|
+
description: 'Max number of results to return',
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
async execute() {
|
|
202
|
+
const items = this.getInputData();
|
|
203
|
+
const returnData = [];
|
|
204
|
+
const credentials = await this.getCredentials('openMrsApi');
|
|
205
|
+
const baseUrl = credentials.baseUrl;
|
|
206
|
+
for (let i = 0; i < items.length; i++) {
|
|
207
|
+
try {
|
|
208
|
+
const resource = this.getNodeParameter('resource', i);
|
|
209
|
+
const operation = this.getNodeParameter('operation', i);
|
|
210
|
+
let endpoint = '';
|
|
211
|
+
const qs = {};
|
|
212
|
+
if (resource === 'patient') {
|
|
213
|
+
if (operation === 'get') {
|
|
214
|
+
const patientId = this.getNodeParameter('patientId', i);
|
|
215
|
+
endpoint = `/ws/fhir2/R4/Patient/${patientId}`;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (resource === 'encounter') {
|
|
219
|
+
if (operation === 'get') {
|
|
220
|
+
const encounterId = this.getNodeParameter('encounterId', i);
|
|
221
|
+
endpoint = `/ws/fhir2/R4/Encounter/${encounterId}`;
|
|
222
|
+
}
|
|
223
|
+
else if (operation === 'getAll') {
|
|
224
|
+
const patientId = this.getNodeParameter('patientId', i);
|
|
225
|
+
endpoint = '/ws/fhir2/R4/Encounter';
|
|
226
|
+
qs.patient = patientId;
|
|
227
|
+
const returnAll = this.getNodeParameter('returnAll', i);
|
|
228
|
+
if (!returnAll) {
|
|
229
|
+
const limit = this.getNodeParameter('limit', i);
|
|
230
|
+
qs._count = limit;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (resource === 'observation') {
|
|
235
|
+
if (operation === 'getAll') {
|
|
236
|
+
const patientId = this.getNodeParameter('patientId', i);
|
|
237
|
+
endpoint = '/ws/fhir2/R4/Observation';
|
|
238
|
+
qs.patient = patientId;
|
|
239
|
+
const returnAll = this.getNodeParameter('returnAll', i);
|
|
240
|
+
if (!returnAll) {
|
|
241
|
+
const limit = this.getNodeParameter('limit', i);
|
|
242
|
+
qs._count = limit;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (resource === 'diagnosticReport') {
|
|
247
|
+
if (operation === 'getAll') {
|
|
248
|
+
const patientId = this.getNodeParameter('patientId', i);
|
|
249
|
+
endpoint = '/ws/fhir2/R4/DiagnosticReport';
|
|
250
|
+
qs.patient = patientId;
|
|
251
|
+
const returnAll = this.getNodeParameter('returnAll', i);
|
|
252
|
+
if (!returnAll) {
|
|
253
|
+
const limit = this.getNodeParameter('limit', i);
|
|
254
|
+
qs._count = limit;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (resource === 'condition') {
|
|
259
|
+
if (operation === 'get') {
|
|
260
|
+
const conditionId = this.getNodeParameter('conditionId', i);
|
|
261
|
+
endpoint = `/ws/fhir2/R4/Condition/${conditionId}`;
|
|
262
|
+
}
|
|
263
|
+
else if (operation === 'getAll') {
|
|
264
|
+
const patientId = this.getNodeParameter('patientId', i);
|
|
265
|
+
endpoint = '/ws/fhir2/R4/Condition';
|
|
266
|
+
qs.patient = patientId;
|
|
267
|
+
const returnAll = this.getNodeParameter('returnAll', i);
|
|
268
|
+
if (!returnAll) {
|
|
269
|
+
const limit = this.getNodeParameter('limit', i);
|
|
270
|
+
qs._count = limit;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
if (resource === 'medicationStatement') {
|
|
275
|
+
if (operation === 'get') {
|
|
276
|
+
const medicationStatementId = this.getNodeParameter('medicationStatementId', i);
|
|
277
|
+
endpoint = `/ws/fhir2/R4/MedicationStatement/${medicationStatementId}`;
|
|
278
|
+
}
|
|
279
|
+
else if (operation === 'getAll') {
|
|
280
|
+
const patientId = this.getNodeParameter('patientId', i);
|
|
281
|
+
endpoint = '/ws/fhir2/R4/MedicationStatement';
|
|
282
|
+
qs.patient = patientId;
|
|
283
|
+
const returnAll = this.getNodeParameter('returnAll', i);
|
|
284
|
+
if (!returnAll) {
|
|
285
|
+
const limit = this.getNodeParameter('limit', i);
|
|
286
|
+
qs._count = limit;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const options = {
|
|
291
|
+
method: 'GET',
|
|
292
|
+
url: `${baseUrl}${endpoint}`,
|
|
293
|
+
qs,
|
|
294
|
+
json: true,
|
|
295
|
+
};
|
|
296
|
+
const responseData = await this.helpers.requestWithAuthentication.call(this, 'openMrsApi', options);
|
|
297
|
+
if (Array.isArray(responseData)) {
|
|
298
|
+
returnData.push(...responseData.map((item) => ({ json: item })));
|
|
299
|
+
}
|
|
300
|
+
else if (responseData.entry) {
|
|
301
|
+
// FHIR Bundle response
|
|
302
|
+
returnData.push(...responseData.entry.map((entry) => ({
|
|
303
|
+
json: entry.resource,
|
|
304
|
+
})));
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
returnData.push({ json: responseData });
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
catch (error) {
|
|
311
|
+
if (this.continueOnFail()) {
|
|
312
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
313
|
+
returnData.push({ json: { error: errorMessage } });
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error instanceof Error ? error : new Error(String(error)));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
return [returnData];
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
exports.OpenMrs = OpenMrs;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g clip-path="url(#clip0_4731_3)">
|
|
3
|
+
<path d="M34.8041 34.8877C42.2938 27.4204 52.6397 22.8017 64.0669 22.8017C75.4685 22.8017 85.7887 27.3995 93.2691 34.8318L93.2995 7.3477C84.5416 2.85009 74.5974 0.306641 64.0669 0.306641C53.5318 0.306641 43.5362 3.16918 34.7714 7.67146L34.8041 34.8877Z" fill="#F26522"/>
|
|
4
|
+
<path d="M93.2995 93.21C85.8144 100.677 75.4708 105.294 64.0413 105.294C52.6467 105.294 42.3218 100.698 34.8368 93.2659L34.8041 120.748C43.5713 125.248 53.5062 127.789 64.0413 127.789C74.5764 127.789 84.5183 125.248 93.2785 120.745L93.2995 93.21Z" fill="#EEA616"/>
|
|
5
|
+
<path d="M34.8625 93.2449C27.3751 85.7776 22.7416 75.4641 22.7416 64.0652C22.7416 52.6988 27.3517 42.4039 34.8041 34.9366L7.24605 34.9087C2.73634 43.6477 0.188393 53.5583 0.188393 64.0652C0.188393 74.5744 2.73634 84.4873 7.25072 93.2263L34.8625 93.2449Z" fill="#5B57A6"/>
|
|
6
|
+
<path d="M93.2831 34.8296C100.773 42.2946 105.404 52.6151 105.404 64.014C105.404 75.3827 100.794 85.6752 93.3392 93.1356L120.9 93.1682C125.412 84.4315 127.957 74.5162 127.957 64.014C127.957 53.5048 125.412 43.5896 120.895 34.8482L93.2831 34.8296Z" fill="#009384"/>
|
|
7
|
+
</g>
|
|
8
|
+
<defs>
|
|
9
|
+
<clipPath id="clip0_4731_3">
|
|
10
|
+
<rect width="128" height="128" fill="white"/>
|
|
11
|
+
</clipPath>
|
|
12
|
+
</defs>
|
|
13
|
+
</svg>
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-openmrs",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "n8n community node for seamless OpenMRS FHIR integration. Fetch patient demographics, encounters, observations, diagnostic reports, conditions, and medications through standardized FHIR R4 API endpoints.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"n8n",
|
|
8
|
+
"n8n-node",
|
|
9
|
+
"openmrs",
|
|
10
|
+
"fhir",
|
|
11
|
+
"healthcare",
|
|
12
|
+
"ehr",
|
|
13
|
+
"emr",
|
|
14
|
+
"medical-records",
|
|
15
|
+
"clinical-data",
|
|
16
|
+
"health-informatics",
|
|
17
|
+
"interoperability",
|
|
18
|
+
"hl7-fhir",
|
|
19
|
+
"patient-data",
|
|
20
|
+
"oncology"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"homepage": "https://github.com/monfortbrian/n8n-nodes-OpenMRS#readme",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "git+https://github.com/monfortbrian/n8n-nodes-OpenMRS.git"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/monfortbrian/n8n-nodes-OpenMRS/issues"
|
|
30
|
+
},
|
|
31
|
+
"author": {
|
|
32
|
+
"name": "Monfort N. Brian",
|
|
33
|
+
"email": "monfortnkurunziza0@gmail.com"
|
|
34
|
+
},
|
|
35
|
+
"main": "dist/index.js",
|
|
36
|
+
"types": "dist/index.d.ts",
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc && npm run copy-assets",
|
|
39
|
+
"copy-assets": "node -e \"const fs=require('fs'); const path=require('path'); const src='src/nodes/OpenMrs/OpenMrs.svg'; const dest='dist/nodes/OpenMrs/OpenMrs.svg'; fs.mkdirSync(path.dirname(dest), {recursive:true}); fs.copyFileSync(src, dest); console.log('Icon copied successfully');\"",
|
|
40
|
+
"dev": "tsc --watch",
|
|
41
|
+
"prepublishOnly": "npm run build"
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"dist",
|
|
45
|
+
"LICENSE",
|
|
46
|
+
"README.md"
|
|
47
|
+
],
|
|
48
|
+
"n8n": {
|
|
49
|
+
"n8nNodesApiVersion": 1,
|
|
50
|
+
"nodes": [
|
|
51
|
+
"dist/nodes/OpenMrs/OpenMrs.node.js"
|
|
52
|
+
],
|
|
53
|
+
"credentials": [
|
|
54
|
+
"dist/credentials/OpenMrsApi.credentials.js"
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@types/node": "^25.0.9",
|
|
59
|
+
"typescript": "^5.9.3"
|
|
60
|
+
},
|
|
61
|
+
"dependencies": {
|
|
62
|
+
"n8n-core": "^1.122.9",
|
|
63
|
+
"n8n-workflow": "^2.4.1"
|
|
64
|
+
},
|
|
65
|
+
"engines": {
|
|
66
|
+
"node": ">=18.0.0"
|
|
67
|
+
}
|
|
68
|
+
}
|