@healthcloudai/hc-safe-cdx 0.1.4 → 0.2.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/README.md +1368 -320
- package/dist/index.cjs +159 -244
- package/dist/index.d.cts +372 -205
- package/dist/index.d.ts +372 -205
- package/dist/index.js +159 -244
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -13,10 +13,15 @@ var ENV_HOST = {
|
|
|
13
13
|
prod: "api-hcs.healthcloud-services.com"
|
|
14
14
|
};
|
|
15
15
|
var ROUTE_PREFIX = "api/console/hcservice/safecdx";
|
|
16
|
-
function
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
function buildSafeCdxBaseUrl(environment) {
|
|
17
|
+
const host = ENV_HOST[environment];
|
|
18
|
+
if (!host) {
|
|
19
|
+
throw new SafeCDXError("Invalid Safe CDX environment.");
|
|
20
|
+
}
|
|
21
|
+
return `https://${host}/${ROUTE_PREFIX}`;
|
|
22
|
+
}
|
|
23
|
+
function buildUrl(baseUrl, route, query) {
|
|
24
|
+
const url = new URL(`${baseUrl}/${route.replace(/^\/+/, "")}`);
|
|
20
25
|
if (query) {
|
|
21
26
|
for (const [key, value] of Object.entries(query)) {
|
|
22
27
|
if (value !== void 0 && value !== "") {
|
|
@@ -26,30 +31,11 @@ function buildUrl(host, route, query) {
|
|
|
26
31
|
}
|
|
27
32
|
return url.toString();
|
|
28
33
|
}
|
|
29
|
-
function wrapData(payload) {
|
|
30
|
-
return { Data: payload };
|
|
31
|
-
}
|
|
32
|
-
function assertApiResponse(envelope, route) {
|
|
33
|
-
var _a;
|
|
34
|
-
if (!envelope.IsOK) {
|
|
35
|
-
throw new SafeCDXError(
|
|
36
|
-
(_a = envelope.ErrorMessage) != null ? _a : `${route} returned IsOK=false.`,
|
|
37
|
-
envelope
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
return envelope;
|
|
41
|
-
}
|
|
42
34
|
var HCSafeCDXClient = class {
|
|
43
|
-
constructor(httpClient,
|
|
44
|
-
var _a;
|
|
45
|
-
if (!ENV_HOST[config.environment]) {
|
|
46
|
-
throw new SafeCDXError(`Invalid environment: ${config.environment}`);
|
|
47
|
-
}
|
|
35
|
+
constructor(httpClient, loginClient) {
|
|
48
36
|
this.http = httpClient;
|
|
49
|
-
this.
|
|
50
|
-
this.
|
|
51
|
-
this.defaultLanguage = config.defaultLanguage;
|
|
52
|
-
this.defaultImageType = (_a = config.defaultImageType) != null ? _a : "jpg";
|
|
37
|
+
this.loginClient = loginClient;
|
|
38
|
+
this.baseUrl = buildSafeCdxBaseUrl(loginClient.getEnvironment());
|
|
53
39
|
}
|
|
54
40
|
setApiKey(headerName, value) {
|
|
55
41
|
const trimmedHeaderName = headerName == null ? void 0 : headerName.trim();
|
|
@@ -63,106 +49,66 @@ var HCSafeCDXClient = class {
|
|
|
63
49
|
this.apiKeyHeaderName = trimmedHeaderName;
|
|
64
50
|
this.apiKeyValue = trimmedValue;
|
|
65
51
|
}
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
69
|
-
getAuthHeaders() {
|
|
70
|
-
return {
|
|
71
|
-
...this.auth.getAuthHeader(),
|
|
72
|
-
...this.getApiKeyHeader()
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
getJsonHeaders() {
|
|
76
|
-
return {
|
|
77
|
-
...this.getAuthHeaders(),
|
|
78
|
-
"Content-Type": "application/json"
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
getApiKeyHeader() {
|
|
82
|
-
if (!this.apiKeyHeaderName || !this.apiKeyValue) {
|
|
83
|
-
return {};
|
|
84
|
-
}
|
|
85
|
-
return {
|
|
86
|
-
[this.apiKeyHeaderName]: this.apiKeyValue
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
url(route, query) {
|
|
90
|
-
return buildUrl(this.host, route, query);
|
|
91
|
-
}
|
|
92
|
-
// ---------------------------------------------------------------------------
|
|
93
|
-
// Scan
|
|
94
|
-
// ---------------------------------------------------------------------------
|
|
52
|
+
// -------------------------------------------------------------------------
|
|
53
|
+
// Test Profiles
|
|
54
|
+
// -------------------------------------------------------------------------
|
|
95
55
|
/**
|
|
96
56
|
* GET gs1/:gtin
|
|
97
|
-
* Resolves
|
|
57
|
+
* Resolves the SafeCDX test profile associated with a GTIN barcode.
|
|
98
58
|
*/
|
|
99
|
-
async getTestProfileByGTIN(gtin
|
|
100
|
-
|
|
101
|
-
this.url(`gs1/${encodeURIComponent(gtin)}
|
|
102
|
-
language: language != null ? language : this.defaultLanguage
|
|
103
|
-
}),
|
|
59
|
+
async getTestProfileByGTIN(gtin) {
|
|
60
|
+
return this.http.get(
|
|
61
|
+
this.url(`gs1/${encodeURIComponent(gtin)}`),
|
|
104
62
|
this.getAuthHeaders()
|
|
105
63
|
);
|
|
106
|
-
return assertApiResponse(envelope, "gs1/:gtin");
|
|
107
|
-
}
|
|
108
|
-
// ---------------------------------------------------------------------------
|
|
109
|
-
// Test Profiles
|
|
110
|
-
// ---------------------------------------------------------------------------
|
|
111
|
-
/**
|
|
112
|
-
* POST test/profile
|
|
113
|
-
* Returns the full test profile for a given GTIN.
|
|
114
|
-
*/
|
|
115
|
-
async getTestProfile(payload) {
|
|
116
|
-
const envelope = await this.http.post(
|
|
117
|
-
this.url("test/profile"),
|
|
118
|
-
wrapData(payload),
|
|
119
|
-
this.getJsonHeaders()
|
|
120
|
-
);
|
|
121
|
-
return assertApiResponse(envelope, "test/profile");
|
|
122
64
|
}
|
|
123
65
|
/**
|
|
124
66
|
* POST test/profiles/by-account
|
|
125
|
-
* Lists
|
|
67
|
+
* Lists test profiles available to the authenticated patient account.
|
|
68
|
+
*
|
|
69
|
+
* TenantId is resolved by the backend from the authenticated patient context.
|
|
126
70
|
*/
|
|
127
|
-
async getTestProfilesByAccount(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
71
|
+
async getTestProfilesByAccount(includeRegisterTestDetails = true) {
|
|
72
|
+
const request = {
|
|
73
|
+
Data: {
|
|
74
|
+
IncludeRegisterTestDetails: includeRegisterTestDetails
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
return this.http.post(
|
|
131
78
|
this.url("test/profiles/by-account"),
|
|
132
|
-
|
|
79
|
+
request,
|
|
133
80
|
this.getJsonHeaders()
|
|
134
81
|
);
|
|
135
|
-
return assertApiResponse(envelope, "test/profiles/by-account");
|
|
136
82
|
}
|
|
137
|
-
//
|
|
138
|
-
// Upload
|
|
139
|
-
//
|
|
83
|
+
// -------------------------------------------------------------------------
|
|
84
|
+
// Image Upload
|
|
85
|
+
// -------------------------------------------------------------------------
|
|
140
86
|
/**
|
|
141
87
|
* POST upload/url
|
|
142
|
-
* Requests a pre-signed
|
|
143
|
-
* Use the returned Data.preSignedURL and Data.Metadata.UploadId in the next steps.
|
|
88
|
+
* Requests a pre-signed URL used to upload a test image.
|
|
144
89
|
*/
|
|
145
|
-
async createUploadUrl(userTestResultId, gtin, imageType) {
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
90
|
+
async createUploadUrl(userTestResultId, gtin, imageType = "jpg") {
|
|
91
|
+
const request = {
|
|
92
|
+
Data: {
|
|
93
|
+
Gtin: gtin,
|
|
94
|
+
UserTestResultId: userTestResultId,
|
|
95
|
+
Metadata: {
|
|
96
|
+
ImageType: imageType
|
|
97
|
+
}
|
|
151
98
|
}
|
|
152
99
|
};
|
|
153
|
-
|
|
100
|
+
return this.http.post(
|
|
154
101
|
this.url("upload/url"),
|
|
155
|
-
|
|
102
|
+
request,
|
|
156
103
|
this.getJsonHeaders()
|
|
157
104
|
);
|
|
158
|
-
return assertApiResponse(envelope, "upload/url");
|
|
159
105
|
}
|
|
160
106
|
/**
|
|
161
|
-
* PUT preSignedURL
|
|
162
|
-
*
|
|
107
|
+
* PUT preSignedURL
|
|
108
|
+
* Uploads the image directly to the pre-signed storage URL.
|
|
163
109
|
*
|
|
164
|
-
* This
|
|
165
|
-
*
|
|
110
|
+
* This request does not call a SafeCDX API route and does not send
|
|
111
|
+
* authenticated Health Cloud headers.
|
|
166
112
|
*/
|
|
167
113
|
async uploadImage(preSignedURL, image, contentType = "image/jpeg") {
|
|
168
114
|
const response = await fetch(preSignedURL, {
|
|
@@ -177,185 +123,154 @@ var HCSafeCDXClient = class {
|
|
|
177
123
|
try {
|
|
178
124
|
body = (await response.text()).slice(0, 500);
|
|
179
125
|
} catch {
|
|
126
|
+
body = void 0;
|
|
180
127
|
}
|
|
181
|
-
throw new SafeCDXError(
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
body
|
|
186
|
-
}
|
|
187
|
-
);
|
|
128
|
+
throw new SafeCDXError(`Image upload failed with HTTP ${response.status}.`, {
|
|
129
|
+
status: response.status,
|
|
130
|
+
body
|
|
131
|
+
});
|
|
188
132
|
}
|
|
189
133
|
}
|
|
190
|
-
//
|
|
134
|
+
// -------------------------------------------------------------------------
|
|
191
135
|
// CVML
|
|
192
|
-
//
|
|
136
|
+
// -------------------------------------------------------------------------
|
|
193
137
|
/**
|
|
194
138
|
* POST cvml/status
|
|
195
|
-
*
|
|
196
|
-
* NOTE: Returns raw service result — no ApiResponse wrapper on this endpoint.
|
|
139
|
+
* Returns the direct SafeCDX response without an outer APIResponse wrapper.
|
|
197
140
|
*/
|
|
198
141
|
async updateCvmlStatus(imageOfCaptureId, cvmlStatus) {
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
|
|
142
|
+
const request = {
|
|
143
|
+
Data: {
|
|
144
|
+
ImageOfCaptureId: imageOfCaptureId,
|
|
145
|
+
CvmlStatus: cvmlStatus
|
|
146
|
+
}
|
|
202
147
|
};
|
|
203
|
-
return this.http.post(
|
|
204
|
-
this.url("cvml/status"),
|
|
205
|
-
wrapData(payload),
|
|
206
|
-
this.getJsonHeaders()
|
|
207
|
-
);
|
|
148
|
+
return this.http.post(this.url("cvml/status"), request, this.getJsonHeaders());
|
|
208
149
|
}
|
|
209
150
|
/**
|
|
210
151
|
* GET cvml/results
|
|
211
|
-
*
|
|
212
|
-
* NOTE: Returns raw service result — no ApiResponse wrapper on this endpoint.
|
|
152
|
+
* Returns the direct SafeCDX response without an outer APIResponse wrapper.
|
|
213
153
|
*/
|
|
214
154
|
async getCvmlResults(imageOfCaptureId) {
|
|
215
|
-
return this.http.get(
|
|
216
|
-
this.url("cvml/results", {
|
|
217
|
-
imageOfCaptureId
|
|
218
|
-
}),
|
|
219
|
-
this.getAuthHeaders()
|
|
220
|
-
);
|
|
155
|
+
return this.http.get(this.url("cvml/results", { imageOfCaptureId }), this.getAuthHeaders());
|
|
221
156
|
}
|
|
222
|
-
//
|
|
157
|
+
// -------------------------------------------------------------------------
|
|
223
158
|
// Test Results
|
|
224
|
-
//
|
|
225
|
-
/**
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
this.url("test/result/pending"),
|
|
234
|
-
wrapData(payload),
|
|
235
|
-
this.getJsonHeaders()
|
|
236
|
-
);
|
|
237
|
-
return assertApiResponse(envelope, "test/result/pending");
|
|
159
|
+
// -------------------------------------------------------------------------
|
|
160
|
+
/** POST test/result/pending */
|
|
161
|
+
async getPendingResults(excludeStatus = "Invalid,Canceled") {
|
|
162
|
+
const request = {
|
|
163
|
+
Data: {
|
|
164
|
+
ExcludeStatus: excludeStatus
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
return this.http.post(this.url("test/result/pending"), request, this.getJsonHeaders());
|
|
238
168
|
}
|
|
239
|
-
/**
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
this.getJsonHeaders()
|
|
248
|
-
);
|
|
249
|
-
return assertApiResponse(envelope, "test/result/last");
|
|
169
|
+
/** POST test/result/last */
|
|
170
|
+
async getLastResults(excludeStatus = "Invalid") {
|
|
171
|
+
const request = {
|
|
172
|
+
Data: {
|
|
173
|
+
ExcludeStatus: excludeStatus
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
return this.http.post(this.url("test/result/last"), request, this.getJsonHeaders());
|
|
250
177
|
}
|
|
251
|
-
/**
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
this.getJsonHeaders()
|
|
260
|
-
);
|
|
261
|
-
return assertApiResponse(envelope, "test/result/history");
|
|
178
|
+
/** POST test/result/history */
|
|
179
|
+
async getTestHistory(excludeStatus = "Invalid") {
|
|
180
|
+
const request = {
|
|
181
|
+
Data: {
|
|
182
|
+
ExcludeStatus: excludeStatus
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
return this.http.post(this.url("test/result/history"), request, this.getJsonHeaders());
|
|
262
186
|
}
|
|
263
187
|
/**
|
|
264
188
|
* POST test/result/details
|
|
265
|
-
* Returns
|
|
266
|
-
* NOTE: Returns raw service result — no ApiResponse wrapper on this endpoint.
|
|
189
|
+
* Returns the direct SafeCDX response without an outer APIResponse wrapper.
|
|
267
190
|
*/
|
|
268
191
|
async getResultDetails(userTestResultId) {
|
|
269
|
-
const
|
|
270
|
-
|
|
192
|
+
const request = {
|
|
193
|
+
Data: {
|
|
194
|
+
UserTestResultId: userTestResultId
|
|
195
|
+
}
|
|
271
196
|
};
|
|
272
|
-
return this.http.post(
|
|
273
|
-
this.url("test/result/details"),
|
|
274
|
-
wrapData(payload),
|
|
275
|
-
this.getJsonHeaders()
|
|
276
|
-
);
|
|
197
|
+
return this.http.post(this.url("test/result/details"), request, this.getJsonHeaders());
|
|
277
198
|
}
|
|
278
|
-
/**
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
this.getJsonHeaders()
|
|
287
|
-
);
|
|
288
|
-
return assertApiResponse(envelope, "test/result/pdf");
|
|
199
|
+
/** POST test/result/pdf */
|
|
200
|
+
async getResultPdf(userTestResultId) {
|
|
201
|
+
const request = {
|
|
202
|
+
Data: {
|
|
203
|
+
UserTestResultId: userTestResultId
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
return this.http.post(this.url("test/result/pdf"), request, this.getJsonHeaders());
|
|
289
207
|
}
|
|
290
208
|
/**
|
|
291
209
|
* POST test/result/image/capture
|
|
292
|
-
* Returns the
|
|
293
|
-
* NOTE: Returns raw service result — no ApiResponse wrapper on this endpoint.
|
|
210
|
+
* Returns the direct SafeCDX response without an outer APIResponse wrapper.
|
|
294
211
|
*/
|
|
295
212
|
async getImageCaptureUrl(userTestResultId) {
|
|
296
|
-
const
|
|
297
|
-
|
|
213
|
+
const request = {
|
|
214
|
+
Data: {
|
|
215
|
+
UserTestResultId: userTestResultId
|
|
216
|
+
}
|
|
298
217
|
};
|
|
299
|
-
return this.http.post(
|
|
300
|
-
this.url("test/result/image/capture"),
|
|
301
|
-
wrapData(payload),
|
|
302
|
-
this.getJsonHeaders()
|
|
303
|
-
);
|
|
218
|
+
return this.http.post(this.url("test/result/image/capture"), request, this.getJsonHeaders());
|
|
304
219
|
}
|
|
305
|
-
|
|
306
|
-
* POST test/result/analytics
|
|
307
|
-
* Returns analytics data for test results.
|
|
308
|
-
*/
|
|
309
|
-
async getAnalytics(payload = {}) {
|
|
310
|
-
const envelope = await this.http.post(
|
|
311
|
-
this.url("test/result/analytics"),
|
|
312
|
-
wrapData(payload),
|
|
313
|
-
this.getJsonHeaders()
|
|
314
|
-
);
|
|
315
|
-
return assertApiResponse(envelope, "test/result/analytics");
|
|
316
|
-
}
|
|
317
|
-
// ---------------------------------------------------------------------------
|
|
220
|
+
// -------------------------------------------------------------------------
|
|
318
221
|
// Resume / Answers / Finalize
|
|
319
|
-
//
|
|
320
|
-
/**
|
|
321
|
-
* POST test/resume
|
|
322
|
-
* Marks a test attempt as resumed (or not).
|
|
323
|
-
*/
|
|
222
|
+
// -------------------------------------------------------------------------
|
|
223
|
+
/** POST test/resume */
|
|
324
224
|
async resumeFlow(userTestResultId, resumed = true) {
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
225
|
+
const request = {
|
|
226
|
+
Data: {
|
|
227
|
+
UserTestResultId: userTestResultId,
|
|
228
|
+
Resumed: resumed
|
|
229
|
+
}
|
|
328
230
|
};
|
|
329
|
-
|
|
330
|
-
this.url("test/resume"),
|
|
331
|
-
wrapData(payload),
|
|
332
|
-
this.getJsonHeaders()
|
|
333
|
-
);
|
|
334
|
-
return assertApiResponse(envelope, "test/resume");
|
|
231
|
+
return this.http.post(this.url("test/resume"), request, this.getJsonHeaders());
|
|
335
232
|
}
|
|
336
|
-
/**
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
this.url("test/answers"),
|
|
343
|
-
wrapData(payload),
|
|
344
|
-
this.getJsonHeaders()
|
|
345
|
-
);
|
|
346
|
-
return assertApiResponse(envelope, "test/answers");
|
|
233
|
+
/** POST test/answers */
|
|
234
|
+
async submitAnswers(submission) {
|
|
235
|
+
const request = {
|
|
236
|
+
Data: submission
|
|
237
|
+
};
|
|
238
|
+
return this.http.post(this.url("test/answers"), request, this.getJsonHeaders());
|
|
347
239
|
}
|
|
348
|
-
/**
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
240
|
+
/** POST test/finalize */
|
|
241
|
+
async finalizeTest(userTestResultId) {
|
|
242
|
+
const request = {
|
|
243
|
+
Data: {
|
|
244
|
+
UserTestResultId: userTestResultId
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
return this.http.post(this.url("test/finalize"), request, this.getJsonHeaders());
|
|
248
|
+
}
|
|
249
|
+
// -------------------------------------------------------------------------
|
|
250
|
+
// Private Helpers
|
|
251
|
+
// -------------------------------------------------------------------------
|
|
252
|
+
getAuthHeaders() {
|
|
253
|
+
return {
|
|
254
|
+
...this.loginClient.getAuthHeader(),
|
|
255
|
+
...this.getApiKeyHeader()
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
getJsonHeaders() {
|
|
259
|
+
return {
|
|
260
|
+
...this.getAuthHeaders(),
|
|
261
|
+
"Content-Type": "application/json"
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
getApiKeyHeader() {
|
|
265
|
+
if (!this.apiKeyHeaderName || !this.apiKeyValue) {
|
|
266
|
+
return {};
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
[this.apiKeyHeaderName]: this.apiKeyValue
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
url(route, query) {
|
|
273
|
+
return buildUrl(this.baseUrl, route, query);
|
|
359
274
|
}
|
|
360
275
|
};
|
|
361
276
|
|