@healthcloudai/hc-safe-cdx 0.2.2 → 0.4.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/README.md +45 -16
- package/dist/index.cjs +183 -133
- package/dist/index.d.cts +28 -60
- package/dist/index.d.ts +28 -60
- package/dist/index.js +184 -132
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -141,7 +141,7 @@ Public methods follow the documented patient workflow request shapes.
|
|
|
141
141
|
|
|
142
142
|
```ts
|
|
143
143
|
safeCdx.getTestProfileByGTIN(gtin)
|
|
144
|
-
safeCdx.
|
|
144
|
+
safeCdx.listTestProfilesByAccount(includeRegisterTestDetails?)
|
|
145
145
|
safeCdx.createUploadUrl(userTestResultId, gtin, imageType?)
|
|
146
146
|
safeCdx.updateCvmlStatus(imageOfCaptureId, cvmlStatus)
|
|
147
147
|
safeCdx.getCvmlResults(imageOfCaptureId)
|
|
@@ -159,7 +159,7 @@ safeCdx.finalizeTest(userTestResultId)
|
|
|
159
159
|
## Typical Patient Workflow
|
|
160
160
|
|
|
161
161
|
```ts
|
|
162
|
-
const profiles = await safeCdx.
|
|
162
|
+
const profiles = await safeCdx.listTestProfilesByAccount();
|
|
163
163
|
|
|
164
164
|
const profile = await safeCdx.getTestProfileByGTIN("850024942325");
|
|
165
165
|
|
|
@@ -211,23 +211,16 @@ The application is responsible for preserving workflow values returned between s
|
|
|
211
211
|
|
|
212
212
|
## Public Methods
|
|
213
213
|
|
|
214
|
-
### `
|
|
214
|
+
### `listTestProfilesByAccount(...)`
|
|
215
215
|
|
|
216
216
|
Lists test profiles available to the authenticated patient account. The backend resolves the tenant from the authenticated patient context.
|
|
217
217
|
|
|
218
218
|
#### Signature
|
|
219
219
|
|
|
220
220
|
```ts
|
|
221
|
-
safeCdx.
|
|
221
|
+
safeCdx.listTestProfilesByAccount(
|
|
222
222
|
includeRegisterTestDetails?: boolean
|
|
223
|
-
): Promise<
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
`GetTestProfilesByAccountData` is:
|
|
227
|
-
|
|
228
|
-
```ts
|
|
229
|
-
type GetTestProfilesByAccountData =
|
|
230
|
-
SafeAPIResponse<TestProfileByAccountItem[]>;
|
|
223
|
+
): Promise<SafeAPIResponse<TestProfileByAccountItem[]>>
|
|
231
224
|
```
|
|
232
225
|
|
|
233
226
|
#### Parameters
|
|
@@ -239,11 +232,11 @@ type GetTestProfilesByAccountData =
|
|
|
239
232
|
#### Usage
|
|
240
233
|
|
|
241
234
|
```ts
|
|
242
|
-
const profiles = await safeCdx.
|
|
235
|
+
const profiles = await safeCdx.listTestProfilesByAccount();
|
|
243
236
|
```
|
|
244
237
|
|
|
245
238
|
```ts
|
|
246
|
-
const profiles = await safeCdx.
|
|
239
|
+
const profiles = await safeCdx.listTestProfilesByAccount(true);
|
|
247
240
|
```
|
|
248
241
|
|
|
249
242
|
#### API request sent internally
|
|
@@ -1649,12 +1642,48 @@ const response = await safeCdx.finalizeTest(userTestResultId);
|
|
|
1649
1642
|
```
|
|
1650
1643
|
|
|
1651
1644
|
|
|
1645
|
+
## Distinct API Host
|
|
1646
|
+
|
|
1647
|
+
Safe CDX calls a **different backend service** from all other connectors. The host is `api-hcs.healthcloud-services.com` (not `api-healthcheck.healthcloud-services.com`). The URL is derived from `loginClient.getEnvironment()`:
|
|
1648
|
+
|
|
1649
|
+
| Environment | Host |
|
|
1650
|
+
| --- | --- |
|
|
1651
|
+
| `dev` | `https://dev-api-hcs.healthcloud-services.com/api/console/hcservice/safecdx` |
|
|
1652
|
+
| `uat` | `https://uat-api-hcs.healthcloud-services.com/api/console/hcservice/safecdx` |
|
|
1653
|
+
| `prod` | `https://api-hcs.healthcloud-services.com/api/console/hcservice/safecdx` |
|
|
1654
|
+
|
|
1655
|
+
`loginClient.getBaseUrl()` is **not used** by this connector — it points to the Healthcheck API, not SafeCDX.
|
|
1656
|
+
|
|
1657
|
+
---
|
|
1658
|
+
|
|
1659
|
+
## Response Type by Method
|
|
1660
|
+
|
|
1661
|
+
| Method | Returns |
|
|
1662
|
+
| --- | --- |
|
|
1663
|
+
| `getTestProfileByGTIN(gtin)` | `APIResponse<GetTestProfileByGTINData>` |
|
|
1664
|
+
| `listTestProfilesByAccount(...)` | `SafeAPIResponse<TestProfileByAccountItem[]>` |
|
|
1665
|
+
| `createUploadUrl(...)` | `APIResponse<CreateUploadUrlData>` |
|
|
1666
|
+
| `uploadImage(preSignedURL, image, contentType?)` | `Promise<void>` — direct S3 upload, no envelope |
|
|
1667
|
+
| `updateCvmlStatus(...)` | `SafeAPIResponse<UpdateCvmlStatusResponse>` |
|
|
1668
|
+
| `getCvmlResults(imageOfCaptureId)` | `SafeAPIResponse<GetCvmlResultsResponse>` |
|
|
1669
|
+
| `getPendingResults(excludeStatus?)` | `APIResponse<GetPendingResultsData>` |
|
|
1670
|
+
| `getLastResults(excludeStatus?)` | `APIResponse<GetLastResultsData>` |
|
|
1671
|
+
| `getTestHistory(excludeStatus?)` | `APIResponse<GetTestHistoryData>` |
|
|
1672
|
+
| `getResultDetails(userTestResultId)` | `SafeAPIResponse<GetResultDetailsResponse>` |
|
|
1673
|
+
| `getResultPdf(userTestResultId)` | `APIResponse<GetResultPdfData>` |
|
|
1674
|
+
| `getImageCaptureUrl(userTestResultId)` | `SafeAPIResponse<GetImageCaptureUrlResponse>` |
|
|
1675
|
+
| `resumeFlow(userTestResultId, resumed?)` | `APIResponse<ResumeFlowData>` |
|
|
1676
|
+
| `submitAnswers(submission)` | `APIResponse<SubmitAnswersData>` |
|
|
1677
|
+
| `finalizeTest(userTestResultId)` | `APIResponse<FinalizeTestData>` |
|
|
1678
|
+
|
|
1679
|
+
`uploadImage` bypasses the `HttpClient` entirely and calls the pre-signed storage URL directly using the native `fetch` API. Auth headers are **not** sent — the pre-signed URL provides its own short-lived credential.
|
|
1680
|
+
|
|
1681
|
+
---
|
|
1682
|
+
|
|
1652
1683
|
## Notes
|
|
1653
1684
|
|
|
1654
1685
|
- `HCSafeCDXClient` documents the authenticated patient workflow supported by the current client.
|
|
1655
1686
|
- The connector does not perform login; it uses `HCLoginClient` for authenticated patient headers.
|
|
1656
|
-
- Safe CDX uses its own service URL derived from `loginClient.getEnvironment()`.
|
|
1657
|
-
- The connector does not use `loginClient.getBaseUrl()` for Safe CDX API routes.
|
|
1658
1687
|
- POST payloads are wrapped internally as `{ Data: payload }`.
|
|
1659
1688
|
- Patient tenant and identity context are supplied by the backend from the authenticated request.
|
|
1660
1689
|
- API responses are returned in their endpoint-defined shape without being unwrapped or transformed by `HCSafeCDXClient`.
|
package/dist/index.cjs
CHANGED
|
@@ -20,20 +20,17 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
APIError: () => import_hc_http2.APIError,
|
|
24
|
+
ConfigError: () => import_hc_http2.ConfigError,
|
|
25
|
+
HCSafeCDXClient: () => HCSafeCDXClient,
|
|
26
|
+
HCServiceError: () => import_hc_http2.HCServiceError,
|
|
27
|
+
NetworkError: () => import_hc_http2.NetworkError,
|
|
28
|
+
ValidationError: () => import_hc_http2.ValidationError
|
|
25
29
|
});
|
|
26
30
|
module.exports = __toCommonJS(index_exports);
|
|
27
31
|
|
|
28
32
|
// src/client.ts
|
|
29
|
-
var
|
|
30
|
-
constructor(message, response) {
|
|
31
|
-
super(message);
|
|
32
|
-
this.name = "SafeCDXError";
|
|
33
|
-
this.response = response;
|
|
34
|
-
Object.setPrototypeOf(this, _SafeCDXError.prototype);
|
|
35
|
-
}
|
|
36
|
-
};
|
|
33
|
+
var import_hc_http = require("@healthcloudai/hc-http");
|
|
37
34
|
var ENV_HOST = {
|
|
38
35
|
dev: "dev-api-hcs.healthcloud-services.com",
|
|
39
36
|
uat: "uat-api-hcs.healthcloud-services.com",
|
|
@@ -43,7 +40,7 @@ var ROUTE_PREFIX = "api/console/hcservice/safecdx";
|
|
|
43
40
|
function buildSafeCdxBaseUrl(environment) {
|
|
44
41
|
const host = ENV_HOST[environment];
|
|
45
42
|
if (!host) {
|
|
46
|
-
throw new
|
|
43
|
+
throw new import_hc_http.ConfigError("Invalid Safe CDX environment.");
|
|
47
44
|
}
|
|
48
45
|
return `https://${host}/${ROUTE_PREFIX}`;
|
|
49
46
|
}
|
|
@@ -58,24 +55,12 @@ function buildUrl(baseUrl, route, query) {
|
|
|
58
55
|
}
|
|
59
56
|
return url.toString();
|
|
60
57
|
}
|
|
61
|
-
var HCSafeCDXClient = class {
|
|
58
|
+
var HCSafeCDXClient = class extends import_hc_http.HCBaseConnector {
|
|
62
59
|
constructor(httpClient, loginClient) {
|
|
63
|
-
|
|
64
|
-
this.
|
|
60
|
+
super(httpClient);
|
|
61
|
+
this.auth = loginClient;
|
|
65
62
|
this.baseUrl = buildSafeCdxBaseUrl(loginClient.getEnvironment());
|
|
66
63
|
}
|
|
67
|
-
setApiKey(headerName, value) {
|
|
68
|
-
const trimmedHeaderName = headerName == null ? void 0 : headerName.trim();
|
|
69
|
-
const trimmedValue = value == null ? void 0 : value.trim();
|
|
70
|
-
if (!trimmedHeaderName) {
|
|
71
|
-
throw new SafeCDXError("API key header name is required.");
|
|
72
|
-
}
|
|
73
|
-
if (!trimmedValue) {
|
|
74
|
-
throw new SafeCDXError("API key value is required.");
|
|
75
|
-
}
|
|
76
|
-
this.apiKeyHeaderName = trimmedHeaderName;
|
|
77
|
-
this.apiKeyValue = trimmedValue;
|
|
78
|
-
}
|
|
79
64
|
// -------------------------------------------------------------------------
|
|
80
65
|
// Test Profiles
|
|
81
66
|
// -------------------------------------------------------------------------
|
|
@@ -84,27 +69,32 @@ var HCSafeCDXClient = class {
|
|
|
84
69
|
* Resolves the SafeCDX test profile associated with a GTIN barcode.
|
|
85
70
|
*/
|
|
86
71
|
async getTestProfileByGTIN(gtin) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
72
|
+
const resolvedGtin = this.requireValue(gtin, "GTIN");
|
|
73
|
+
return this.execute(
|
|
74
|
+
"getTestProfileByGTIN",
|
|
75
|
+
() => this.http.get(
|
|
76
|
+
this.url(`gs1/${encodeURIComponent(resolvedGtin)}`),
|
|
77
|
+
this.getAuthHeaders()
|
|
78
|
+
)
|
|
90
79
|
);
|
|
91
80
|
}
|
|
92
81
|
/**
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
async
|
|
82
|
+
* POST test/profiles/by-account
|
|
83
|
+
*
|
|
84
|
+
* Returns test profiles configured for the authenticated patient's account.
|
|
85
|
+
* TenantId is resolved by the backend from the authenticated patient context.
|
|
86
|
+
*/
|
|
87
|
+
async listTestProfilesByAccount(includeRegisterTestDetails = true) {
|
|
99
88
|
const request = {
|
|
100
|
-
Data: {
|
|
101
|
-
IncludeRegisterTestDetails: includeRegisterTestDetails
|
|
102
|
-
}
|
|
89
|
+
Data: { IncludeRegisterTestDetails: includeRegisterTestDetails }
|
|
103
90
|
};
|
|
104
|
-
return this.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
91
|
+
return this.executeSafe(
|
|
92
|
+
"listTestProfilesByAccount",
|
|
93
|
+
() => this.http.post(
|
|
94
|
+
this.url("test/profiles/by-account"),
|
|
95
|
+
request,
|
|
96
|
+
this.getAuthHeaders()
|
|
97
|
+
)
|
|
108
98
|
);
|
|
109
99
|
}
|
|
110
100
|
// -------------------------------------------------------------------------
|
|
@@ -115,34 +105,42 @@ var HCSafeCDXClient = class {
|
|
|
115
105
|
* Requests a pre-signed URL used to upload a test image.
|
|
116
106
|
*/
|
|
117
107
|
async createUploadUrl(userTestResultId, gtin, imageType = "jpg") {
|
|
108
|
+
const resolvedUserTestResultId = this.requireValue(userTestResultId, "User test result ID");
|
|
109
|
+
const resolvedGtin = this.requireValue(gtin, "GTIN");
|
|
110
|
+
const resolvedImageType = this.requireValue(imageType, "Image type");
|
|
118
111
|
const request = {
|
|
119
112
|
Data: {
|
|
120
|
-
Gtin:
|
|
121
|
-
UserTestResultId:
|
|
122
|
-
Metadata: {
|
|
123
|
-
ImageType: imageType
|
|
124
|
-
}
|
|
113
|
+
Gtin: resolvedGtin,
|
|
114
|
+
UserTestResultId: resolvedUserTestResultId,
|
|
115
|
+
Metadata: { ImageType: resolvedImageType }
|
|
125
116
|
}
|
|
126
117
|
};
|
|
127
|
-
return this.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
118
|
+
return this.execute(
|
|
119
|
+
"createUploadUrl",
|
|
120
|
+
() => this.http.post(
|
|
121
|
+
this.url("upload/url"),
|
|
122
|
+
request,
|
|
123
|
+
this.getAuthHeaders()
|
|
124
|
+
)
|
|
131
125
|
);
|
|
132
126
|
}
|
|
133
127
|
/**
|
|
134
128
|
* PUT preSignedURL
|
|
135
129
|
* Uploads the image directly to the pre-signed storage URL.
|
|
136
130
|
*
|
|
137
|
-
* This request does
|
|
138
|
-
*
|
|
131
|
+
* This request does NOT call a SafeCDX API route and does NOT send
|
|
132
|
+
* Health Cloud auth headers — it targets the storage provider directly.
|
|
133
|
+
* The pre-signed URL embeds credentials; no Authorization header is needed.
|
|
139
134
|
*/
|
|
140
135
|
async uploadImage(preSignedURL, image, contentType = "image/jpeg") {
|
|
141
|
-
const
|
|
136
|
+
const resolvedPreSignedURL = this.requireValue(preSignedURL, "Pre-signed URL");
|
|
137
|
+
const resolvedContentType = this.requireValue(contentType, "Content type");
|
|
138
|
+
if (image == null) {
|
|
139
|
+
throw new import_hc_http.ValidationError({ message: "Image is required.", code: "INVALID_INPUT" });
|
|
140
|
+
}
|
|
141
|
+
const response = await fetch(resolvedPreSignedURL, {
|
|
142
142
|
method: "PUT",
|
|
143
|
-
headers: {
|
|
144
|
-
"Content-Type": contentType
|
|
145
|
-
},
|
|
143
|
+
headers: { "Content-Type": resolvedContentType },
|
|
146
144
|
body: image
|
|
147
145
|
});
|
|
148
146
|
if (!response.ok) {
|
|
@@ -152,9 +150,12 @@ var HCSafeCDXClient = class {
|
|
|
152
150
|
} catch {
|
|
153
151
|
body = void 0;
|
|
154
152
|
}
|
|
155
|
-
throw new
|
|
156
|
-
|
|
157
|
-
|
|
153
|
+
throw new import_hc_http.APIError({
|
|
154
|
+
message: `uploadImage: image upload failed with HTTP ${response.status}.`,
|
|
155
|
+
code: response.status >= 500 ? "SERVER_ERROR" : "UNKNOWN_ERROR",
|
|
156
|
+
statusCode: response.status,
|
|
157
|
+
backendMessage: body,
|
|
158
|
+
details: { status: response.status, body }
|
|
158
159
|
});
|
|
159
160
|
}
|
|
160
161
|
}
|
|
@@ -163,138 +164,188 @@ var HCSafeCDXClient = class {
|
|
|
163
164
|
// -------------------------------------------------------------------------
|
|
164
165
|
/**
|
|
165
166
|
* POST cvml/status
|
|
166
|
-
*
|
|
167
|
+
* Updates the CVML processing status for an image capture.
|
|
167
168
|
*/
|
|
168
169
|
async updateCvmlStatus(imageOfCaptureId, cvmlStatus) {
|
|
170
|
+
const resolvedImageOfCaptureId = this.requireValue(imageOfCaptureId, "Image capture ID");
|
|
171
|
+
const resolvedCvmlStatus = this.requireValue(cvmlStatus, "CVML status");
|
|
169
172
|
const request = {
|
|
170
173
|
Data: {
|
|
171
|
-
ImageOfCaptureId:
|
|
172
|
-
CvmlStatus:
|
|
174
|
+
ImageOfCaptureId: resolvedImageOfCaptureId,
|
|
175
|
+
CvmlStatus: resolvedCvmlStatus
|
|
173
176
|
}
|
|
174
177
|
};
|
|
175
|
-
return this.
|
|
178
|
+
return this.executeSafe(
|
|
179
|
+
"updateCvmlStatus",
|
|
180
|
+
() => this.http.post(this.url("cvml/status"), request, this.getAuthHeaders())
|
|
181
|
+
);
|
|
176
182
|
}
|
|
177
183
|
/**
|
|
178
184
|
* GET cvml/results
|
|
179
|
-
* Returns the
|
|
185
|
+
* Returns the CVML analysis results for an image capture.
|
|
180
186
|
*/
|
|
181
187
|
async getCvmlResults(imageOfCaptureId) {
|
|
182
|
-
|
|
188
|
+
const resolvedImageOfCaptureId = this.requireValue(imageOfCaptureId, "Image capture ID");
|
|
189
|
+
return this.executeSafe(
|
|
190
|
+
"getCvmlResults",
|
|
191
|
+
() => this.http.get(
|
|
192
|
+
this.url("cvml/results", { imageOfCaptureId: resolvedImageOfCaptureId }),
|
|
193
|
+
this.getAuthHeaders()
|
|
194
|
+
)
|
|
195
|
+
);
|
|
183
196
|
}
|
|
184
197
|
// -------------------------------------------------------------------------
|
|
185
198
|
// Test Results
|
|
186
199
|
// -------------------------------------------------------------------------
|
|
187
|
-
/** POST test/result/pending */
|
|
188
|
-
async
|
|
200
|
+
/** POST test/result/pending — returns pending test results excluding the given statuses. */
|
|
201
|
+
async listPendingResults(excludeStatus = "Invalid,Canceled") {
|
|
189
202
|
const request = {
|
|
190
|
-
Data: {
|
|
191
|
-
ExcludeStatus: excludeStatus
|
|
192
|
-
}
|
|
203
|
+
Data: { ExcludeStatus: excludeStatus }
|
|
193
204
|
};
|
|
194
|
-
return this.
|
|
205
|
+
return this.executeSafe(
|
|
206
|
+
"listPendingResults",
|
|
207
|
+
() => this.http.post(this.url("test/result/pending"), request, this.getAuthHeaders())
|
|
208
|
+
);
|
|
195
209
|
}
|
|
196
|
-
/** POST test/result/last */
|
|
210
|
+
/** POST test/result/last — returns the most recent test result. */
|
|
197
211
|
async getLastResults(excludeStatus = "Invalid") {
|
|
198
212
|
const request = {
|
|
199
|
-
Data: {
|
|
200
|
-
ExcludeStatus: excludeStatus
|
|
201
|
-
}
|
|
213
|
+
Data: { ExcludeStatus: excludeStatus }
|
|
202
214
|
};
|
|
203
|
-
return this.
|
|
215
|
+
return this.executeSafe(
|
|
216
|
+
"getLastResults",
|
|
217
|
+
() => this.http.post(this.url("test/result/last"), request, this.getAuthHeaders())
|
|
218
|
+
);
|
|
204
219
|
}
|
|
205
|
-
/** POST test/result/history */
|
|
206
|
-
async
|
|
220
|
+
/** POST test/result/history — returns all test results excluding the given statuses. */
|
|
221
|
+
async listTestHistory(excludeStatus = "Invalid") {
|
|
207
222
|
const request = {
|
|
208
|
-
Data: {
|
|
209
|
-
ExcludeStatus: excludeStatus
|
|
210
|
-
}
|
|
223
|
+
Data: { ExcludeStatus: excludeStatus }
|
|
211
224
|
};
|
|
212
|
-
return this.
|
|
225
|
+
return this.executeSafe(
|
|
226
|
+
"listTestHistory",
|
|
227
|
+
() => this.http.post(this.url("test/result/history"), request, this.getAuthHeaders())
|
|
228
|
+
);
|
|
213
229
|
}
|
|
214
230
|
/**
|
|
215
231
|
* POST test/result/details
|
|
216
|
-
* Returns
|
|
232
|
+
* Returns full details for a single test result.
|
|
217
233
|
*/
|
|
218
234
|
async getResultDetails(userTestResultId) {
|
|
235
|
+
const resolvedUserTestResultId = this.requireValue(userTestResultId, "User test result ID");
|
|
219
236
|
const request = {
|
|
220
|
-
Data: {
|
|
221
|
-
UserTestResultId: userTestResultId
|
|
222
|
-
}
|
|
237
|
+
Data: { UserTestResultId: resolvedUserTestResultId }
|
|
223
238
|
};
|
|
224
|
-
return this.
|
|
239
|
+
return this.executeSafe(
|
|
240
|
+
"getResultDetails",
|
|
241
|
+
() => this.http.post(this.url("test/result/details"), request, this.getAuthHeaders())
|
|
242
|
+
);
|
|
225
243
|
}
|
|
226
|
-
/** POST test/result/pdf */
|
|
244
|
+
/** POST test/result/pdf — returns the PDF report for a test result. */
|
|
227
245
|
async getResultPdf(userTestResultId) {
|
|
246
|
+
const resolvedUserTestResultId = this.requireValue(userTestResultId, "User test result ID");
|
|
228
247
|
const request = {
|
|
229
|
-
Data: {
|
|
230
|
-
UserTestResultId: userTestResultId
|
|
231
|
-
}
|
|
248
|
+
Data: { UserTestResultId: resolvedUserTestResultId }
|
|
232
249
|
};
|
|
233
|
-
return this.
|
|
250
|
+
return this.executeSafe(
|
|
251
|
+
"getResultPdf",
|
|
252
|
+
() => this.http.post(this.url("test/result/pdf"), request, this.getAuthHeaders())
|
|
253
|
+
);
|
|
234
254
|
}
|
|
235
255
|
/**
|
|
236
256
|
* POST test/result/image/capture
|
|
237
|
-
* Returns
|
|
257
|
+
* Returns a URL to capture an image for the given test result.
|
|
238
258
|
*/
|
|
239
259
|
async getImageCaptureUrl(userTestResultId) {
|
|
260
|
+
const resolvedUserTestResultId = this.requireValue(userTestResultId, "User test result ID");
|
|
240
261
|
const request = {
|
|
241
|
-
Data: {
|
|
242
|
-
UserTestResultId: userTestResultId
|
|
243
|
-
}
|
|
262
|
+
Data: { UserTestResultId: resolvedUserTestResultId }
|
|
244
263
|
};
|
|
245
|
-
return this.
|
|
264
|
+
return this.executeSafe(
|
|
265
|
+
"getImageCaptureUrl",
|
|
266
|
+
() => this.http.post(this.url("test/result/image/capture"), request, this.getAuthHeaders())
|
|
267
|
+
);
|
|
246
268
|
}
|
|
247
269
|
// -------------------------------------------------------------------------
|
|
248
270
|
// Resume / Answers / Finalize
|
|
249
271
|
// -------------------------------------------------------------------------
|
|
250
272
|
/** POST test/resume */
|
|
251
273
|
async resumeFlow(userTestResultId, resumed = true) {
|
|
274
|
+
const resolvedUserTestResultId = this.requireValue(userTestResultId, "User test result ID");
|
|
252
275
|
const request = {
|
|
253
|
-
Data: {
|
|
254
|
-
UserTestResultId: userTestResultId,
|
|
255
|
-
Resumed: resumed
|
|
256
|
-
}
|
|
276
|
+
Data: { UserTestResultId: resolvedUserTestResultId, Resumed: resumed }
|
|
257
277
|
};
|
|
258
|
-
return this.
|
|
278
|
+
return this.executeSafe(
|
|
279
|
+
"resumeFlow",
|
|
280
|
+
() => this.http.post(this.url("test/resume"), request, this.getAuthHeaders())
|
|
281
|
+
);
|
|
259
282
|
}
|
|
260
283
|
/** POST test/answers */
|
|
261
284
|
async submitAnswers(submission) {
|
|
285
|
+
const resolvedId = this.requireValue(submission == null ? void 0 : submission.UserTestResultId, "User test result ID");
|
|
286
|
+
if (!(submission == null ? void 0 : submission.Result) || submission.Result.length === 0) {
|
|
287
|
+
throw new import_hc_http.ValidationError({
|
|
288
|
+
message: "At least one answer result is required.",
|
|
289
|
+
code: "INVALID_INPUT",
|
|
290
|
+
param: "Result"
|
|
291
|
+
});
|
|
292
|
+
}
|
|
262
293
|
const request = {
|
|
263
|
-
Data: submission
|
|
294
|
+
Data: { ...submission, UserTestResultId: resolvedId }
|
|
264
295
|
};
|
|
265
|
-
return this.
|
|
296
|
+
return this.executeSafe(
|
|
297
|
+
"submitAnswers",
|
|
298
|
+
() => this.http.post(this.url("test/answers"), request, this.getAuthHeaders())
|
|
299
|
+
);
|
|
266
300
|
}
|
|
267
301
|
/** POST test/finalize */
|
|
268
302
|
async finalizeTest(userTestResultId) {
|
|
303
|
+
const resolvedUserTestResultId = this.requireValue(userTestResultId, "User test result ID");
|
|
269
304
|
const request = {
|
|
270
|
-
Data: {
|
|
271
|
-
UserTestResultId: userTestResultId
|
|
272
|
-
}
|
|
305
|
+
Data: { UserTestResultId: resolvedUserTestResultId }
|
|
273
306
|
};
|
|
274
|
-
return this.
|
|
307
|
+
return this.execute(
|
|
308
|
+
"finalizeTest",
|
|
309
|
+
() => this.http.post(this.url("test/finalize"), request, this.getAuthHeaders())
|
|
310
|
+
);
|
|
275
311
|
}
|
|
276
312
|
// -------------------------------------------------------------------------
|
|
277
313
|
// Private Helpers
|
|
278
314
|
// -------------------------------------------------------------------------
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
315
|
+
async executeSafe(operation, request) {
|
|
316
|
+
var _a;
|
|
317
|
+
let response;
|
|
318
|
+
try {
|
|
319
|
+
response = await request();
|
|
320
|
+
} catch (err) {
|
|
321
|
+
if (err instanceof import_hc_http.APIError) throw err;
|
|
322
|
+
if (err instanceof Error) {
|
|
323
|
+
throw new import_hc_http.APIError({ message: `${operation}: ${err.message}`, code: "UNKNOWN_ERROR", details: err });
|
|
324
|
+
}
|
|
325
|
+
throw new import_hc_http.APIError({ message: `${operation}: unexpected runtime failure`, code: "UNKNOWN_ERROR", details: err });
|
|
326
|
+
}
|
|
327
|
+
if (response == null) {
|
|
328
|
+
throw new import_hc_http.APIError({ message: `${operation}: empty response received`, code: "EMPTY_RESPONSE", details: response });
|
|
329
|
+
}
|
|
330
|
+
if (!this.isSafeApiResponse(response)) {
|
|
331
|
+
throw new import_hc_http.APIError({ message: `${operation}: invalid SafeCDX response structure`, code: "INVALID_RESPONSE", details: response });
|
|
332
|
+
}
|
|
333
|
+
if (!response.success) {
|
|
334
|
+
throw new import_hc_http.HCServiceError(
|
|
335
|
+
operation,
|
|
336
|
+
(_a = response.message) != null ? _a : "Backend returned success: false",
|
|
337
|
+
response
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
return response;
|
|
284
341
|
}
|
|
285
|
-
|
|
286
|
-
return
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
};
|
|
342
|
+
isSafeApiResponse(value) {
|
|
343
|
+
if (!value || typeof value !== "object") return false;
|
|
344
|
+
const r = value;
|
|
345
|
+
return "success" in r && typeof r.success === "boolean" && "data" in r && "message" in r && "code" in r && typeof r.code === "number";
|
|
290
346
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
return {};
|
|
294
|
-
}
|
|
295
|
-
return {
|
|
296
|
-
[this.apiKeyHeaderName]: this.apiKeyValue
|
|
297
|
-
};
|
|
347
|
+
getAuthHeaders() {
|
|
348
|
+
return { ...this.getHeaders(), ...this.auth.getAuthHeader() };
|
|
298
349
|
}
|
|
299
350
|
url(route, query) {
|
|
300
351
|
return buildUrl(this.baseUrl, route, query);
|
|
@@ -302,14 +353,13 @@ var HCSafeCDXClient = class {
|
|
|
302
353
|
};
|
|
303
354
|
|
|
304
355
|
// src/errors.ts
|
|
305
|
-
var
|
|
306
|
-
constructor(message) {
|
|
307
|
-
super(message);
|
|
308
|
-
this.name = "ConfigError";
|
|
309
|
-
}
|
|
310
|
-
};
|
|
356
|
+
var import_hc_http2 = require("@healthcloudai/hc-http");
|
|
311
357
|
// Annotate the CommonJS export names for ESM import in node:
|
|
312
358
|
0 && (module.exports = {
|
|
359
|
+
APIError,
|
|
313
360
|
ConfigError,
|
|
314
|
-
HCSafeCDXClient
|
|
361
|
+
HCSafeCDXClient,
|
|
362
|
+
HCServiceError,
|
|
363
|
+
NetworkError,
|
|
364
|
+
ValidationError
|
|
315
365
|
});
|