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 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
+ ![n8n-nodes-openmrs](https://img.shields.io/npm/v/n8n-nodes-openmrs)
4
+ ![npm](https://img.shields.io/npm/dt/n8n-nodes-openmrs)
5
+ ![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)
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;
@@ -0,0 +1,2 @@
1
+ export * from './nodes/OpenMrs/OpenMrs.node';
2
+ export * from './credentials/OpenMrsApi.credentials';
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,5 @@
1
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class OpenMrs implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -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
+ }