@openfn/language-fhir-4 0.4.1 → 0.5.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.
@@ -2,63 +2,56 @@
2
2
  "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "type": "object",
4
4
  "properties": {
5
- "baseUrl": {
6
- "title": "Base Url",
7
- "type": "string",
8
- "description": "The baseUrl",
9
- "examples": [
10
- "https://hapi.fhir.org"
11
- ]
12
- },
13
- "apiPath": {
14
- "title": "API Path",
15
- "anyOf": [
16
- {
17
- "type": "string"
18
- },
19
- {
20
- "type": "null"
21
- }
22
- ],
23
- "placeholder": "R4",
24
- "description": "FHIR api path",
25
- "minLength": 1,
26
- "examples": [
27
- "baseR4",
28
- "R3",
29
- "R4"
30
- ]
31
- },
32
- "username": {
33
- "title": "Username",
34
- "type": "string",
35
- "description": "Username",
36
- "examples": [
37
- "openfn_test"
38
- ]
39
- },
40
- "password": {
41
- "title": "Password",
42
- "type": "string",
43
- "description": "Password",
44
- "writeOnly": true,
45
- "examples": [
46
- "@some(!)Str0ngp4ss0w0rd"
47
- ]
48
- },
49
- "access_token": {
50
- "title": "Access Token",
51
- "type": "string",
52
- "description": "Your FHIR access token",
53
- "writeOnly": true,
54
- "minLength": 1,
55
- "examples": [
56
- "the-long-access-token-from-your-auth"
57
- ]
58
- }
5
+ "baseUrl": {
6
+ "title": "Base Url",
7
+ "type": "string",
8
+ "description": "The baseUrl",
9
+ "examples": ["https://hapi.fhir.org"]
10
+ },
11
+ "apiPath": {
12
+ "title": "API Path",
13
+ "anyOf": [
14
+ {
15
+ "type": "string"
16
+ },
17
+ {
18
+ "type": "null"
19
+ }
20
+ ],
21
+ "placeholder": "R4",
22
+ "description": "FHIR api path",
23
+ "minLength": 1,
24
+ "examples": ["baseR4", "R3", "R4"]
25
+ },
26
+ "username": {
27
+ "title": "Username",
28
+ "type": "string",
29
+ "description": "Username",
30
+ "examples": ["openfn_test"]
31
+ },
32
+ "password": {
33
+ "title": "Password",
34
+ "type": "string",
35
+ "description": "Password",
36
+ "writeOnly": true,
37
+ "examples": ["@some(!)Str0ngp4ss0w0rd"]
38
+ },
39
+ "access_token": {
40
+ "title": "Access Token",
41
+ "type": "string",
42
+ "description": "Your FHIR access token",
43
+ "writeOnly": true,
44
+ "minLength": 1,
45
+ "examples": ["the-long-access-token-from-your-auth"]
46
+ },
47
+ "authorization": {
48
+ "title": "Authorization Header",
49
+ "type": "string",
50
+ "description": "Custom Authorization header value",
51
+ "writeOnly": true,
52
+ "minLength": 1,
53
+ "examples": ["custom 12345"]
54
+ }
59
55
  },
60
- "required": [
61
- "baseUrl",
62
- "apiPath"
63
- ]
64
- }
56
+ "required": ["baseUrl", "apiPath"]
57
+ }
package/dist/index.cjs CHANGED
@@ -79,6 +79,7 @@ var import_util2 = require("@openfn/language-common/util");
79
79
 
80
80
  // src/util.ts
81
81
  var import_node_path = __toESM(require("path"), 1);
82
+ var import_lodash_es = __toESM(require("lodash-es"), 1);
82
83
  var import_language_common = require("@openfn/language-common");
83
84
  var import_util = require("@openfn/language-common/util");
84
85
  function assertValidResourceId(id2) {
@@ -91,18 +92,22 @@ function assertValidResourceId(id2) {
91
92
  }
92
93
  function addAuth(options) {
93
94
  var _a;
94
- if ((_a = options == null ? void 0 : options.headers) == null ? void 0 : _a.Authorization)
95
+ if ((_a = options.headers) == null ? void 0 : _a.Authorization) {
95
96
  return;
96
- const { username, password, access_token } = options.configuration;
97
- if (access_token) {
97
+ }
98
+ const { username, password, access_token, authorization } = options.configuration;
99
+ if (authorization) {
100
+ return { Authorization: authorization };
101
+ }
102
+ if (access_token || authorization) {
98
103
  return { Authorization: `Bearer ${access_token}` };
99
- } else if (username && password) {
104
+ }
105
+ if (username && password) {
100
106
  return { ...(0, import_util.makeBasicAuthHeader)(username, password) };
101
107
  }
102
- return {};
103
108
  }
104
109
  var prepareNextState = (state, response) => {
105
- const { body, ...responseWithoutBody } = response;
110
+ const { body, validationErrors, ...responseWithoutBody } = response;
106
111
  return {
107
112
  ...(0, import_language_common.composeNextState)(state, body),
108
113
  response: responseWithoutBody
@@ -130,15 +135,15 @@ var request = (method, path, options) => {
130
135
  (0, import_util.assertRelativeUrl)(path);
131
136
  const { configuration, ...otherOptions } = options;
132
137
  const fullPath = import_node_path.default.join(configuration.apiPath ?? "/fhir", path);
133
- const headers = addAuth(options);
134
138
  const opts = {
135
139
  ...otherOptions,
136
140
  headers: Object.assign(
137
- headers,
141
+ {},
138
142
  {
139
143
  accept: "application/fhir+json",
140
144
  "content-type": "application/fhir+json"
141
145
  },
146
+ addAuth(options),
142
147
  options.headers
143
148
  ),
144
149
  baseUrl: configuration.baseUrl,
@@ -146,12 +151,12 @@ var request = (method, path, options) => {
146
151
  };
147
152
  return (0, import_util.request)(method, fullPath, opts).then(logResponse).then().catch(async (e) => {
148
153
  if (e.headers && "content-type" in e.headers && e.headers["content-type"].match(/fhir\+json/)) {
149
- logValidationErrors(e);
154
+ logValidationErrors(e, opts.body);
150
155
  }
151
156
  throw e;
152
157
  });
153
158
  };
154
- function logValidationErrors(response, logger = console) {
159
+ function logValidationErrors(response, payload, logger = console) {
155
160
  const error = JSON.parse(response.body);
156
161
  if (error.issue && error.issue.length) {
157
162
  delete response.body;
@@ -176,20 +181,35 @@ function logValidationErrors(response, logger = console) {
176
181
  error.issue.forEach((issue) => {
177
182
  var _a, _b, _c;
178
183
  try {
179
- let id2 = "unidentified resource";
184
+ let resourceId = "unidentified resource";
180
185
  if (issue.location) {
181
- id2 = issue.location[0];
182
- if (id2.startsWith("Bundle")) {
183
- id2 = id2.split("*").at(1) ?? id2;
186
+ let idx = issue.location[0].match(/Bundle.entry\[(\d+)\]/);
187
+ if (idx && idx.length >= 2) {
188
+ idx = idx[1];
189
+ const resource = (_a = import_lodash_es.default.get(payload, `entry[${idx}]`)) == null ? void 0 : _a.resource;
190
+ if (resource) {
191
+ resourceId = `${resource.resourceType}/${resource.id}`;
192
+ }
193
+ } else {
194
+ if (payload.resourceType && payload.id) {
195
+ resourceId = `${payload.resourceType}/${payload.id}`;
196
+ }
184
197
  }
185
- } else {
186
- console.log({ issue });
187
198
  }
188
- groups[id2] ?? (groups[id2] = {});
189
- (_a = groups[id2])[_b = issue.severity] ?? (_a[_b] = []);
190
- const path = issue.location[0];
191
- (_c = groups[id2][issue.severity])[path] ?? (_c[path] = []);
192
- groups[id2][issue.severity][path].push(issue.diagnostics);
199
+ const type = `${issue.severity}s`;
200
+ groups[resourceId] ?? (groups[resourceId] = {});
201
+ let path = "*";
202
+ if (issue.location[0].match(/\.resource\./)) {
203
+ path = issue.location[0].split(/\.resource\./)[1];
204
+ } else if (issue.location.includes(`*${resourceId}*`)) {
205
+ const suffix = issue.location[0].split(/\.resource\./)[1];
206
+ if (suffix && suffix.length > 1) {
207
+ path = suffix;
208
+ }
209
+ }
210
+ (_b = groups[resourceId])[type] ?? (_b[type] = {});
211
+ (_c = groups[resourceId][type])[path] ?? (_c[path] = []);
212
+ groups[resourceId][type][path].push(issue.diagnostics);
193
213
  } catch (e) {
194
214
  logger.log("error parsing issue at ", issue.location);
195
215
  console.log(e);
@@ -197,26 +217,36 @@ function logValidationErrors(response, logger = console) {
197
217
  });
198
218
  for (const resource in groups) {
199
219
  logger.log(`${resource} issues:`);
200
- ["error", "warning"].forEach((type) => {
220
+ ["errors", "warnings"].forEach((type) => {
201
221
  if (type in groups[resource]) {
202
- logger.log(` ${type}s:`.toUpperCase());
222
+ logger.log(` ${type}:`.toUpperCase());
203
223
  for (const path in groups[resource][type]) {
204
224
  logger.log();
205
225
  logger.log(" ", path);
206
226
  for (const message of groups[resource][type][path]) {
207
- logger.log(" -", message);
227
+ const fn2 = type === "error" ? console.error : console.warn;
228
+ fn2(" -", message);
208
229
  }
209
230
  }
210
231
  logger.log();
211
232
  }
212
233
  });
213
234
  }
214
- response.validationIssues = groups;
235
+ logger.log("Issues will be written to state.fhirValidationIssues");
236
+ response.$validationIssues = groups;
215
237
  } else {
216
238
  response.body = error;
217
239
  }
218
240
  return response;
219
241
  }
242
+ function cleanResponseObject(state, response) {
243
+ if (response.$validationIssues) {
244
+ state.fhirValidationIssues ?? (state.fhirValidationIssues = {});
245
+ Object.assign(state.fhirValidationIssues, response.$validationIssues);
246
+ delete response.$validationIssues;
247
+ }
248
+ return state;
249
+ }
220
250
  function collectRefs(value2) {
221
251
  if (!value2 || typeof value2 !== "object")
222
252
  return [];
@@ -383,6 +413,9 @@ function create(resource) {
383
413
  const response = await request("POST", $resource.resourceType, {
384
414
  configuration: state.configuration,
385
415
  body: $resource
416
+ }).catch((e) => {
417
+ cleanResponseObject(state, e);
418
+ throw e;
386
419
  });
387
420
  console.log(`Created ${response.body.id}`);
388
421
  return prepareNextState(state, response);
@@ -447,6 +480,9 @@ function uploadBundle(bundle = "bundle") {
447
480
  const response = await request("POST", "/", {
448
481
  configuration: state.configuration,
449
482
  body: data
483
+ }).catch((e) => {
484
+ cleanResponseObject(state, e);
485
+ throw e;
450
486
  });
451
487
  return prepareNextState(state, response);
452
488
  };