@healthcloudai/hc-safe-cdx 0.3.0 → 0.4.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 +55 -26
- package/dist/index.cjs +84 -220
- package/dist/index.d.cts +26 -63
- package/dist/index.d.ts +26 -63
- package/dist/index.js +89 -230
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -141,13 +141,13 @@ 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)
|
|
148
|
-
safeCdx.
|
|
148
|
+
safeCdx.listPendingResults(excludeStatus?)
|
|
149
149
|
safeCdx.getLastResults(excludeStatus?)
|
|
150
|
-
safeCdx.
|
|
150
|
+
safeCdx.listTestHistory(excludeStatus?)
|
|
151
151
|
safeCdx.getResultDetails(userTestResultId)
|
|
152
152
|
safeCdx.getResultPdf(userTestResultId)
|
|
153
153
|
safeCdx.getImageCaptureUrl(userTestResultId)
|
|
@@ -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
|
|
@@ -768,14 +761,14 @@ const response = await safeCdx.getCvmlResults(imageOfCaptureId);
|
|
|
768
761
|
|
|
769
762
|
---
|
|
770
763
|
|
|
771
|
-
### `
|
|
764
|
+
### `listPendingResults(...)`
|
|
772
765
|
|
|
773
766
|
Returns pending or incomplete test results for the authenticated patient.
|
|
774
767
|
|
|
775
768
|
#### Signature
|
|
776
769
|
|
|
777
770
|
```ts
|
|
778
|
-
safeCdx.
|
|
771
|
+
safeCdx.listPendingResults(
|
|
779
772
|
excludeStatus?: string
|
|
780
773
|
): Promise<APIResponse<GetPendingResultsData>>
|
|
781
774
|
```
|
|
@@ -796,11 +789,11 @@ type GetPendingResultsData =
|
|
|
796
789
|
#### Usage
|
|
797
790
|
|
|
798
791
|
```ts
|
|
799
|
-
const pending = await safeCdx.
|
|
792
|
+
const pending = await safeCdx.listPendingResults();
|
|
800
793
|
```
|
|
801
794
|
|
|
802
795
|
```ts
|
|
803
|
-
const pending = await safeCdx.
|
|
796
|
+
const pending = await safeCdx.listPendingResults(
|
|
804
797
|
"Invalid,Canceled"
|
|
805
798
|
);
|
|
806
799
|
```
|
|
@@ -1088,14 +1081,14 @@ const lastResult = await safeCdx.getLastResults("Invalid");
|
|
|
1088
1081
|
|
|
1089
1082
|
---
|
|
1090
1083
|
|
|
1091
|
-
### `
|
|
1084
|
+
### `listTestHistory(...)`
|
|
1092
1085
|
|
|
1093
1086
|
Returns test result history for the authenticated patient.
|
|
1094
1087
|
|
|
1095
1088
|
#### Signature
|
|
1096
1089
|
|
|
1097
1090
|
```ts
|
|
1098
|
-
safeCdx.
|
|
1091
|
+
safeCdx.listTestHistory(
|
|
1099
1092
|
excludeStatus?: string
|
|
1100
1093
|
): Promise<APIResponse<GetTestHistoryData>>
|
|
1101
1094
|
```
|
|
@@ -1116,11 +1109,11 @@ type GetTestHistoryData =
|
|
|
1116
1109
|
#### Usage
|
|
1117
1110
|
|
|
1118
1111
|
```ts
|
|
1119
|
-
const history = await safeCdx.
|
|
1112
|
+
const history = await safeCdx.listTestHistory();
|
|
1120
1113
|
```
|
|
1121
1114
|
|
|
1122
1115
|
```ts
|
|
1123
|
-
const history = await safeCdx.
|
|
1116
|
+
const history = await safeCdx.listTestHistory("Invalid");
|
|
1124
1117
|
```
|
|
1125
1118
|
|
|
1126
1119
|
#### 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
|
+
| `listPendingResults(excludeStatus?)` | `APIResponse<GetPendingResultsData>` |
|
|
1670
|
+
| `getLastResults(excludeStatus?)` | `APIResponse<GetLastResultsData>` |
|
|
1671
|
+
| `listTestHistory(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
|
@@ -25,8 +25,7 @@ __export(index_exports, {
|
|
|
25
25
|
HCSafeCDXClient: () => HCSafeCDXClient,
|
|
26
26
|
HCServiceError: () => import_hc_http2.HCServiceError,
|
|
27
27
|
NetworkError: () => import_hc_http2.NetworkError,
|
|
28
|
-
ValidationError: () => import_hc_http2.ValidationError
|
|
29
|
-
errorFromHttpStatus: () => import_hc_http2.errorFromHttpStatus
|
|
28
|
+
ValidationError: () => import_hc_http2.ValidationError
|
|
30
29
|
});
|
|
31
30
|
module.exports = __toCommonJS(index_exports);
|
|
32
31
|
|
|
@@ -56,24 +55,12 @@ function buildUrl(baseUrl, route, query) {
|
|
|
56
55
|
}
|
|
57
56
|
return url.toString();
|
|
58
57
|
}
|
|
59
|
-
var HCSafeCDXClient = class {
|
|
58
|
+
var HCSafeCDXClient = class extends import_hc_http.HCBaseConnector {
|
|
60
59
|
constructor(httpClient, loginClient) {
|
|
61
|
-
|
|
62
|
-
this.
|
|
60
|
+
super(httpClient);
|
|
61
|
+
this.auth = loginClient;
|
|
63
62
|
this.baseUrl = buildSafeCdxBaseUrl(loginClient.getEnvironment());
|
|
64
63
|
}
|
|
65
|
-
setApiKey(headerName, value) {
|
|
66
|
-
const trimmedHeaderName = headerName == null ? void 0 : headerName.trim();
|
|
67
|
-
const trimmedValue = value == null ? void 0 : value.trim();
|
|
68
|
-
if (!trimmedHeaderName) {
|
|
69
|
-
throw new import_hc_http.ConfigError("API key header name is required.");
|
|
70
|
-
}
|
|
71
|
-
if (!trimmedValue) {
|
|
72
|
-
throw new import_hc_http.ConfigError("API key value is required.");
|
|
73
|
-
}
|
|
74
|
-
this.apiKeyHeaderName = trimmedHeaderName;
|
|
75
|
-
this.apiKeyValue = trimmedValue;
|
|
76
|
-
}
|
|
77
64
|
// -------------------------------------------------------------------------
|
|
78
65
|
// Test Profiles
|
|
79
66
|
// -------------------------------------------------------------------------
|
|
@@ -82,7 +69,7 @@ var HCSafeCDXClient = class {
|
|
|
82
69
|
* Resolves the SafeCDX test profile associated with a GTIN barcode.
|
|
83
70
|
*/
|
|
84
71
|
async getTestProfileByGTIN(gtin) {
|
|
85
|
-
const resolvedGtin = this.requireValue(gtin, "gtin");
|
|
72
|
+
const resolvedGtin = this.requireValue(gtin, "GTIN", "gtin");
|
|
86
73
|
return this.execute(
|
|
87
74
|
"getTestProfileByGTIN",
|
|
88
75
|
() => this.http.get(
|
|
@@ -92,23 +79,21 @@ var HCSafeCDXClient = class {
|
|
|
92
79
|
);
|
|
93
80
|
}
|
|
94
81
|
/**
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
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) {
|
|
101
88
|
const request = {
|
|
102
|
-
Data: {
|
|
103
|
-
IncludeRegisterTestDetails: includeRegisterTestDetails
|
|
104
|
-
}
|
|
89
|
+
Data: { IncludeRegisterTestDetails: includeRegisterTestDetails }
|
|
105
90
|
};
|
|
106
91
|
return this.execute(
|
|
107
|
-
"
|
|
92
|
+
"listTestProfilesByAccount",
|
|
108
93
|
() => this.http.post(
|
|
109
94
|
this.url("test/profiles/by-account"),
|
|
110
95
|
request,
|
|
111
|
-
this.
|
|
96
|
+
this.getAuthHeaders()
|
|
112
97
|
)
|
|
113
98
|
);
|
|
114
99
|
}
|
|
@@ -120,16 +105,14 @@ var HCSafeCDXClient = class {
|
|
|
120
105
|
* Requests a pre-signed URL used to upload a test image.
|
|
121
106
|
*/
|
|
122
107
|
async createUploadUrl(userTestResultId, gtin, imageType = "jpg") {
|
|
123
|
-
const resolvedUserTestResultId = this.requireValue(userTestResultId, "userTestResultId");
|
|
124
|
-
const resolvedGtin = this.requireValue(gtin, "gtin");
|
|
125
|
-
const resolvedImageType = this.requireValue(imageType, "imageType");
|
|
108
|
+
const resolvedUserTestResultId = this.requireValue(userTestResultId, "User test result ID", "userTestResultId");
|
|
109
|
+
const resolvedGtin = this.requireValue(gtin, "GTIN", "gtin");
|
|
110
|
+
const resolvedImageType = this.requireValue(imageType, "Image type", "imageType");
|
|
126
111
|
const request = {
|
|
127
112
|
Data: {
|
|
128
113
|
Gtin: resolvedGtin,
|
|
129
114
|
UserTestResultId: resolvedUserTestResultId,
|
|
130
|
-
Metadata: {
|
|
131
|
-
ImageType: resolvedImageType
|
|
132
|
-
}
|
|
115
|
+
Metadata: { ImageType: resolvedImageType }
|
|
133
116
|
}
|
|
134
117
|
};
|
|
135
118
|
return this.execute(
|
|
@@ -137,7 +120,7 @@ var HCSafeCDXClient = class {
|
|
|
137
120
|
() => this.http.post(
|
|
138
121
|
this.url("upload/url"),
|
|
139
122
|
request,
|
|
140
|
-
this.
|
|
123
|
+
this.getAuthHeaders()
|
|
141
124
|
)
|
|
142
125
|
);
|
|
143
126
|
}
|
|
@@ -145,17 +128,19 @@ var HCSafeCDXClient = class {
|
|
|
145
128
|
* PUT preSignedURL
|
|
146
129
|
* Uploads the image directly to the pre-signed storage URL.
|
|
147
130
|
*
|
|
148
|
-
* This request does
|
|
149
|
-
*
|
|
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.
|
|
150
134
|
*/
|
|
151
135
|
async uploadImage(preSignedURL, image, contentType = "image/jpeg") {
|
|
152
|
-
const resolvedPreSignedURL = this.requireValue(preSignedURL, "preSignedURL");
|
|
153
|
-
const resolvedContentType = this.requireValue(contentType, "contentType");
|
|
136
|
+
const resolvedPreSignedURL = this.requireValue(preSignedURL, "Pre-signed URL", "preSignedURL");
|
|
137
|
+
const resolvedContentType = this.requireValue(contentType, "Content type", "contentType");
|
|
138
|
+
if (image == null) {
|
|
139
|
+
throw new import_hc_http.ValidationError({ message: "Image is required.", code: "INVALID_INPUT", param: "image" });
|
|
140
|
+
}
|
|
154
141
|
const response = await fetch(resolvedPreSignedURL, {
|
|
155
142
|
method: "PUT",
|
|
156
|
-
headers: {
|
|
157
|
-
"Content-Type": resolvedContentType
|
|
158
|
-
},
|
|
143
|
+
headers: { "Content-Type": resolvedContentType },
|
|
159
144
|
body: image
|
|
160
145
|
});
|
|
161
146
|
if (!response.ok) {
|
|
@@ -165,15 +150,10 @@ var HCSafeCDXClient = class {
|
|
|
165
150
|
} catch {
|
|
166
151
|
body = void 0;
|
|
167
152
|
}
|
|
168
|
-
throw
|
|
169
|
-
|
|
170
|
-
code: response.status >= 500 ? "SERVER_ERROR" : "UNKNOWN_ERROR",
|
|
171
|
-
statusCode: response.status,
|
|
153
|
+
throw (0, import_hc_http.errorFromHttpStatus)(response.status, {
|
|
154
|
+
operationName: "uploadImage",
|
|
172
155
|
backendMessage: body,
|
|
173
|
-
details: {
|
|
174
|
-
status: response.status,
|
|
175
|
-
body
|
|
176
|
-
}
|
|
156
|
+
details: { status: response.status, body }
|
|
177
157
|
});
|
|
178
158
|
}
|
|
179
159
|
}
|
|
@@ -182,29 +162,29 @@ var HCSafeCDXClient = class {
|
|
|
182
162
|
// -------------------------------------------------------------------------
|
|
183
163
|
/**
|
|
184
164
|
* POST cvml/status
|
|
185
|
-
*
|
|
165
|
+
* Updates the CVML processing status for an image capture.
|
|
186
166
|
*/
|
|
187
167
|
async updateCvmlStatus(imageOfCaptureId, cvmlStatus) {
|
|
188
|
-
const resolvedImageOfCaptureId = this.requireValue(imageOfCaptureId, "imageOfCaptureId");
|
|
189
|
-
const resolvedCvmlStatus = this.requireValue(cvmlStatus, "cvmlStatus");
|
|
168
|
+
const resolvedImageOfCaptureId = this.requireValue(imageOfCaptureId, "Image capture ID", "imageOfCaptureId");
|
|
169
|
+
const resolvedCvmlStatus = this.requireValue(cvmlStatus, "CVML status", "cvmlStatus");
|
|
190
170
|
const request = {
|
|
191
171
|
Data: {
|
|
192
172
|
ImageOfCaptureId: resolvedImageOfCaptureId,
|
|
193
173
|
CvmlStatus: resolvedCvmlStatus
|
|
194
174
|
}
|
|
195
175
|
};
|
|
196
|
-
return this.
|
|
176
|
+
return this.execute(
|
|
197
177
|
"updateCvmlStatus",
|
|
198
|
-
() => this.http.post(this.url("cvml/status"), request, this.
|
|
178
|
+
() => this.http.post(this.url("cvml/status"), request, this.getAuthHeaders())
|
|
199
179
|
);
|
|
200
180
|
}
|
|
201
181
|
/**
|
|
202
182
|
* GET cvml/results
|
|
203
|
-
* Returns the
|
|
183
|
+
* Returns the CVML analysis results for an image capture.
|
|
204
184
|
*/
|
|
205
185
|
async getCvmlResults(imageOfCaptureId) {
|
|
206
|
-
const resolvedImageOfCaptureId = this.requireValue(imageOfCaptureId, "imageOfCaptureId");
|
|
207
|
-
return this.
|
|
186
|
+
const resolvedImageOfCaptureId = this.requireValue(imageOfCaptureId, "Image capture ID", "imageOfCaptureId");
|
|
187
|
+
return this.execute(
|
|
208
188
|
"getCvmlResults",
|
|
209
189
|
() => this.http.get(
|
|
210
190
|
this.url("cvml/results", { imageOfCaptureId: resolvedImageOfCaptureId }),
|
|
@@ -215,85 +195,73 @@ var HCSafeCDXClient = class {
|
|
|
215
195
|
// -------------------------------------------------------------------------
|
|
216
196
|
// Test Results
|
|
217
197
|
// -------------------------------------------------------------------------
|
|
218
|
-
/** POST test/result/pending */
|
|
219
|
-
async
|
|
198
|
+
/** POST test/result/pending — returns pending test results excluding the given statuses. */
|
|
199
|
+
async listPendingResults(excludeStatus = "Invalid,Canceled") {
|
|
220
200
|
const request = {
|
|
221
|
-
Data: {
|
|
222
|
-
ExcludeStatus: excludeStatus
|
|
223
|
-
}
|
|
201
|
+
Data: { ExcludeStatus: excludeStatus }
|
|
224
202
|
};
|
|
225
203
|
return this.execute(
|
|
226
|
-
"
|
|
227
|
-
() => this.http.post(this.url("test/result/pending"), request, this.
|
|
204
|
+
"listPendingResults",
|
|
205
|
+
() => this.http.post(this.url("test/result/pending"), request, this.getAuthHeaders())
|
|
228
206
|
);
|
|
229
207
|
}
|
|
230
|
-
/** POST test/result/last */
|
|
208
|
+
/** POST test/result/last — returns the most recent test result. */
|
|
231
209
|
async getLastResults(excludeStatus = "Invalid") {
|
|
232
210
|
const request = {
|
|
233
|
-
Data: {
|
|
234
|
-
ExcludeStatus: excludeStatus
|
|
235
|
-
}
|
|
211
|
+
Data: { ExcludeStatus: excludeStatus }
|
|
236
212
|
};
|
|
237
213
|
return this.execute(
|
|
238
214
|
"getLastResults",
|
|
239
|
-
() => this.http.post(this.url("test/result/last"), request, this.
|
|
215
|
+
() => this.http.post(this.url("test/result/last"), request, this.getAuthHeaders())
|
|
240
216
|
);
|
|
241
217
|
}
|
|
242
|
-
/** POST test/result/history */
|
|
243
|
-
async
|
|
218
|
+
/** POST test/result/history — returns all test results excluding the given statuses. */
|
|
219
|
+
async listTestHistory(excludeStatus = "Invalid") {
|
|
244
220
|
const request = {
|
|
245
|
-
Data: {
|
|
246
|
-
ExcludeStatus: excludeStatus
|
|
247
|
-
}
|
|
221
|
+
Data: { ExcludeStatus: excludeStatus }
|
|
248
222
|
};
|
|
249
223
|
return this.execute(
|
|
250
|
-
"
|
|
251
|
-
() => this.http.post(this.url("test/result/history"), request, this.
|
|
224
|
+
"listTestHistory",
|
|
225
|
+
() => this.http.post(this.url("test/result/history"), request, this.getAuthHeaders())
|
|
252
226
|
);
|
|
253
227
|
}
|
|
254
228
|
/**
|
|
255
229
|
* POST test/result/details
|
|
256
|
-
* Returns
|
|
230
|
+
* Returns full details for a single test result.
|
|
257
231
|
*/
|
|
258
232
|
async getResultDetails(userTestResultId) {
|
|
259
|
-
const resolvedUserTestResultId = this.requireValue(userTestResultId, "userTestResultId");
|
|
233
|
+
const resolvedUserTestResultId = this.requireValue(userTestResultId, "User test result ID", "userTestResultId");
|
|
260
234
|
const request = {
|
|
261
|
-
Data: {
|
|
262
|
-
UserTestResultId: resolvedUserTestResultId
|
|
263
|
-
}
|
|
235
|
+
Data: { UserTestResultId: resolvedUserTestResultId }
|
|
264
236
|
};
|
|
265
|
-
return this.
|
|
237
|
+
return this.execute(
|
|
266
238
|
"getResultDetails",
|
|
267
|
-
() => this.http.post(this.url("test/result/details"), request, this.
|
|
239
|
+
() => this.http.post(this.url("test/result/details"), request, this.getAuthHeaders())
|
|
268
240
|
);
|
|
269
241
|
}
|
|
270
|
-
/** POST test/result/pdf */
|
|
242
|
+
/** POST test/result/pdf — returns the PDF report for a test result. */
|
|
271
243
|
async getResultPdf(userTestResultId) {
|
|
272
|
-
const resolvedUserTestResultId = this.requireValue(userTestResultId, "userTestResultId");
|
|
244
|
+
const resolvedUserTestResultId = this.requireValue(userTestResultId, "User test result ID", "userTestResultId");
|
|
273
245
|
const request = {
|
|
274
|
-
Data: {
|
|
275
|
-
UserTestResultId: resolvedUserTestResultId
|
|
276
|
-
}
|
|
246
|
+
Data: { UserTestResultId: resolvedUserTestResultId }
|
|
277
247
|
};
|
|
278
248
|
return this.execute(
|
|
279
249
|
"getResultPdf",
|
|
280
|
-
() => this.http.post(this.url("test/result/pdf"), request, this.
|
|
250
|
+
() => this.http.post(this.url("test/result/pdf"), request, this.getAuthHeaders())
|
|
281
251
|
);
|
|
282
252
|
}
|
|
283
253
|
/**
|
|
284
254
|
* POST test/result/image/capture
|
|
285
|
-
* Returns
|
|
255
|
+
* Returns a URL to capture an image for the given test result.
|
|
286
256
|
*/
|
|
287
257
|
async getImageCaptureUrl(userTestResultId) {
|
|
288
|
-
const resolvedUserTestResultId = this.requireValue(userTestResultId, "userTestResultId");
|
|
258
|
+
const resolvedUserTestResultId = this.requireValue(userTestResultId, "User test result ID", "userTestResultId");
|
|
289
259
|
const request = {
|
|
290
|
-
Data: {
|
|
291
|
-
UserTestResultId: resolvedUserTestResultId
|
|
292
|
-
}
|
|
260
|
+
Data: { UserTestResultId: resolvedUserTestResultId }
|
|
293
261
|
};
|
|
294
|
-
return this.
|
|
262
|
+
return this.execute(
|
|
295
263
|
"getImageCaptureUrl",
|
|
296
|
-
() => this.http.post(this.url("test/result/image/capture"), request, this.
|
|
264
|
+
() => this.http.post(this.url("test/result/image/capture"), request, this.getAuthHeaders())
|
|
297
265
|
);
|
|
298
266
|
}
|
|
299
267
|
// -------------------------------------------------------------------------
|
|
@@ -301,152 +269,49 @@ var HCSafeCDXClient = class {
|
|
|
301
269
|
// -------------------------------------------------------------------------
|
|
302
270
|
/** POST test/resume */
|
|
303
271
|
async resumeFlow(userTestResultId, resumed = true) {
|
|
304
|
-
const resolvedUserTestResultId = this.requireValue(userTestResultId, "userTestResultId");
|
|
272
|
+
const resolvedUserTestResultId = this.requireValue(userTestResultId, "User test result ID", "userTestResultId");
|
|
305
273
|
const request = {
|
|
306
|
-
Data: {
|
|
307
|
-
UserTestResultId: resolvedUserTestResultId,
|
|
308
|
-
Resumed: resumed
|
|
309
|
-
}
|
|
274
|
+
Data: { UserTestResultId: resolvedUserTestResultId, Resumed: resumed }
|
|
310
275
|
};
|
|
311
276
|
return this.execute(
|
|
312
277
|
"resumeFlow",
|
|
313
|
-
() => this.http.post(this.url("test/resume"), request, this.
|
|
278
|
+
() => this.http.post(this.url("test/resume"), request, this.getAuthHeaders())
|
|
314
279
|
);
|
|
315
280
|
}
|
|
316
281
|
/** POST test/answers */
|
|
317
282
|
async submitAnswers(submission) {
|
|
283
|
+
const resolvedId = this.requireValue(submission == null ? void 0 : submission.UserTestResultId, "User test result ID", "UserTestResultId");
|
|
284
|
+
if (!(submission == null ? void 0 : submission.Result) || submission.Result.length === 0) {
|
|
285
|
+
throw new import_hc_http.ValidationError({
|
|
286
|
+
message: "At least one answer result is required.",
|
|
287
|
+
code: "INVALID_INPUT",
|
|
288
|
+
param: "Result"
|
|
289
|
+
});
|
|
290
|
+
}
|
|
318
291
|
const request = {
|
|
319
|
-
Data: submission
|
|
292
|
+
Data: { ...submission, UserTestResultId: resolvedId }
|
|
320
293
|
};
|
|
321
294
|
return this.execute(
|
|
322
295
|
"submitAnswers",
|
|
323
|
-
() => this.http.post(this.url("test/answers"), request, this.
|
|
296
|
+
() => this.http.post(this.url("test/answers"), request, this.getAuthHeaders())
|
|
324
297
|
);
|
|
325
298
|
}
|
|
326
299
|
/** POST test/finalize */
|
|
327
300
|
async finalizeTest(userTestResultId) {
|
|
328
|
-
const resolvedUserTestResultId = this.requireValue(userTestResultId, "userTestResultId");
|
|
301
|
+
const resolvedUserTestResultId = this.requireValue(userTestResultId, "User test result ID", "userTestResultId");
|
|
329
302
|
const request = {
|
|
330
|
-
Data: {
|
|
331
|
-
UserTestResultId: resolvedUserTestResultId
|
|
332
|
-
}
|
|
303
|
+
Data: { UserTestResultId: resolvedUserTestResultId }
|
|
333
304
|
};
|
|
334
305
|
return this.execute(
|
|
335
306
|
"finalizeTest",
|
|
336
|
-
() => this.http.post(this.url("test/finalize"), request, this.
|
|
307
|
+
() => this.http.post(this.url("test/finalize"), request, this.getAuthHeaders())
|
|
337
308
|
);
|
|
338
309
|
}
|
|
339
310
|
// -------------------------------------------------------------------------
|
|
340
311
|
// Private Helpers
|
|
341
312
|
// -------------------------------------------------------------------------
|
|
342
|
-
async execute(operation, request) {
|
|
343
|
-
const response = await this.executeRaw(operation, request);
|
|
344
|
-
if (!this.isApiResponse(response)) {
|
|
345
|
-
throw new import_hc_http.APIError({
|
|
346
|
-
message: `${operation}: invalid API response structure`,
|
|
347
|
-
code: "INVALID_RESPONSE",
|
|
348
|
-
details: response
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
if (!response.IsOK) {
|
|
352
|
-
throw new import_hc_http.HCServiceError(operation, response.ErrorMessage, response);
|
|
353
|
-
}
|
|
354
|
-
return response;
|
|
355
|
-
}
|
|
356
|
-
async executeSafe(operation, request) {
|
|
357
|
-
var _a, _b;
|
|
358
|
-
const response = await this.executeRaw(operation, request);
|
|
359
|
-
if (!this.isSafeApiResponse(response)) {
|
|
360
|
-
throw new import_hc_http.APIError({
|
|
361
|
-
message: `${operation}: invalid SafeCDX response structure`,
|
|
362
|
-
code: "INVALID_RESPONSE",
|
|
363
|
-
details: response
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
if (!response.success) {
|
|
367
|
-
throw new import_hc_http.APIError({
|
|
368
|
-
message: (_a = response.message) != null ? _a : `${operation}: backend returned success: false`,
|
|
369
|
-
code: "BACKEND_FAILURE",
|
|
370
|
-
statusCode: 200,
|
|
371
|
-
backendMessage: (_b = response.message) != null ? _b : void 0,
|
|
372
|
-
details: response
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
return response;
|
|
376
|
-
}
|
|
377
|
-
async executeRaw(operation, request) {
|
|
378
|
-
let response;
|
|
379
|
-
try {
|
|
380
|
-
response = await request();
|
|
381
|
-
} catch (err) {
|
|
382
|
-
if (err instanceof import_hc_http.APIError) {
|
|
383
|
-
throw err;
|
|
384
|
-
}
|
|
385
|
-
if (err instanceof Error) {
|
|
386
|
-
throw new import_hc_http.APIError({
|
|
387
|
-
message: `${operation}: ${err.message}`,
|
|
388
|
-
code: "UNKNOWN_ERROR",
|
|
389
|
-
details: err
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
throw new import_hc_http.APIError({
|
|
393
|
-
message: `${operation}: unexpected runtime failure`,
|
|
394
|
-
code: "UNKNOWN_ERROR",
|
|
395
|
-
details: err
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
if (response == null) {
|
|
399
|
-
throw new import_hc_http.APIError({
|
|
400
|
-
message: `${operation}: empty response received`,
|
|
401
|
-
code: "EMPTY_RESPONSE",
|
|
402
|
-
details: response
|
|
403
|
-
});
|
|
404
|
-
}
|
|
405
|
-
return response;
|
|
406
|
-
}
|
|
407
|
-
isApiResponse(value) {
|
|
408
|
-
if (!value || typeof value !== "object") {
|
|
409
|
-
return false;
|
|
410
|
-
}
|
|
411
|
-
const response = value;
|
|
412
|
-
return "IsOK" in response && typeof response.IsOK === "boolean" && "Data" in response && "ErrorMessage" in response;
|
|
413
|
-
}
|
|
414
|
-
isSafeApiResponse(value) {
|
|
415
|
-
if (!value || typeof value !== "object") {
|
|
416
|
-
return false;
|
|
417
|
-
}
|
|
418
|
-
const response = value;
|
|
419
|
-
return "success" in response && typeof response.success === "boolean" && "data" in response && "message" in response && "code" in response && typeof response.code === "number";
|
|
420
|
-
}
|
|
421
|
-
requireValue(value, parameterName) {
|
|
422
|
-
const trimmedValue = value == null ? void 0 : value.trim();
|
|
423
|
-
if (!trimmedValue) {
|
|
424
|
-
throw new import_hc_http.ValidationError({
|
|
425
|
-
message: `${parameterName} is required.`,
|
|
426
|
-
code: "INVALID_INPUT"
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
return trimmedValue;
|
|
430
|
-
}
|
|
431
313
|
getAuthHeaders() {
|
|
432
|
-
return {
|
|
433
|
-
...this.loginClient.getAuthHeader(),
|
|
434
|
-
...this.getApiKeyHeader()
|
|
435
|
-
};
|
|
436
|
-
}
|
|
437
|
-
getJsonHeaders() {
|
|
438
|
-
return {
|
|
439
|
-
...this.getAuthHeaders(),
|
|
440
|
-
"Content-Type": "application/json"
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
getApiKeyHeader() {
|
|
444
|
-
if (!this.apiKeyHeaderName || !this.apiKeyValue) {
|
|
445
|
-
return {};
|
|
446
|
-
}
|
|
447
|
-
return {
|
|
448
|
-
[this.apiKeyHeaderName]: this.apiKeyValue
|
|
449
|
-
};
|
|
314
|
+
return { ...this.getHeaders(), ...this.auth.getAuthHeader() };
|
|
450
315
|
}
|
|
451
316
|
url(route, query) {
|
|
452
317
|
return buildUrl(this.baseUrl, route, query);
|
|
@@ -462,6 +327,5 @@ var import_hc_http2 = require("@healthcloudai/hc-http");
|
|
|
462
327
|
HCSafeCDXClient,
|
|
463
328
|
HCServiceError,
|
|
464
329
|
NetworkError,
|
|
465
|
-
ValidationError
|
|
466
|
-
errorFromHttpStatus
|
|
330
|
+
ValidationError
|
|
467
331
|
});
|