@crvouga/mockingbird-service-junction 0.2.0 → 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/CHANGELOG.md +13 -5
- package/README.md +222 -21
- package/dist/{chunk-MD4KYEIM.js → chunk-C2UGIPPM.js} +2 -2
- package/dist/{chunk-KKNULV7X.js → chunk-CR35KYXY.js} +120 -10
- package/dist/chunk-CR35KYXY.js.map +7 -0
- package/dist/{chunk-MA3HSUVD.js → chunk-ICINEQ7D.js} +2181 -821
- package/dist/chunk-ICINEQ7D.js.map +7 -0
- package/dist/{chunk-4C6VYRJN.js → chunk-ISDNA2TU.js} +8 -3
- package/dist/chunk-ISDNA2TU.js.map +7 -0
- package/dist/cli.js +4 -4
- package/dist/corpus.d.ts +10 -2
- package/dist/corpus.js +2 -2
- package/dist/index.d.ts +453 -56
- package/dist/index.js +34 -2
- package/dist/server.d.ts +504 -24
- package/dist/server.js +4 -4
- package/package.json +1 -1
- package/dist/chunk-4C6VYRJN.js.map +0 -7
- package/dist/chunk-KKNULV7X.js.map +0 -7
- package/dist/chunk-MA3HSUVD.js.map +0 -7
- /package/dist/{chunk-MD4KYEIM.js.map → chunk-C2UGIPPM.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SEALED_CORPUS_VERSION
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-ISDNA2TU.js";
|
|
4
4
|
|
|
5
5
|
// ../core/dist/clock.js
|
|
6
6
|
var createClock = (source = Date.now) => {
|
|
@@ -94,6 +94,11 @@ var Collection = class {
|
|
|
94
94
|
const result = this.sqlite.prepare("DELETE FROM mockingbird_records WHERE namespace = ? AND collection = ? AND id = ?").run(this.namespace, this.name, id);
|
|
95
95
|
return result.changes > 0;
|
|
96
96
|
}
|
|
97
|
+
/** How many records the collection holds, without reading them. */
|
|
98
|
+
count() {
|
|
99
|
+
const row = this.sqlite.prepare("SELECT COUNT(*) AS n FROM mockingbird_records WHERE namespace = ? AND collection = ?").get(this.namespace, this.name);
|
|
100
|
+
return Number(row?.n ?? 0);
|
|
101
|
+
}
|
|
97
102
|
list(options = {}) {
|
|
98
103
|
const rows = this.sqlite.prepare("SELECT id, seq, value FROM mockingbird_records WHERE namespace = ? AND collection = ?").all(this.namespace, this.name);
|
|
99
104
|
const out = [];
|
|
@@ -189,10 +194,10 @@ var createControlPlane = (context) => {
|
|
|
189
194
|
if (body.reset === true)
|
|
190
195
|
context.clock.reset();
|
|
191
196
|
if (body.set !== void 0) {
|
|
192
|
-
const
|
|
193
|
-
if (
|
|
197
|
+
const instant2 = parseInstant(body.set);
|
|
198
|
+
if (instant2 === void 0)
|
|
194
199
|
return adminError(400, "set: expected epoch ms or ISO-8601");
|
|
195
|
-
context.clock.set(
|
|
200
|
+
context.clock.set(instant2);
|
|
196
201
|
}
|
|
197
202
|
if (body.advance !== void 0) {
|
|
198
203
|
const delta = parseDuration(body.advance);
|
|
@@ -208,8 +213,21 @@ var createControlPlane = (context) => {
|
|
|
208
213
|
},
|
|
209
214
|
"GET /faults": () => json(200, { faults: context.faults.list() }),
|
|
210
215
|
"POST /faults": ({ body, namespace }) => {
|
|
211
|
-
if (
|
|
212
|
-
|
|
216
|
+
if (isRecord(body) && typeof body.preset === "string") {
|
|
217
|
+
if (!context.applyPreset)
|
|
218
|
+
return adminError(400, `${context.name} has no fault presets`);
|
|
219
|
+
const { preset, ...overrides } = body;
|
|
220
|
+
try {
|
|
221
|
+
return json(201, {
|
|
222
|
+
preset,
|
|
223
|
+
rules: context.applyPreset(preset, namespace, overrides)
|
|
224
|
+
});
|
|
225
|
+
} catch (error) {
|
|
226
|
+
return adminError(404, error instanceof Error ? error.message : String(error));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
if (!isRecord(body) || typeof body.status !== "number" && typeof body.delayMs !== "number" && typeof body.latencyMs !== "number" && body.drop !== true && typeof body.effect !== "string") {
|
|
230
|
+
return adminError(400, "a fault needs a numeric status, a delayMs/latencyMs, drop: true, an effect, or a preset");
|
|
213
231
|
}
|
|
214
232
|
const rule = {
|
|
215
233
|
// Scoped to the caller's namespace unless it asks for every one, so one worker's
|
|
@@ -243,6 +261,35 @@ var createControlPlane = (context) => {
|
|
|
243
261
|
return json(200, { status: "ok", id: params.id, namespace });
|
|
244
262
|
},
|
|
245
263
|
"DELETE /snapshots/:id": ({ params }) => snapshots.delete(params.id) ? json(200, { status: "ok" }) : adminError(404, `no snapshot ${params.id}`),
|
|
264
|
+
"GET /requests": ({ url, namespace }) => {
|
|
265
|
+
const status = url.searchParams.get("status");
|
|
266
|
+
const since = url.searchParams.get("since");
|
|
267
|
+
const limit = url.searchParams.get("limit");
|
|
268
|
+
const sinceMs = since === null ? void 0 : parseInstant(/^\d+$/.test(since) ? Number(since) : since);
|
|
269
|
+
if (since !== null && sinceMs === void 0) {
|
|
270
|
+
return adminError(400, "since: expected epoch ms or ISO-8601");
|
|
271
|
+
}
|
|
272
|
+
if (status !== null && !/^\d{3}$/.test(status))
|
|
273
|
+
return adminError(400, "status: expected an HTTP status");
|
|
274
|
+
if (limit !== null && !/^\d+$/.test(limit))
|
|
275
|
+
return adminError(400, "limit: expected a count");
|
|
276
|
+
const operationId = url.searchParams.get("operationId");
|
|
277
|
+
const everyNamespace = url.searchParams.get("all") === "1";
|
|
278
|
+
return json(200, {
|
|
279
|
+
size: context.journal.size,
|
|
280
|
+
requests: context.journal.list({
|
|
281
|
+
...everyNamespace ? {} : { namespace },
|
|
282
|
+
...operationId !== null ? { operationId } : {},
|
|
283
|
+
...status !== null ? { status: Number(status) } : {},
|
|
284
|
+
...sinceMs !== void 0 ? { since: sinceMs } : {},
|
|
285
|
+
...limit !== null ? { limit: Number(limit) } : {}
|
|
286
|
+
})
|
|
287
|
+
});
|
|
288
|
+
},
|
|
289
|
+
"DELETE /requests": ({ url, namespace }) => {
|
|
290
|
+
context.journal.clear(url.searchParams.get("all") === "1" ? void 0 : namespace);
|
|
291
|
+
return json(200, { status: "ok" });
|
|
292
|
+
},
|
|
246
293
|
"GET /metrics": () => json(200, context.metrics.report()),
|
|
247
294
|
"DELETE /metrics": () => {
|
|
248
295
|
context.metrics.reset();
|
|
@@ -299,6 +346,21 @@ var createControlPlane = (context) => {
|
|
|
299
346
|
};
|
|
300
347
|
};
|
|
301
348
|
|
|
349
|
+
// ../core/dist/credentials.js
|
|
350
|
+
var createCredentialRegistry = () => {
|
|
351
|
+
const map = /* @__PURE__ */ new Map();
|
|
352
|
+
return {
|
|
353
|
+
set: (credential, namespace) => {
|
|
354
|
+
map.set(credential, namespace);
|
|
355
|
+
},
|
|
356
|
+
get: (credential) => map.get(credential),
|
|
357
|
+
remove: (credential) => map.delete(credential),
|
|
358
|
+
clear: () => map.clear(),
|
|
359
|
+
entries: () => [...map].map(([credential, namespace]) => ({ credential, namespace })).sort((a, b) => a.credential.localeCompare(b.credential))
|
|
360
|
+
};
|
|
361
|
+
};
|
|
362
|
+
var maskCredential = (credential) => credential.length <= 8 ? `${credential.slice(0, 2)}\u2026` : `${credential.slice(0, 6)}\u2026${credential.slice(-2)}`;
|
|
363
|
+
|
|
302
364
|
// ../core/dist/rng.js
|
|
303
365
|
var seedFrom = (value) => {
|
|
304
366
|
let hash = 2166136261;
|
|
@@ -343,11 +405,14 @@ var matches = (rule, candidate) => {
|
|
|
343
405
|
return true;
|
|
344
406
|
};
|
|
345
407
|
var faultResponse = (rule) => {
|
|
408
|
+
const status = rule.status ?? 500;
|
|
346
409
|
const headers = { "content-type": "application/json", ...rule.headers };
|
|
347
410
|
if (typeof rule.body === "string")
|
|
348
|
-
return new Response(rule.body, { status
|
|
411
|
+
return new Response(rule.body, { status, headers });
|
|
412
|
+
if (rule.body === null)
|
|
413
|
+
return new Response(null, { status, headers: rule.headers ?? {} });
|
|
349
414
|
const body = rule.body === void 0 ? { detail: "Injected by Mockingbird" } : rule.body;
|
|
350
|
-
return new Response(JSON.stringify(body), { status
|
|
415
|
+
return new Response(JSON.stringify(body), { status, headers });
|
|
351
416
|
};
|
|
352
417
|
var createFaultRegistry = (rng = createRng(0)) => {
|
|
353
418
|
const entries = [];
|
|
@@ -373,6 +438,7 @@ var createFaultRegistry = (rng = createRng(0)) => {
|
|
|
373
438
|
entries.length = 0;
|
|
374
439
|
},
|
|
375
440
|
async take(candidate) {
|
|
441
|
+
const hits = [];
|
|
376
442
|
for (const entry of entries) {
|
|
377
443
|
if (entry.remaining === 0)
|
|
378
444
|
continue;
|
|
@@ -384,12 +450,23 @@ var createFaultRegistry = (rng = createRng(0)) => {
|
|
|
384
450
|
entry.hits++;
|
|
385
451
|
if (entry.remaining !== null)
|
|
386
452
|
entry.remaining--;
|
|
387
|
-
|
|
388
|
-
|
|
453
|
+
const delay = entry.rule.delayMs ?? entry.rule.latencyMs;
|
|
454
|
+
if (delay !== void 0 && delay > 0) {
|
|
455
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
389
456
|
}
|
|
390
|
-
|
|
457
|
+
const hit = { id: entry.rule.id };
|
|
458
|
+
if (entry.rule.effect !== void 0) {
|
|
459
|
+
hit.effect = { name: entry.rule.effect, params: entry.rule.params ?? {} };
|
|
460
|
+
}
|
|
461
|
+
if (entry.rule.drop === true)
|
|
462
|
+
hit.drop = true;
|
|
463
|
+
else if (entry.rule.status !== void 0)
|
|
464
|
+
hit.response = faultResponse(entry.rule);
|
|
465
|
+
hits.push(hit);
|
|
466
|
+
if (hit.drop || hit.response)
|
|
467
|
+
break;
|
|
391
468
|
}
|
|
392
|
-
return
|
|
469
|
+
return hits;
|
|
393
470
|
}
|
|
394
471
|
};
|
|
395
472
|
};
|
|
@@ -693,6 +770,57 @@ var IdSequence = class {
|
|
|
693
770
|
}
|
|
694
771
|
};
|
|
695
772
|
|
|
773
|
+
// ../core/dist/journal.js
|
|
774
|
+
var DEFAULT_JOURNAL_SIZE = 1e3;
|
|
775
|
+
var createJournal = (size = DEFAULT_JOURNAL_SIZE) => {
|
|
776
|
+
const capacity = Math.max(0, Math.floor(size));
|
|
777
|
+
const rings = /* @__PURE__ */ new Map();
|
|
778
|
+
let sequence = 0;
|
|
779
|
+
const order = /* @__PURE__ */ new WeakMap();
|
|
780
|
+
const inOrder = (ring) => ring.entries.length < capacity ? ring.entries : [...ring.entries.slice(ring.next), ...ring.entries.slice(0, ring.next)];
|
|
781
|
+
return {
|
|
782
|
+
size: capacity,
|
|
783
|
+
record(entry) {
|
|
784
|
+
if (capacity === 0)
|
|
785
|
+
return;
|
|
786
|
+
order.set(entry, sequence++);
|
|
787
|
+
let ring = rings.get(entry.namespace);
|
|
788
|
+
if (!ring) {
|
|
789
|
+
ring = { entries: [], next: 0 };
|
|
790
|
+
rings.set(entry.namespace, ring);
|
|
791
|
+
}
|
|
792
|
+
if (ring.entries.length < capacity)
|
|
793
|
+
ring.entries.push(entry);
|
|
794
|
+
else {
|
|
795
|
+
ring.entries[ring.next] = entry;
|
|
796
|
+
ring.next = (ring.next + 1) % capacity;
|
|
797
|
+
}
|
|
798
|
+
},
|
|
799
|
+
list(query = {}) {
|
|
800
|
+
const source = query.namespace !== void 0 ? inOrder(rings.get(query.namespace) ?? { entries: [], next: 0 }) : [...rings.values()].flatMap(inOrder).sort((a, b) => (order.get(a) ?? 0) - (order.get(b) ?? 0));
|
|
801
|
+
const matched = source.filter((entry) => (query.operationId === void 0 || entry.operationId === query.operationId) && (query.status === void 0 || entry.status === query.status) && (query.since === void 0 || Date.parse(entry.at) >= query.since));
|
|
802
|
+
return query.limit !== void 0 ? matched.slice(-Math.max(0, query.limit)) : matched;
|
|
803
|
+
},
|
|
804
|
+
clear(namespace) {
|
|
805
|
+
if (namespace === void 0)
|
|
806
|
+
rings.clear();
|
|
807
|
+
else
|
|
808
|
+
rings.delete(namespace);
|
|
809
|
+
}
|
|
810
|
+
};
|
|
811
|
+
};
|
|
812
|
+
var notes = /* @__PURE__ */ new WeakMap();
|
|
813
|
+
var annotateResponse = (response, extra) => {
|
|
814
|
+
const existing = notes.get(response);
|
|
815
|
+
notes.set(response, {
|
|
816
|
+
...existing,
|
|
817
|
+
...extra,
|
|
818
|
+
...existing?.ids || extra.ids ? { ids: { ...existing?.ids, ...extra.ids } } : {}
|
|
819
|
+
});
|
|
820
|
+
return response;
|
|
821
|
+
};
|
|
822
|
+
var responseNotes = (response) => notes.get(response);
|
|
823
|
+
|
|
696
824
|
// ../core/dist/metrics.js
|
|
697
825
|
var createMetrics = () => {
|
|
698
826
|
let requests = 0;
|
|
@@ -911,6 +1039,7 @@ var createService = (options) => {
|
|
|
911
1039
|
sqlite: options.sqlite,
|
|
912
1040
|
namespace: options.namespace,
|
|
913
1041
|
operation,
|
|
1042
|
+
document: options.document,
|
|
914
1043
|
now
|
|
915
1044
|
};
|
|
916
1045
|
const short = await options.before?.(context);
|
|
@@ -952,9 +1081,124 @@ var restoreNamespace = (sqlite, namespace, snapshot) => {
|
|
|
952
1081
|
});
|
|
953
1082
|
};
|
|
954
1083
|
|
|
1084
|
+
// ../core/dist/version.js
|
|
1085
|
+
var PACKAGE_VERSION = true ? "0.4.0" : UNRELEASED_VERSION;
|
|
1086
|
+
|
|
1087
|
+
// ../core/dist/webhooks.js
|
|
1088
|
+
var json2 = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
|
|
1089
|
+
var adminError2 = (status, message) => json2(status, { error: { type: "mockingbird_admin", message } });
|
|
1090
|
+
var isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1091
|
+
var parseEndpoint = (value) => {
|
|
1092
|
+
if (!isRecord3(value) || typeof value.url !== "string")
|
|
1093
|
+
return "each endpoint needs a url";
|
|
1094
|
+
try {
|
|
1095
|
+
new URL(value.url);
|
|
1096
|
+
} catch {
|
|
1097
|
+
return `not a URL: ${value.url}`;
|
|
1098
|
+
}
|
|
1099
|
+
const endpoint = { url: value.url };
|
|
1100
|
+
if (typeof value.id === "string")
|
|
1101
|
+
endpoint.id = value.id;
|
|
1102
|
+
if (typeof value.secret === "string")
|
|
1103
|
+
endpoint.secret = value.secret;
|
|
1104
|
+
if (typeof value.signUrl === "string")
|
|
1105
|
+
endpoint.signUrl = value.signUrl;
|
|
1106
|
+
const events = value.events ?? value.enabledEvents;
|
|
1107
|
+
if (Array.isArray(events))
|
|
1108
|
+
endpoint.events = events.map(String);
|
|
1109
|
+
if (isRecord3(value.tags)) {
|
|
1110
|
+
endpoint.tags = Object.fromEntries(Object.entries(value.tags).map(([k, v]) => [k, String(v)]));
|
|
1111
|
+
}
|
|
1112
|
+
if (typeof value.account === "string")
|
|
1113
|
+
endpoint.tags = { ...endpoint.tags, account: value.account };
|
|
1114
|
+
if (isRecord3(value.headers)) {
|
|
1115
|
+
endpoint.headers = Object.fromEntries(Object.entries(value.headers).map(([k, v]) => [k, String(v)]));
|
|
1116
|
+
}
|
|
1117
|
+
return endpoint;
|
|
1118
|
+
};
|
|
1119
|
+
var webhookAdminRoutes = (hub) => ({
|
|
1120
|
+
"GET /webhooks": ({ url, namespace }) => json2(200, {
|
|
1121
|
+
deliveries: hub.deliveries(url.searchParams.get("all") === "1" ? void 0 : namespace).filter((d) => {
|
|
1122
|
+
const type = url.searchParams.get("type");
|
|
1123
|
+
return type === null || d.type === type;
|
|
1124
|
+
})
|
|
1125
|
+
}),
|
|
1126
|
+
"GET /webhooks/events": ({ url, namespace }) => {
|
|
1127
|
+
const type = url.searchParams.get("type");
|
|
1128
|
+
return json2(200, {
|
|
1129
|
+
events: hub.messages(url.searchParams.get("all") === "1" ? void 0 : namespace).filter((m) => type === null || m.type === type).map((m) => ({ ...m, payload: parsePayload(m) }))
|
|
1130
|
+
});
|
|
1131
|
+
},
|
|
1132
|
+
"POST /webhooks/:id/replay": async ({ params }) => {
|
|
1133
|
+
const replayed = await hub.replay(params.id);
|
|
1134
|
+
return replayed ? json2(200, replayed) : adminError2(404, `no delivery ${params.id}`);
|
|
1135
|
+
},
|
|
1136
|
+
"POST /webhooks/flush": async () => {
|
|
1137
|
+
await hub.flush();
|
|
1138
|
+
return json2(200, { status: "ok" });
|
|
1139
|
+
},
|
|
1140
|
+
"POST /webhooks/faults": ({ body, namespace }) => {
|
|
1141
|
+
if (!isRecord3(body) || !["duplicate", "reorder", "drop"].includes(String(body.mode))) {
|
|
1142
|
+
return adminError2(400, "mode must be duplicate, reorder or drop");
|
|
1143
|
+
}
|
|
1144
|
+
const fault = { mode: body.mode };
|
|
1145
|
+
if (typeof body.count === "number")
|
|
1146
|
+
fault.count = body.count;
|
|
1147
|
+
hub.fault(namespace, fault);
|
|
1148
|
+
return json2(201, { namespace, ...fault });
|
|
1149
|
+
},
|
|
1150
|
+
"GET /webhook-endpoints": ({ namespace }) => json2(200, {
|
|
1151
|
+
endpoints: hub.endpoints(namespace).map(({ secret, ...rest }) => ({
|
|
1152
|
+
...rest,
|
|
1153
|
+
secret: secret ? "(set)" : null
|
|
1154
|
+
}))
|
|
1155
|
+
}),
|
|
1156
|
+
"PUT /webhook-endpoints": ({ body, namespace }) => {
|
|
1157
|
+
const list = Array.isArray(body) ? body : isRecord3(body) ? body.endpoints : void 0;
|
|
1158
|
+
if (!Array.isArray(list))
|
|
1159
|
+
return adminError2(400, "expected [{url, secret?, events?}]");
|
|
1160
|
+
const parsed = [];
|
|
1161
|
+
for (const each of list) {
|
|
1162
|
+
const endpoint = parseEndpoint(each);
|
|
1163
|
+
if (typeof endpoint === "string")
|
|
1164
|
+
return adminError2(400, endpoint);
|
|
1165
|
+
parsed.push(endpoint);
|
|
1166
|
+
}
|
|
1167
|
+
const set = hub.setEndpoints(namespace, parsed);
|
|
1168
|
+
return json2(200, { endpoints: set.map((e) => ({ ...e, secret: e.secret ? "(set)" : null })) });
|
|
1169
|
+
},
|
|
1170
|
+
"DELETE /webhook-endpoints": ({ namespace }) => {
|
|
1171
|
+
hub.setEndpoints(namespace, []);
|
|
1172
|
+
return json2(200, { status: "ok" });
|
|
1173
|
+
}
|
|
1174
|
+
});
|
|
1175
|
+
var parsePayload = (message) => {
|
|
1176
|
+
if (message.contentType.startsWith("application/json")) {
|
|
1177
|
+
try {
|
|
1178
|
+
return JSON.parse(message.body);
|
|
1179
|
+
} catch {
|
|
1180
|
+
return message.body;
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
if (message.contentType.startsWith("application/x-www-form-urlencoded")) {
|
|
1184
|
+
return Object.fromEntries(new URLSearchParams(message.body));
|
|
1185
|
+
}
|
|
1186
|
+
return message.body;
|
|
1187
|
+
};
|
|
1188
|
+
|
|
955
1189
|
// ../core/dist/runtime.js
|
|
1190
|
+
var MOCKINGBIRD_HEADER = "x-mockingbird";
|
|
956
1191
|
var DEFAULT_NAMESPACE = "default";
|
|
957
1192
|
var NAMESPACE_PATTERN = /^[A-Za-z0-9_.-]{1,64}$/;
|
|
1193
|
+
var PATH_PREFIX = /^\/ns\/([^/]+)(\/.*)?$/;
|
|
1194
|
+
var effects = /* @__PURE__ */ new WeakMap();
|
|
1195
|
+
var DroppedConnectionError = class extends TypeError {
|
|
1196
|
+
code = "MOCKINGBIRD_DROP";
|
|
1197
|
+
constructor() {
|
|
1198
|
+
super("fetch failed: connection dropped by Mockingbird fault");
|
|
1199
|
+
this.name = "TypeError";
|
|
1200
|
+
}
|
|
1201
|
+
};
|
|
958
1202
|
var operationMatcher = (document2) => {
|
|
959
1203
|
const matchers = listOperations(document2).map((operation) => ({
|
|
960
1204
|
operationId: operation.operationId,
|
|
@@ -970,7 +1214,10 @@ var createRuntime = (options) => {
|
|
|
970
1214
|
const rng = createRng(options.seed ?? 0);
|
|
971
1215
|
const faults = createFaultRegistry(createRng(options.seed ?? 0));
|
|
972
1216
|
const metrics = createMetrics();
|
|
1217
|
+
const journal = createJournal(options.journalSize ?? DEFAULT_JOURNAL_SIZE);
|
|
1218
|
+
const version = options.version ?? PACKAGE_VERSION;
|
|
973
1219
|
const instances = /* @__PURE__ */ new Map();
|
|
1220
|
+
const credentials = createCredentialRegistry();
|
|
974
1221
|
const operationIdFor = options.document ? operationMatcher(options.document) : () => void 0;
|
|
975
1222
|
const storageNamespace = (name) => name === DEFAULT_NAMESPACE ? options.name : `${options.name}:${name}`;
|
|
976
1223
|
const instance = (name = DEFAULT_NAMESPACE) => {
|
|
@@ -992,10 +1239,12 @@ var createRuntime = (options) => {
|
|
|
992
1239
|
};
|
|
993
1240
|
const reset = async (name = DEFAULT_NAMESPACE) => {
|
|
994
1241
|
if (name === "*") {
|
|
1242
|
+
options.webhooks?.clear();
|
|
995
1243
|
for (const each of instances.values())
|
|
996
1244
|
await each.reset();
|
|
997
1245
|
return;
|
|
998
1246
|
}
|
|
1247
|
+
options.webhooks?.clear(name);
|
|
999
1248
|
const target = instances.get(name);
|
|
1000
1249
|
if (target)
|
|
1001
1250
|
await target.reset();
|
|
@@ -1016,21 +1265,78 @@ var createRuntime = (options) => {
|
|
|
1016
1265
|
clock,
|
|
1017
1266
|
faults,
|
|
1018
1267
|
metrics,
|
|
1268
|
+
journal,
|
|
1019
1269
|
rng,
|
|
1270
|
+
credentials,
|
|
1271
|
+
webhooks: options.webhooks,
|
|
1272
|
+
applyPreset: (name, namespace = DEFAULT_NAMESPACE, overrides = {}) => {
|
|
1273
|
+
const preset = options.presets?.[name];
|
|
1274
|
+
if (!preset)
|
|
1275
|
+
throw new RangeError(`no fault preset ${JSON.stringify(name)}`);
|
|
1276
|
+
const added = (preset.rules ?? []).map((rule, index) => faults.add({
|
|
1277
|
+
namespace,
|
|
1278
|
+
...rule,
|
|
1279
|
+
...overrides,
|
|
1280
|
+
preset: name,
|
|
1281
|
+
id: `${overrides.id ?? name}${(preset.rules ?? []).length > 1 ? `_${index + 1}` : ""}`
|
|
1282
|
+
}));
|
|
1283
|
+
if (preset.webhook && options.webhooks) {
|
|
1284
|
+
options.webhooks.fault(namespace, {
|
|
1285
|
+
...preset.webhook,
|
|
1286
|
+
...overrides.count !== void 0 ? { count: overrides.count } : {}
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
return added;
|
|
1290
|
+
},
|
|
1020
1291
|
instance,
|
|
1021
1292
|
namespaces: () => [...instances.keys()].sort(),
|
|
1022
1293
|
reset,
|
|
1023
1294
|
snapshot,
|
|
1024
1295
|
restore,
|
|
1025
|
-
fetch: async (
|
|
1296
|
+
fetch: async (incoming) => {
|
|
1297
|
+
let request2 = incoming;
|
|
1298
|
+
const prefixed = PATH_PREFIX.exec(new URL(request2.url).pathname);
|
|
1299
|
+
if (prefixed) {
|
|
1300
|
+
const url2 = new URL(request2.url);
|
|
1301
|
+
url2.pathname = prefixed[2] ?? "/";
|
|
1302
|
+
const headers = new Headers(request2.headers);
|
|
1303
|
+
if (!headers.has(NAMESPACE_HEADER)) {
|
|
1304
|
+
headers.set(NAMESPACE_HEADER, decodeURIComponent(prefixed[1]));
|
|
1305
|
+
}
|
|
1306
|
+
const hasBody = request2.method !== "GET" && request2.method !== "HEAD";
|
|
1307
|
+
request2 = new Request(url2, {
|
|
1308
|
+
method: request2.method,
|
|
1309
|
+
headers,
|
|
1310
|
+
...hasBody ? { body: await request2.arrayBuffer() } : {},
|
|
1311
|
+
signal: request2.signal
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
let namespace = control.namespaceOf(request2);
|
|
1315
|
+
if (!request2.headers.has(NAMESPACE_HEADER) && options.credential) {
|
|
1316
|
+
const credential = options.credential(request2);
|
|
1317
|
+
const mapped = credential !== void 0 ? credentials.get(credential) : void 0;
|
|
1318
|
+
if (mapped !== void 0)
|
|
1319
|
+
namespace = mapped;
|
|
1320
|
+
}
|
|
1321
|
+
const stamp = (response2) => {
|
|
1322
|
+
const value = NAMESPACE_PATTERN.test(namespace) ? `${options.name}@${version}; ns=${namespace}` : `${options.name}@${version}`;
|
|
1323
|
+
try {
|
|
1324
|
+
response2.headers.set(MOCKINGBIRD_HEADER, value);
|
|
1325
|
+
return response2;
|
|
1326
|
+
} catch {
|
|
1327
|
+
const copy = new Response(response2.body, response2);
|
|
1328
|
+
copy.headers.set(MOCKINGBIRD_HEADER, value);
|
|
1329
|
+
return copy;
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1026
1332
|
const handled = await control.handle(request2);
|
|
1027
1333
|
if (handled)
|
|
1028
|
-
return handled;
|
|
1334
|
+
return stamp(handled);
|
|
1029
1335
|
const started = performance.now();
|
|
1030
1336
|
const url = new URL(request2.url);
|
|
1031
|
-
const namespace = control.namespaceOf(request2);
|
|
1032
1337
|
const operationId = operationIdFor(request2, url.pathname);
|
|
1033
|
-
const log = (status, faultId) => {
|
|
1338
|
+
const log = (status, faultId, response2) => {
|
|
1339
|
+
const noted = response2 ? responseNotes(response2) : void 0;
|
|
1034
1340
|
const entry = {
|
|
1035
1341
|
service: options.name,
|
|
1036
1342
|
namespace,
|
|
@@ -1040,33 +1346,44 @@ var createRuntime = (options) => {
|
|
|
1040
1346
|
status,
|
|
1041
1347
|
durationMs: Math.round((performance.now() - started) * 100) / 100,
|
|
1042
1348
|
unmatched: options.document !== void 0 && operationId === void 0,
|
|
1043
|
-
...faultId !== void 0 ? { faultId } : {}
|
|
1349
|
+
...faultId !== void 0 ? { faultId } : {},
|
|
1350
|
+
...noted?.ids && Object.keys(noted.ids).length > 0 ? { ids: noted.ids } : {},
|
|
1351
|
+
...noted?.adopted ? { adopted: true } : {}
|
|
1044
1352
|
};
|
|
1045
1353
|
metrics.record(entry);
|
|
1354
|
+
journal.record({ ...entry, at: new Date(clock.now()).toISOString() });
|
|
1046
1355
|
options.onLog?.(entry);
|
|
1047
1356
|
};
|
|
1048
1357
|
if (!NAMESPACE_PATTERN.test(namespace)) {
|
|
1049
1358
|
log(400);
|
|
1050
|
-
return new Response(JSON.stringify({
|
|
1359
|
+
return stamp(new Response(JSON.stringify({
|
|
1051
1360
|
error: {
|
|
1052
1361
|
type: "mockingbird_admin",
|
|
1053
1362
|
message: `${NAMESPACE_HEADER} must match ${NAMESPACE_PATTERN}`
|
|
1054
1363
|
}
|
|
1055
|
-
}), { status: 400, headers: { "content-type": "application/json" } });
|
|
1364
|
+
}), { status: 400, headers: { "content-type": "application/json" } }));
|
|
1056
1365
|
}
|
|
1057
|
-
const
|
|
1366
|
+
const hits = await faults.take({
|
|
1058
1367
|
operationId,
|
|
1059
1368
|
method: request2.method,
|
|
1060
1369
|
path: url.pathname,
|
|
1061
1370
|
namespace
|
|
1062
1371
|
});
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1372
|
+
const final = hits.find((hit) => hit.drop || hit.response);
|
|
1373
|
+
if (final?.drop) {
|
|
1374
|
+
log(0, final.id);
|
|
1375
|
+
throw new DroppedConnectionError();
|
|
1376
|
+
}
|
|
1377
|
+
if (final?.response) {
|
|
1378
|
+
log(final.response.status, final.id);
|
|
1379
|
+
return stamp(final.response);
|
|
1066
1380
|
}
|
|
1381
|
+
const fired = hits.filter((hit) => hit.effect !== void 0);
|
|
1382
|
+
if (fired.length > 0)
|
|
1383
|
+
effects.set(request2, fired.map((hit) => hit.effect));
|
|
1067
1384
|
const response = await instance(namespace).fetch(request2);
|
|
1068
|
-
log(response.status);
|
|
1069
|
-
return response;
|
|
1385
|
+
log(response.status, fired[0]?.id, response);
|
|
1386
|
+
return stamp(response);
|
|
1070
1387
|
}
|
|
1071
1388
|
};
|
|
1072
1389
|
const control = createControlPlane({
|
|
@@ -1075,22 +1392,90 @@ var createRuntime = (options) => {
|
|
|
1075
1392
|
clock,
|
|
1076
1393
|
faults,
|
|
1077
1394
|
metrics,
|
|
1395
|
+
journal,
|
|
1078
1396
|
defaultNamespace: DEFAULT_NAMESPACE,
|
|
1079
1397
|
namespaces: runtime.namespaces,
|
|
1080
1398
|
reset,
|
|
1081
1399
|
snapshot: (name) => snapshot(name),
|
|
1082
1400
|
restore: (name, from) => restore(from, name),
|
|
1083
1401
|
describe: options.describe ?? (() => ({})),
|
|
1084
|
-
|
|
1402
|
+
...options.presets ? {
|
|
1403
|
+
applyPreset: (name, namespace, overrides) => runtime.applyPreset(name, namespace, overrides)
|
|
1404
|
+
} : {},
|
|
1405
|
+
routes: {
|
|
1406
|
+
...credentialRoutes(credentials),
|
|
1407
|
+
...options.presets ? presetRoutes(options.presets, runtime) : {},
|
|
1408
|
+
...options.webhooks ? webhookAdminRoutes(options.webhooks) : {},
|
|
1409
|
+
...options.admin?.(runtime) ?? {}
|
|
1410
|
+
},
|
|
1085
1411
|
adminKey: options.adminKey
|
|
1086
1412
|
});
|
|
1087
1413
|
return runtime;
|
|
1088
1414
|
};
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
var document = JSON.parse(`{"openapi":"3.1.0","info":{"title":"Junction API (Mockingbird subset)","description":"Stateful mock subset of the Junction (Vital) API.\\nDocs: https://docs.junction.com/\\nAuth: https://docs.junction.com/api-details/junction-api\\nCreate user: https://docs.junction.com/api-reference/user/create-user\\nGet user: https://docs.junction.com/api-reference/user/get-user\\nDelete user: https://docs.junction.com/api-reference/user/delete-user\\nResolve user: https://docs.junction.com/api-reference/user/resolve-user\\nUpdate user: https://docs.junction.com/api-reference/user/patch-user\\nGet users: https://docs.junction.com/api-reference/user/get-users\\nCreate order: https://docs.junction.com/api-reference/lab-testing/create-order\\nGet order: https://docs.junction.com/api-reference/lab-testing/get-order\\nGet orders: https://docs.junction.com/api-reference/lab-testing/get-orders\\nGet order transaction: https://docs.junction.com/api-reference/lab-testing/order-transactions/get-order-transaction\\nGet order transaction result: https://docs.junction.com/api-reference/lab-testing/results/get-order-transaction-results\\nLab tests: https://docs.junction.com/api-reference/lab-testing/tests-paginated\\nAppointment availability: https://docs.junction.com/api-reference/lab-testing/appointments/get-appointment-availability\\nBook appointment: https://docs.junction.com/api-reference/lab-testing/appointments/book-appointment\\nGet appointment: https://docs.junction.com/api-reference/lab-testing/appointments/get-appointment\\nReschedule appointment: https://docs.junction.com/api-reference/lab-testing/appointments/reschedule-appointment\\nCancel appointment: https://docs.junction.com/api-reference/lab-testing/appointments/cancel-appointment\\nAppointment cancellation reasons: https://docs.junction.com/api-reference/lab-testing/appointments/get-appointment-cancellation-reasons\\nArea info: https://docs.junction.com/api-reference/lab-testing/service-areas/get-area-info\\nPSC info: https://docs.junction.com/api-reference/lab-testing/service-areas/get-psc-info\\nLab locations: https://docs.junction.com/api-reference/lab-testing/service-areas/get-lab-locations\\nRaw results: https://docs.junction.com/api-reference/lab-testing/results/get-lab-results\\nResult metadata: https://docs.junction.com/api-reference/lab-testing/results/get-lab-results-metadata\\nResult PDF: https://docs.junction.com/api-reference/lab-testing/results/get-lab-result-pdf\\nRequisition form PDF: https://docs.junction.com/api-reference/lab-testing/orders/get-requisition-form\\nList labs: https://docs.junction.com/api-reference/lab-testing/tests/get-labs\\nTest markers: https://docs.junction.com/api-reference/lab-testing/tests/get-markers-for-lab-test\\nOrder-set markers: https://docs.junction.com/api-reference/lab-testing/results/get-order-set-markers\\nLatest user info: https://docs.junction.com/api-reference/user/get-latest-user-info\\n","version":"0.4.758","x-mockingbird-upstream":{"docs":"https://docs.junction.com/","note":"Hand-authored subset from Junction public OpenAPI snippets in the docs."}},"servers":[{"url":"https://api.sandbox.us.junction.com"}],"security":[{"apiKeyAuth":[]}],"paths":{"/v2/user":{"post":{"operationId":"create_user_v2_user_post","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserCreateBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingUser"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientUserIDConflictResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"operationId":"get_teams_users_v2_user_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"offset","in":"query","required":false,"schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedUsersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"500":{"description":"Internal Server Error","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/v2/user/{user_id}":{"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"user","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_user_v2_user__user_id__get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingUser"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"operationId":"delete_user_v2_user__user_id__delete","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserSuccessResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"operationId":"patch_user_v2_user__user_id__patch","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPatchBody"}}}},"responses":{"204":{"description":"Successful Response"},"400":{"description":"Nothing to patch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/user/resolve/{client_user_id}":{"parameters":[{"name":"client_user_id","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"get":{"operationId":"get_user_by_client_user_id_v2_user_resolve__client_user_id__get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ClientFacingUser"},{"$ref":"#/components/schemas/PaginatedUsersResponse"}]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/user/{user_id}/info":{"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"user","missing":"00000000-0000-4000-8000-000000000000"}}}],"patch":{"operationId":"patch_user_info_v2_user__user_id__info_patch","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserInfoPatchBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserInfo"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/user/{user_id}/info/latest":{"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"user","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_latest_user_info_user_v2_user__user_id__info_latest_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserInfo"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/lab_test":{"get":{"operationId":"get_paginated_lab_tests_for_team_v3_lab_test_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabTestResourcesResponse"}}}}}}},"/v3/lab_test/lab_account":{"get":{"operationId":"get_team_lab_accounts_v3_lab_test_lab_account_get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"lab-account inventory is fixture-owned; the mock models the documented shape, filters and routing rules, not a live account inventory"}},"parameters":[{"name":"lab_account_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/LabAccountStatus"},{"type":"null"}]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetTeamLabAccountsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/lab_tests/{lab_test_id}":{"parameters":[{"name":"lab_test_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"lab_test","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_lab_test_for_team_v3_lab_tests__lab_test_id__get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingLabTest"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/lab_tests/labs":{"get":{"operationId":"get_labs_v3_lab_tests_labs_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingLab"}}}}}}}},"/v3/lab_tests/{lab_test_id}/markers":{"parameters":[{"name":"lab_test_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"lab_test","missing":"00000000-0000-4000-8000-000000000000"}}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1}},{"name":"size","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"provider_id","in":"query","required":false,"schema":{"type":"string"}}],"get":{"operationId":"get_markers_for_lab_test_v3_lab_tests__lab_test_id__markers_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMarkersResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/lab_tests/list_order_set_markers":{"post":{"operationId":"list_order_set_markers_v3_lab_tests_list_order_set_markers_post","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListOrderSetMarkersRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMarkersResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order":{"post":{"operationId":"create_order_v3_order_post","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostOrderResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"500":{"description":"Internal Server Error"}}}},"/v3/order/area/info":{"get":{"operationId":"get_area_info_v3_order_area_info_get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"central_labs presence, within_radius counts and getlabs market coverage are provider-owned geo data that a mock cannot replicate per-zip; covered by the client-parity scenario shape checks instead"}},"parameters":[{"name":"zip_code","in":"query","required":true,"schema":{"type":"string","pattern":"^\\\\d{5}(?:-?\\\\d{4})?$"}},{"name":"radius","in":"query","required":false,"schema":{"$ref":"#/components/schemas/AllowedRadius"}},{"name":"lab_account_id","in":"query","required":false,"description":"Lab Account ID to use for availability checks","schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AreaInfo"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/psc/info":{"get":{"operationId":"get_psc_info_v3_order_psc_info_get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"patient_service_centers are real site inventories per zip (thousands of sites, result-capped at 30) that a fixture mock cannot replicate per-zip; covered by the client-parity scenario shape checks instead"}},"parameters":[{"name":"zip_code","in":"query","required":true,"schema":{"type":"string","pattern":"^\\\\d{5}$"}},{"name":"lab_id","in":"query","required":true,"schema":{"type":"integer"}},{"name":"radius","in":"query","required":false,"schema":{"$ref":"#/components/schemas/AllowedRadius"}},{"name":"capabilities","in":"query","required":false,"schema":{"type":"array","items":{"$ref":"#/components/schemas/LabLocationCapability"}}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PscInfo"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/phlebotomy/appointment/availability":{"post":{"operationId":"get_phlebotomy_appointment_availability_v3_order_phlebotomy_appointment_availability_post","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"availability slots are provider-side data whose values shift between sandbox calls; covered by the mock-internal scheduling property suite instead"}},"parameters":[{"name":"start_date","in":"query","required":false,"schema":{"type":"string","format":"date"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsAddress"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentAvailabilitySlots"}}}},"400":{"description":"Availability not bookable for this address","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"No availability found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/phlebotomy/appointment/cancellation-reasons":{"get":{"operationId":"get_phlebotomy_appointment_cancellation_reason_v3_order_phlebotomy_appointment_cancellation_reasons_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingAppointmentCancellationReason"}}}}}}}},"/v3/order/psc/appointment/availability":{"post":{"operationId":"get_psc_appointment_availability_v3_order_psc_appointment_availability_post","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"availability slots are provider-side data whose values shift between sandbox calls; covered by the mock-internal scheduling property suite instead"}},"parameters":[{"name":"lab","in":"query","required":true,"schema":{"$ref":"#/components/schemas/AppointmentPscLabs"}},{"name":"start_date","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"site_codes","in":"query","required":false,"schema":{"type":"string","description":"JSON-encoded array of site codes"}},{"name":"zip_code","in":"query","required":false,"schema":{"type":"string"}},{"name":"radius","in":"query","required":false,"schema":{"$ref":"#/components/schemas/AllowedRadius"}},{"name":"allow_stale","in":"query","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentAvailabilitySlots"}}}},"400":{"description":"Availability not bookable for this address","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"No slots found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/psc/appointment/cancellation-reasons":{"get":{"operationId":"get_psc_appointment_cancellation_reason_v3_order_psc_appointment_cancellation_reasons_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingAppointmentCancellationReason"}}}}}}}},"/v3/order/{order_id}/phlebotomy/appointment":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"appointment payloads embed provider-side slot data; covered by the mock-internal scheduling property suite instead"}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingAppointment"}}}},"404":{"description":"No appointment for this order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/phlebotomy/appointment/book":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"post":{"operationId":"book_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_book_post","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"booking depends on live provider slot state; covered by the mock-internal scheduling property suite instead"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentBookingRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingAppointment"}}}},"400":{"description":"Booking failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"Order not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/phlebotomy/appointment/reschedule":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"patch":{"operationId":"reschedule_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_reschedule_patch","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"rescheduling depends on live provider slot state; covered by the mock-internal scheduling property suite instead"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentRescheduleRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingAppointment"}}}},"400":{"description":"Invalid or expired booking key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"No appointment for this order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/phlebotomy/appointment/cancel":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"patch":{"operationId":"cancel_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_cancel_patch","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"cancellation depends on live provider appointment state; covered by the mock-internal scheduling property suite instead"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhlebotomyAppointmentCancelRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingAppointment"}}}},"400":{"description":"Invalid cancellation reason","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"No appointment for this order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/psc/appointment":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_psc_appointment_v3_order__order_id__psc_appointment_get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"appointment payloads embed provider-side slot data; covered by the mock-internal scheduling property suite instead"}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingAppointment"}}}},"404":{"description":"No appointment for this order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/psc/appointment/book":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"post":{"operationId":"book_psc_appointment_v3_order__order_id__psc_appointment_book_post","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"booking depends on live provider slot state; covered by the mock-internal scheduling property suite instead"}},"parameters":[{"name":"x-idempotency-key","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PscAppointmentBookingRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingAppointment"}}}},"400":{"description":"Booking failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"Order not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/psc/appointment/reschedule":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"patch":{"operationId":"reschedule_psc_appointment_v3_order__order_id__psc_appointment_reschedule_patch","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"rescheduling depends on live provider slot state; covered by the mock-internal scheduling property suite instead"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentRescheduleRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingAppointment"}}}},"400":{"description":"Invalid or expired booking key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"No appointment for this order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/psc/appointment/cancel":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"patch":{"operationId":"cancel_psc_appointment_v3_order__order_id__psc_appointment_cancel_patch","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"cancellation depends on live provider appointment state; covered by the mock-internal scheduling property suite instead"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PscAppointmentCancelRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingAppointment"}}}},"400":{"description":"Invalid cancellation reason","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"No appointment for this order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_order_v3_order__order_id__get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingOrder"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/cancel":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"post":{"operationId":"cancel_order_v3_order__order_id__cancel_post","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostOrderResponse"}}}},"400":{"description":"Order is not in a cancellable status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/test":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"post":{"operationId":"simulate_order_v3_order__order_id__test_post","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"final_status","in":"query","required":true,"schema":{"type":"string","enum":["received.walk_in_test.ordered","received.walk_in_test.requisition_created","received.walk_in_test.requisition_bypassed","completed.walk_in_test.completed","sample_with_lab.walk_in_test.partial_results","failed.walk_in_test.sample_error","cancelled.walk_in_test.cancelled","collecting_sample.walk_in_test.appointment_pending","collecting_sample.walk_in_test.appointment_scheduled","collecting_sample.walk_in_test.appointment_cancelled","collecting_sample.walk_in_test.redraw_available","received.at_home_phlebotomy.ordered","received.at_home_phlebotomy.requisition_created","received.at_home_phlebotomy.requisition_bypassed","collecting_sample.at_home_phlebotomy.appointment_pending","collecting_sample.at_home_phlebotomy.appointment_scheduled","collecting_sample.at_home_phlebotomy.draw_completed","collecting_sample.at_home_phlebotomy.appointment_cancelled","completed.at_home_phlebotomy.completed","sample_with_lab.at_home_phlebotomy.partial_results","cancelled.at_home_phlebotomy.cancelled","failed.at_home_phlebotomy.sample_error","received.testkit.ordered","received.testkit.awaiting_registration","received.testkit.requisition_created","received.testkit.requisition_bypassed","received.testkit.registered","collecting_sample.testkit.transit_customer","collecting_sample.testkit.out_for_delivery","collecting_sample.testkit.with_customer","collecting_sample.testkit.transit_lab","sample_with_lab.testkit.delivered_to_lab","sample_with_lab.testkit.lab_processing_blocked","completed.testkit.completed","failed.testkit.failure_to_deliver_to_customer","failed.testkit.failure_to_deliver_to_lab","failed.testkit.sample_error","failed.testkit.lost","cancelled.testkit.cancelled","cancelled.testkit.do_not_process","collecting_sample.testkit.problem_in_transit_customer","collecting_sample.testkit.problem_in_transit_lab","received.on_site_collection.ordered","received.on_site_collection.requisition_created","received.on_site_collection.requisition_bypassed","sample_with_lab.on_site_collection.draw_completed","completed.on_site_collection.completed","cancelled.on_site_collection.cancelled","sample_with_lab.on_site_collection.partial_results","failed.on_site_collection.sample_error","completed.walk_in_test.corrected","completed.at_home_phlebotomy.corrected","completed.on_site_collection.corrected","completed.testkit.corrected"]}},{"name":"delay","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Seconds to defer the status transition"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulationFlags"}}}},"responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string","examples":["Success"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/result":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_result_raw_v3_order__order_id__result_get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"results carry provider-side specimen data; covered by the mock-internal results property suite instead"}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabResultsRaw"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/result/metadata":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_result_metadata_v3_order__order_id__result_metadata_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabResultsMetadata"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/result/pdf":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_result_pdf_v3_order__order_id__result_pdf_get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"PDF bytes are provider-rendered; compared by shape (byteLength) only in the scenario script"}},"responses":{"200":{"description":"Successful Response","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/requisition/pdf":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_order_requisition_pdf_v3_order__order_id__requisition_pdf_get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"PDF bytes are provider-rendered; compared by shape (byteLength) only in the scenario script"}},"responses":{"200":{"description":"Successful Response","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/orders":{"get":{"operationId":"get_orders_v3_orders_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"user_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"user","missing":"00000000-0000-4000-8000-000000000000"}}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1}},{"name":"size","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetOrdersResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order_transaction/{transaction_id}":{"parameters":[{"name":"transaction_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order_transaction","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_order_transaction_v3_order_transaction__transaction_id__get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"order transactions are not available to the sandbox team (feature not available)"}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetOrderTransactionResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order_transaction/{transaction_id}/result":{"parameters":[{"name":"transaction_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order_transaction","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_order_transaction_result_v3_order_transaction__transaction_id__result_get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"lab results are not available to the sandbox team (feature not available)"}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabResultsRaw"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"securitySchemes":{"apiKeyAuth":{"type":"apiKey","in":"header","name":"x-vital-api-key"}},"schemas":{"UserCreateBody":{"type":"object","required":["client_user_id"],"properties":{"client_user_id":{"type":"string","minLength":1,"maxLength":64,"examples":["mbr_8891","patient-1042","user.profile_52"]},"fallback_time_zone":{"type":["string","null"],"enum":["America/Los_Angeles","America/New_York","Europe/London","UTC",null]}}},"ClientFacingUser":{"type":"object","required":["user_id","team_id","client_user_id","created_on","connected_sources","fallback_time_zone"],"properties":{"user_id":{"type":"string","format":"uuid","x-mockingbird-resource":{"type":"user","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"team_id":{"type":"string","format":"uuid","x-mockingbird-volatile":{"kind":"account"}},"client_user_id":{"type":"string"},"created_on":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"connected_sources":{"type":"array","items":{"type":"object","additionalProperties":true}},"fallback_time_zone":{"type":["object","null"],"properties":{"id":{"type":"string"},"source_slug":{"type":"string"},"updated_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}}}},"fallback_birth_date":{"type":["object","null"],"properties":{"value":{"type":"string"},"source_slug":{"type":"string"},"updated_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}}}},"ingestion_start":{"type":["string","null"]},"ingestion_end":{"type":["string","null"]}}},"UserInfoPatchBody":{"type":"object","properties":{"first_name":{"type":["string","null"]},"last_name":{"type":["string","null"]},"dob":{"type":["string","null"]},"gender":{"type":["string","null"]},"phone_number":{"type":["string","null"]},"email":{"type":["string","null"]},"address":{"type":["object","null"],"additionalProperties":true}}},"UserInfo":{"type":"object","additionalProperties":true},"ClientUserIDConflictResponse":{"type":"object","required":["detail"],"properties":{"detail":{"type":"object","required":["error_type","error_message","user_id","created_on"],"properties":{"error_type":{"type":"string"},"error_message":{"type":"string"},"user_id":{"type":"string","format":"uuid","x-mockingbird-volatile":{"kind":"id"}},"created_on":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}}}}}},"UserSuccessResponse":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean"}}},"PaginatedUsersResponse":{"type":"object","required":["users","total","offset","limit"],"properties":{"users":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingUser"}},"total":{"type":"integer"},"offset":{"type":"integer"},"limit":{"type":"integer"}}},"UserPatchBody":{"type":"object","properties":{"fallback_time_zone":{"type":["string","null"]},"fallback_birth_date":{"type":["string","null"],"format":"date"},"ingestion_start":{"type":["string","null"],"format":"date"},"ingestion_end":{"type":["string","null"],"format":"date"},"client_user_id":{"type":["string","null"]}}},"LabTestCollectionMethod":{"type":"string","enum":["testkit","walk_in_test","at_home_phlebotomy","on_site_collection"]},"LabTestSampleType":{"type":"string","enum":["dried_blood_spot","arm_collector","serum","saliva","urine","stool"]},"LabTestStatus":{"type":"string","enum":["active","pending_approval","inactive"]},"MarkerType":{"type":"string","enum":["biomarker","panel"]},"ClientFacingLab":{"type":"object","required":["id","slug","name","first_line_address","city","zipcode","collection_methods","sample_types"],"properties":{"id":{"type":"integer","x-mockingbird-volatile":{"kind":"id"}},"slug":{"type":"string"},"name":{"type":"string"},"first_line_address":{"type":"string"},"city":{"type":"string"},"zipcode":{"type":"string"},"collection_methods":{"type":"array","items":{"$ref":"#/components/schemas/LabTestCollectionMethod"}},"sample_types":{"type":"array","items":{"$ref":"#/components/schemas/LabTestSampleType"}},"logo_url":{"type":["string","null"]}}},"ClientFacingMarker":{"type":"object","required":["id","name","slug"],"properties":{"id":{"type":"integer","x-mockingbird-volatile":{"kind":"id"}},"name":{"type":"string"},"slug":{"type":"string"},"description":{"type":["string","null"]},"lab_id":{"type":["integer","null"]},"provider_id":{"type":["string","null"]},"type":{"anyOf":[{"$ref":"#/components/schemas/MarkerType"},{"type":"null"}]},"unit":{"type":["string","null"]},"price":{"type":["string","null"]},"aoe":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}]},"a_la_carte_enabled":{"type":"boolean","default":false},"common_tat_days":{"type":["integer","null"]},"worst_case_tat_days":{"type":["integer","null"]},"is_orderable":{"type":["boolean","null"],"default":true}}},"ClientFacingLabTest":{"type":"object","required":["id","slug","name","sample_type","method","price","is_active","status","lab","markers"],"properties":{"id":{"type":"string","format":"uuid","x-mockingbird-resource":{"type":"lab_test","identity":true}},"slug":{"type":"string","x-mockingbird-volatile":{"kind":"token"}},"name":{"type":"string","x-mockingbird-volatile":{"kind":"token"}},"sample_type":{"$ref":"#/components/schemas/LabTestSampleType"},"method":{"$ref":"#/components/schemas/LabTestCollectionMethod"},"price":{"type":"number"},"is_active":{"type":"boolean"},"status":{"$ref":"#/components/schemas/LabTestStatus"},"fasting":{"type":"boolean","default":false},"lab":{"anyOf":[{"$ref":"#/components/schemas/ClientFacingLab"},{"type":"null"}]},"markers":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingMarker"}},{"type":"null"}]},"is_delegated":{"type":"boolean","default":false},"auto_generated":{"type":"boolean","default":false},"has_collection_instructions":{"type":["boolean","null"]},"common_tat_days":{"type":["integer","null"]},"worst_case_tat_days":{"type":["integer","null"]}}},"LabTestResourcesResponse":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingLabTest"}},"next_cursor":{"type":["string","null"]}}},"LabAccountStatus":{"type":"string","enum":["active","pending","suspended","ready_to_launch"]},"LabAccountDelegatedFlow":{"type":"string","enum":["order_delegated","result_delegated","fully_delegated","not_delegated"]},"USState":{"type":"string","enum":["AL","AK","AZ","AR","CA","CO","CT","DE","FL","GA","HI","ID","IL","IN","IA","KS","KY","LA","ME","MD","MA","MI","MN","MS","MO","MT","NE","NV","NH","NJ","NM","NY","NC","ND","OH","OK","OR","PA","RI","SC","SD","TN","TX","UT","VT","VA","WA","WV","WI","WY"]},"Labs":{"type":"string","enum":["ayumetrix","spiriplex","ussl","quest","sonora_quest","labcorp","bioreference","us_biotek","manual","sanocardio","ihd","nexus","my_uti","crl","mtl"]},"ClientFacingLabAccount":{"type":"object","required":["id","lab","status","delegated_flow","provider_account_id","allowed_billing","team_id_allowlist"],"properties":{"id":{"type":"string","format":"uuid","x-mockingbird-resource":{"type":"lab_account","identity":true}},"lab":{"$ref":"#/components/schemas/Labs"},"org_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"status":{"$ref":"#/components/schemas/LabAccountStatus"},"delegated_flow":{"$ref":"#/components/schemas/LabAccountDelegatedFlow"},"provider_account_id":{"type":"string"},"account_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"default_clinical_notes":{"anyOf":[{"type":"string"},{"type":"null"}]},"business_units":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"allowed_billing":{"type":"object","propertyNames":{"$ref":"#/components/schemas/Billing"},"additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/USState"}}},"team_id_allowlist":{"type":"array","items":{"type":"string","format":"uuid"}}}},"GetTeamLabAccountsResponse":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingLabAccount"}}}},"ClientFacingResult":{"type":"object","required":["name","slug","type"],"properties":{"name":{"type":"string"},"slug":{"type":"string"},"provider_id":{"type":["string","null"]},"type":{"$ref":"#/components/schemas/ResultType"},"value":{"type":["number","null"]},"unit":{"type":["string","null"]},"low_value":{"type":["number","null"]},"high_value":{"type":["number","null"]},"string_value":{"type":["string","null"]},"coded_value":{"type":["string","null"]},"is_abnormal":{"type":"boolean","default":false},"notes":{"type":["string","null"]}}},"GetMarkersResponse":{"type":"object","required":["markers"],"properties":{"markers":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingMarkerComplete"}},"total":{"type":["integer","null"]},"page":{"type":["integer","null"]},"size":{"type":["integer","null"]},"pages":{"type":["integer","null"]}}},"ClientFacingExpectedResult":{"type":"object","required":["id","name","slug","required"],"properties":{"id":{"type":"integer","x-mockingbird-volatile":{"kind":"id"}},"name":{"type":"string"},"slug":{"type":"string"},"lab_id":{"type":["integer","null"]},"provider_id":{"type":["string","null"]},"required":{"type":"boolean"},"loinc":{"type":["object","null"],"required":["id","name","slug","code"],"properties":{"id":{"type":"integer","x-mockingbird-volatile":{"kind":"id"}},"name":{"type":"string"},"slug":{"type":"string"},"code":{"type":"string"},"unit":{"type":["string","null"]}}}}},"ClientFacingMarkerComplete":{"type":"object","required":["id","name","slug","expected_results"],"properties":{"id":{"type":"integer","x-mockingbird-volatile":{"kind":"id"}},"name":{"type":"string"},"slug":{"type":"string"},"description":{"type":["string","null"]},"lab_id":{"type":["integer","null"]},"provider_id":{"type":["string","null"]},"type":{"anyOf":[{"$ref":"#/components/schemas/MarkerType"},{"type":"null"}]},"unit":{"type":["string","null"]},"price":{"type":["string","null"]},"a_la_carte_enabled":{"type":"boolean","default":false},"common_tat_days":{"type":["integer","null"]},"worst_case_tat_days":{"type":["integer","null"]},"is_orderable":{"type":["boolean","null"],"default":true},"expected_results":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingExpectedResult"}}}},"ListOrderSetMarkersRequest":{"type":"object","required":["order_set_id"],"properties":{"order_set_id":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order_transaction","missing":"00000000-0000-4000-8000-000000000000"}},"provider_id":{"type":["string","null"]}}},"AllowedRadius":{"type":"integer","enum":[10,20,25,50,100]},"AppointmentPscLabs":{"type":"string","enum":["quest"]},"AppointmentProvider":{"type":"string","enum":["getlabs","phlebfinders","quest"]},"AppointmentType":{"type":"string","enum":["phlebotomy","patient_service_center"]},"AppointmentStatus":{"type":"string","enum":["confirmed","pending","reserved","in_progress","completed","cancelled"]},"AppointmentEventStatus":{"type":"string","enum":["pending","reserved","scheduled","completed","cancelled","in_progress"]},"AppointmentServiceType":{"type":"string","enum":["appointment-ready","appointment-request"]},"Billing":{"type":"string","enum":["client_bill","commercial_insurance","patient_bill_passthrough","patient_bill"]},"LabLocationCapability":{"type":"string","enum":["stat","appointment_scheduling_via_junction","appointment_scheduling_with_lab"]},"LngLat":{"type":"object","required":["lng","lat"],"properties":{"lng":{"type":"number"},"lat":{"type":"number"}}},"UsAddress":{"type":"object","required":["first_line","city","state","zip_code"],"properties":{"first_line":{"type":"string","minLength":1},"second_line":{"type":["string","null"]},"city":{"type":"string","minLength":1},"state":{"type":"string","minLength":1},"zip_code":{"type":"string","minLength":1},"unit":{"type":["string","null"]}}},"AppointmentLocation":{"type":"object","required":["location","address","code","name"],"properties":{"location":{"$ref":"#/components/schemas/LngLat"},"distance":{"type":["number","null"]},"address":{"$ref":"#/components/schemas/UsAddress"},"code":{"type":"string"},"name":{"type":"string"},"iana_timezone":{"type":["string","null"]}}},"TimeSlot":{"type":"object","required":["start","end","price","is_priority","num_appointments_available"],"properties":{"booking_key":{"type":["string","null"],"x-mockingbird-resource":{"type":"booking_key","identity":true},"x-mockingbird-volatile":{"kind":"token"}},"start":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"end":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"expires_at":{"type":["string","null"],"x-mockingbird-volatile":{"kind":"timestamp"}},"price":{"type":"number"},"is_priority":{"type":"boolean"},"num_appointments_available":{"type":"integer"}}},"DaySlots":{"type":"object","required":["date","slots"],"properties":{"location":{"anyOf":[{"$ref":"#/components/schemas/AppointmentLocation"},{"type":"null"}]},"date":{"type":"string","format":"date"},"slots":{"type":"array","items":{"$ref":"#/components/schemas/TimeSlot"}}}},"AppointmentAvailabilitySlots":{"type":"object","required":["slots"],"properties":{"slots":{"type":"array","items":{"$ref":"#/components/schemas/DaySlots"}},"timezone":{"type":["string","null"]}}},"AppointmentBookingRequest":{"type":"object","required":["booking_key"],"properties":{"booking_key":{"type":"string","minLength":1,"x-mockingbird-resource-ref":{"type":"booking_key"}}}},"AppointmentRescheduleRequest":{"type":"object","required":["booking_key"],"properties":{"booking_key":{"type":"string","minLength":1,"x-mockingbird-resource-ref":{"type":"booking_key"}}}},"PscAppointmentBookingRequest":{"type":"object","required":["booking_key","site_code"],"properties":{"booking_key":{"type":"string","minLength":1,"x-mockingbird-resource-ref":{"type":"booking_key"}},"site_code":{"type":"string","minLength":1}}},"PhlebotomyAppointmentCancelRequest":{"type":"object","required":["cancellation_reason_id"],"properties":{"cancellation_reason_id":{"type":"string","minLength":1},"notes":{"type":["string","null"]}}},"PscAppointmentCancelRequest":{"type":"object","required":["cancellationReasonId"],"properties":{"cancellationReasonId":{"type":"string","minLength":1},"note":{"type":["string","null"]}}},"ClientFacingAppointmentEvent":{"type":"object","required":["created_at","status"],"properties":{"created_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"status":{"$ref":"#/components/schemas/AppointmentEventStatus"},"data":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}]}}},"ClientFacingAppointment":{"type":"object","required":["id","user_id","order_id","address","location","type","provider","status","provider_id","can_reschedule","event_status","events"],"properties":{"id":{"type":"string","format":"uuid","x-mockingbird-resource":{"type":"appointment","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"user_id":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"user"}},"order_id":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order"}},"address":{"$ref":"#/components/schemas/UsAddress"},"location":{"$ref":"#/components/schemas/LngLat"},"start_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"timestamp"}},"end_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"timestamp"}},"iana_timezone":{"type":["string","null"]},"type":{"$ref":"#/components/schemas/AppointmentType"},"provider":{"$ref":"#/components/schemas/AppointmentProvider"},"status":{"$ref":"#/components/schemas/AppointmentStatus"},"provider_id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"external_id":{"type":["string","null"],"x-mockingbird-volatile":{"kind":"id"}},"can_reschedule":{"type":"boolean"},"event_status":{"$ref":"#/components/schemas/AppointmentEventStatus"},"event_data":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}]},"order_transaction_id":{"type":["string","null"],"x-mockingbird-resource-ref":{"type":"order_transaction"}},"appointment_notes":{"type":["string","null"]},"events":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingAppointmentEvent"}}}},"ClientFacingAppointmentCancellationReason":{"type":"object","required":["id","name","is_refundable"],"properties":{"id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"name":{"type":"string"},"is_refundable":{"type":"boolean"}}},"PhlebotomyAreaInfo":{"type":"object","required":["is_served","providers"],"properties":{"is_served":{"type":"boolean"},"providers":{"type":"array","items":{"$ref":"#/components/schemas/PhlebotomyProviderInfo"}}}},"PhlebotomyProviderInfo":{"type":"object","required":["name","service_types"],"properties":{"name":{"$ref":"#/components/schemas/AppointmentProvider"},"service_types":{"type":"array","items":{"$ref":"#/components/schemas/AppointmentServiceType"}}}},"PscAreaInfoDetails":{"type":"object","required":["appointment_with_vital","within_radius","radius"],"properties":{"appointment_with_vital":{"type":"boolean"},"within_radius":{"type":"number"},"radius":{"type":"string"},"capabilities":{"type":["array","null"],"items":{"$ref":"#/components/schemas/LabLocationCapability"}}}},"PscAreaInfo":{"type":"object","required":["patient_service_centers","supported_bill_types","lab_id"],"properties":{"patient_service_centers":{"$ref":"#/components/schemas/PscAreaInfoDetails"},"supported_bill_types":{"type":"array","items":{"$ref":"#/components/schemas/Billing"}},"lab_id":{"type":"integer"}}},"AreaInfo":{"type":"object","required":["zip_code","phlebotomy","central_labs"],"properties":{"zip_code":{"type":"string"},"phlebotomy":{"$ref":"#/components/schemas/PhlebotomyAreaInfo"},"central_labs":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/PscAreaInfo"}}}},"LabLocationMetadata":{"type":"object","required":["name","state","city","zip_code","first_line"],"properties":{"name":{"type":"string"},"state":{"type":"string"},"city":{"type":"string"},"zip_code":{"type":"string"},"first_line":{"type":"string"},"second_line":{"type":["string","null"]},"phone_number":{"type":["string","null"]},"fax_number":{"type":["string","null"]},"hours":{"type":["object","null"],"additionalProperties":true}}},"ClientFacingLabLocation":{"type":"object","required":["metadata","distance","site_code","supported_bill_types","location","capabilities"],"properties":{"metadata":{"$ref":"#/components/schemas/LabLocationMetadata"},"distance":{"type":"number"},"site_code":{"type":"string"},"supported_bill_types":{"type":"array","items":{"$ref":"#/components/schemas/Billing"}},"location":{"$ref":"#/components/schemas/LngLat"},"capabilities":{"type":"array","items":{"$ref":"#/components/schemas/LabLocationCapability"}}}},"PscInfo":{"type":"object","required":["lab_id","slug","patient_service_centers"],"properties":{"lab_id":{"type":"integer"},"slug":{"type":"string"},"patient_service_centers":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingLabLocation"}}}},"OrderTopLevelStatus":{"type":"string","enum":["received","collecting_sample","sample_with_lab","completed","cancelled","failed"]},"OrderLowLevelStatus":{"type":"string","enum":["ordered","requisition_created","requisition_bypassed","transit_customer","out_for_delivery","with_customer","transit_lab","delivered_to_lab","completed","failure_to_deliver_to_lab","failure_to_deliver_to_customer","problem_in_transit_lab","problem_in_transit_customer","sample_error","appointment_scheduled","appointment_cancelled","appointment_pending","draw_completed","cancelled","lost","do_not_process","partial_results","awaiting_registration","registered","redraw_available","corrected","lab_processing_blocked"]},"OrderStatus":{"type":"string","enum":["received.walk_in_test.ordered","received.walk_in_test.requisition_created","received.walk_in_test.requisition_bypassed","completed.walk_in_test.completed","sample_with_lab.walk_in_test.partial_results","failed.walk_in_test.sample_error","cancelled.walk_in_test.cancelled","collecting_sample.walk_in_test.appointment_pending","collecting_sample.walk_in_test.appointment_scheduled","collecting_sample.walk_in_test.appointment_cancelled","collecting_sample.walk_in_test.redraw_available","received.at_home_phlebotomy.ordered","received.at_home_phlebotomy.requisition_created","received.at_home_phlebotomy.requisition_bypassed","collecting_sample.at_home_phlebotomy.appointment_pending","collecting_sample.at_home_phlebotomy.appointment_scheduled","collecting_sample.at_home_phlebotomy.draw_completed","collecting_sample.at_home_phlebotomy.appointment_cancelled","completed.at_home_phlebotomy.completed","sample_with_lab.at_home_phlebotomy.partial_results","cancelled.at_home_phlebotomy.cancelled","failed.at_home_phlebotomy.sample_error","received.testkit.ordered","received.testkit.awaiting_registration","received.testkit.requisition_created","received.testkit.requisition_bypassed","received.testkit.registered","collecting_sample.testkit.transit_customer","collecting_sample.testkit.out_for_delivery","collecting_sample.testkit.with_customer","collecting_sample.testkit.transit_lab","sample_with_lab.testkit.delivered_to_lab","sample_with_lab.testkit.lab_processing_blocked","completed.testkit.completed","failed.testkit.failure_to_deliver_to_customer","failed.testkit.failure_to_deliver_to_lab","failed.testkit.sample_error","failed.testkit.lost","cancelled.testkit.cancelled","cancelled.testkit.do_not_process","collecting_sample.testkit.problem_in_transit_customer","collecting_sample.testkit.problem_in_transit_lab","received.on_site_collection.ordered","received.on_site_collection.requisition_created","received.on_site_collection.requisition_bypassed","sample_with_lab.on_site_collection.draw_completed","completed.on_site_collection.completed","cancelled.on_site_collection.cancelled","sample_with_lab.on_site_collection.partial_results","failed.on_site_collection.sample_error","completed.walk_in_test.corrected","completed.at_home_phlebotomy.corrected","completed.on_site_collection.corrected","completed.testkit.corrected"]},"OrderStatusDetail":{"type":"string","enum":["fulfillment_error","date_of_collection_unspecified","demographic_information_unspecified","demographic_information_mismatch","sample_quantity_not_sufficient","sample_contaminated","sample_stability_exceeded","sample_hemolyzed","sample_improper_collection"]},"OrderTransactionStatus":{"type":"string","enum":["active","completed","cancelled"]},"ClientFacingOrderEvent":{"type":"object","required":["id","created_at","status"],"properties":{"id":{"type":"integer","x-mockingbird-volatile":{"kind":"opaque"}},"created_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"status":{"$ref":"#/components/schemas/OrderStatus"},"status_detail":{"anyOf":[{"$ref":"#/components/schemas/OrderStatusDetail"},{"type":"null"}]}}},"ShippingAddress":{"type":"object","required":["receiver_name","first_line","city","state","zip","country","phone_number"],"properties":{"receiver_name":{"type":"string"},"first_line":{"type":"string"},"second_line":{"type":["string","null"]},"city":{"type":"string"},"state":{"type":"string"},"zip":{"type":"string"},"country":{"type":"string"},"phone_number":{"type":"string"}}},"ClientFacingPhysician":{"type":"object","required":["first_name","last_name","npi"],"properties":{"first_name":{"type":"string"},"last_name":{"type":"string"},"npi":{"type":"string"}}},"ClientFacingShipment":{"type":"object","required":["id","outbound_tracking_number","outbound_tracking_url","inbound_tracking_number","inbound_tracking_url","outbound_courier","inbound_courier","notes"],"properties":{"id":{"type":"string","format":"uuid","x-mockingbird-volatile":{"kind":"id"}},"outbound_tracking_number":{"type":["string","null"]},"outbound_tracking_url":{"type":["string","null"]},"inbound_tracking_number":{"type":["string","null"]},"inbound_tracking_url":{"type":["string","null"]},"outbound_courier":{"type":["string","null"]},"inbound_courier":{"type":["string","null"]},"notes":{"type":["string","null"]}}},"ClientFacingTestkitOrder":{"type":"object","required":["id","shipment","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","x-mockingbird-volatile":{"kind":"id"}},"shipment":{"anyOf":[{"$ref":"#/components/schemas/ClientFacingShipment"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"opaque"}},"created_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"updated_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}}}},"ClientFacingWalkInOrder":{"type":"object","required":["id","appointment_id","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","x-mockingbird-volatile":{"kind":"id"}},"appointment_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"created_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"updated_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}}}},"ClientFacingTestKitOrderDetails":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["testkit","walk_in_test","at_home_phlebotomy","on_site_collection"]},"data":{"anyOf":[{"$ref":"#/components/schemas/ClientFacingTestkitOrder"},{"$ref":"#/components/schemas/ClientFacingWalkInOrder"},{"type":"null"}]}}},"ClientFacingPatientDetailsCompatible":{"type":"object","required":["first_name","last_name","dob","gender","phone_number","email"],"properties":{"first_name":{"type":"string","minLength":1,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9 .,'-]*$","examples":["Ada","Chris","Maria","Jun"]},"last_name":{"type":"string","minLength":1,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9 .,'-]*$","examples":["Lovelace","Vouga","Delgado","Park"]},"dob":{"type":"string","format":"date-time","examples":["1990-06-15T00:00:00.000Z","1983-02-02T00:00:00.000Z"]},"gender":{"type":"string","minLength":1,"pattern":"\\\\S","examples":["male","female","other","prefer_not_to_say"]},"phone_number":{"type":"string","minLength":1,"pattern":"^\\\\+1[2-9]\\\\d{9}$","examples":["+14155550123","+12125550987","+13105554444"]},"email":{"type":"string","format":"email","minLength":1,"examples":["ada@example.com","chris@example.com","maria@example.com"]}}},"PatientAddressCompatible":{"type":"object","required":["first_line","city","state","zip","country"],"properties":{"receiver_name":{"type":"string","default":"","examples":["Ada Lovelace","Chris Vouga"]},"first_line":{"type":"string","minLength":1,"pattern":"\\\\S","examples":["1 Main St","450 Market St","88 Kearny St"]},"second_line":{"type":["string","null"]},"city":{"type":"string","minLength":1,"pattern":"\\\\S","examples":["San Diego","San Francisco","New York"]},"state":{"type":"string","minLength":1,"enum":["AL","AK","AZ","AR","CA","CO","CT","DE","FL","GA","HI","ID","IL","IN","IA","KS","KY","LA","ME","MD","MA","MI","MN","MS","MO","MT","NE","NV","NH","NJ","NM","NY","NC","ND","OH","OK","OR","PA","RI","SC","SD","TN","TX","UT","VT","VA","WA","WV","WI","WY"]},"zip":{"type":"string","pattern":"^\\\\d{5}(-\\\\d{4})?$","examples":["92101","94105","10001"]},"country":{"type":"string","minLength":1,"pattern":"\\\\S","examples":["US"]},"access_notes":{"type":["string","null"],"examples":[null]},"phone_number":{"type":["string","null"],"examples":["+14155550123",null]}}},"AoeAnswerRequest":{"type":"object","required":["marker_id","question_id","answer"],"properties":{"marker_id":{"type":"integer","examples":[2075,1975]},"question_id":{"type":"integer","examples":[1]},"answer":{"type":"string","minLength":1,"examples":["No","Yes"]}}},"ClientFacingOrderInTransaction":{"type":"object","required":["id","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"low_level_status":{"anyOf":[{"$ref":"#/components/schemas/OrderLowLevelStatus"},{"type":"null"}]},"low_level_status_created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"timestamp"}},"created_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"updated_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}}}},"ClientFacingOrderTransaction":{"type":"object","required":["id","status","orders"],"properties":{"id":{"type":"string","format":"uuid","x-mockingbird-resource":{"type":"order_transaction","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"status":{"$ref":"#/components/schemas/OrderTransactionStatus"},"orders":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingOrderInTransaction"}}}},"OrderSummary":{"type":"object","required":["id","last_status","last_status_created_at","updated_at","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"origin":{"anyOf":[{"type":"string","enum":["initial","redraw","recreation"]},{"type":"null"}]},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"last_status":{"$ref":"#/components/schemas/OrderLowLevelStatus"},"last_status_created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"created_at":{"type":"string","format":"date-time"}}},"GetOrderTransactionResponse":{"type":"object","required":["id","team_id","status"],"properties":{"id":{"type":"string","format":"uuid","x-mockingbird-resource":{"type":"order_transaction","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"team_id":{"type":"string","format":"uuid","x-mockingbird-volatile":{"kind":"account"}},"status":{"$ref":"#/components/schemas/OrderTransactionStatus"},"orders":{"type":"array","items":{"$ref":"#/components/schemas/OrderSummary"}}}},"ClientFacingOrder":{"type":"object","required":["user_id","id","team_id","patient_details","patient_address","lab_test","details","sample_id","notes","created_at","updated_at","events","last_event","health_insurance_id","requisition_form_url","shipping_details","has_abn"],"properties":{"user_id":{"type":"string","format":"uuid"},"id":{"type":"string","format":"uuid","x-mockingbird-resource":{"type":"order","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"team_id":{"type":"string","format":"uuid","x-mockingbird-volatile":{"kind":"account"}},"patient_details":{"anyOf":[{"$ref":"#/components/schemas/ClientFacingPatientDetailsCompatible"},{"type":"null"}]},"patient_address":{"anyOf":[{"$ref":"#/components/schemas/PatientAddressCompatible"},{"type":"null"}]},"lab_test":{"$ref":"#/components/schemas/ClientFacingLabTest"},"details":{"$ref":"#/components/schemas/ClientFacingTestKitOrderDetails"},"sample_id":{"anyOf":[{"type":"string"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"opaque"}},"notes":{"anyOf":[{"type":"string"},{"type":"null"}]},"clinical_notes":{"anyOf":[{"type":"string"},{"type":"null"}]},"created_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"updated_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingOrderEvent"}},"status":{"anyOf":[{"$ref":"#/components/schemas/OrderTopLevelStatus"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"opaque"}},"last_event":{"$ref":"#/components/schemas/ClientFacingOrderEvent"},"physician":{"anyOf":[{"$ref":"#/components/schemas/ClientFacingPhysician"},{"type":"null"}]},"health_insurance_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"requisition_form_url":{"anyOf":[{"type":"string"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"url"}},"priority":{"type":"boolean","default":false},"shipping_details":{"anyOf":[{"$ref":"#/components/schemas/ShippingAddress"},{"type":"null"}]},"activate_by":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}]},"passthrough":{"anyOf":[{"type":"string"},{"type":"null"}]},"billing_type":{"type":"string","enum":["client_bill","commercial_insurance","patient_bill_passthrough","patient_bill","upfront_payment"],"default":"client_bill"},"icd_codes":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"has_abn":{"type":"boolean"},"interpretation":{"anyOf":[{"type":"string","enum":["normal","abnormal","critical","unknown"]},{"type":"null"}]},"has_missing_results":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"expected_result_by_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}]},"worst_case_result_by_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}]},"origin":{"anyOf":[{"type":"string","enum":["initial","redraw","recreation"]},{"type":"null"}]},"order_transaction":{"anyOf":[{"$ref":"#/components/schemas/ClientFacingOrderTransaction"},{"type":"null"}]}}},"OrderSetRequest":{"type":"object","required":["lab_test_ids"],"properties":{"lab_test_ids":{"type":"array","minItems":1,"items":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"lab_test","missing":"00000000-0000-4000-8000-000000000000"}}}}},"CreateOrderRequest":{"type":"object","required":["user_id","patient_details","patient_address","order_set"],"properties":{"user_id":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"user","missing":"00000000-0000-4000-8000-000000000000"}},"patient_details":{"$ref":"#/components/schemas/ClientFacingPatientDetailsCompatible"},"patient_address":{"$ref":"#/components/schemas/PatientAddressCompatible"},"order_set":{"$ref":"#/components/schemas/OrderSetRequest"},"clinical_notes":{"anyOf":[{"type":"string"},{"type":"null"}]},"passthrough":{"anyOf":[{"type":"string"},{"type":"null"}]},"collection_method":{"$ref":"#/components/schemas/LabTestCollectionMethod"},"lab_account_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"idempotency_key":{"type":["string","null"]},"idempotency_error":{"type":["string","null"]},"aoe_answers":{"type":["array","null"],"items":{"$ref":"#/components/schemas/AoeAnswerRequest"}}}},"PostOrderResponse":{"type":"object","required":["order","status","message"],"properties":{"order":{"$ref":"#/components/schemas/ClientFacingOrder"},"status":{"type":"string"},"message":{"type":"string"}}},"ResultType":{"type":"string","enum":["numeric","range","comment","coded_value"]},"ParentBiomarkerData":{"type":"object","required":["marker_id","name","slug"],"properties":{"marker_id":{"type":"integer"},"name":{"type":"string"},"slug":{"type":"string"},"provider_id":{"type":["string","null"]}}},"BiomarkerResult":{"type":"object","required":["name","result","type"],"properties":{"name":{"type":"string"},"slug":{"type":"string","default":""},"value":{"anyOf":[{"type":"number"},{"type":"null"}]},"result":{"type":"string"},"type":{"$ref":"#/components/schemas/ResultType"},"unit":{"type":["string","null"]},"timestamp":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"timestamp"}},"notes":{"type":["string","null"]},"reference_range":{"type":["string","null"]},"min_range_value":{"type":["number","null"]},"max_range_value":{"type":["number","null"]},"is_above_max_range":{"type":["boolean","null"]},"is_below_min_range":{"type":["boolean","null"]},"interpretation":{"type":"string","default":"normal"},"source_interpretation":{"type":"string","default":""},"loinc":{"type":["string","null"]},"loinc_slug":{"type":["string","null"]},"provider_id":{"type":["string","null"]},"source_markers":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ParentBiomarkerData"}},{"type":"null"}]},"performing_laboratory":{"type":["string","null"]},"source_sample_id":{"type":["string","null"]}}},"FailureType":{"type":"string","enum":["quantity_not_sufficient_failure","collection_process_failure","drop_off_failure","internal_lab_failure","order_entry_failure","non_failure","unknown_failure","patient_condition_failure","missing_result_calc_failure","missing_demo_aoe_calc_failure","insufficient_volume"]},"MissingBiomarkerResult":{"type":"object","required":["name","slug","inferred_failure_type"],"properties":{"name":{"type":"string"},"slug":{"type":"string"},"inferred_failure_type":{"$ref":"#/components/schemas/FailureType"},"note":{"type":["string","null"]},"loinc":{"type":["string","null"]},"loinc_slug":{"type":["string","null"]},"provider_id":{"type":["string","null"]},"source_markers":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ParentBiomarkerData"}},{"type":"null"}]}}},"SampleData":{"type":"object","properties":{"sample_id":{"type":["string","null"]},"control_number":{"type":["string","null"]},"date_collected":{"type":["string","null"]},"date_received":{"type":["string","null"]},"date_reported":{"type":["string","null"]},"performing_laboratories":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}]},"clinical_information":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}]}}},"LabResultsMetadata":{"type":"object","required":["age","dob","patient","date_reported","specimen_number"],"properties":{"age":{"type":"string","x-mockingbird-volatile":{"kind":"token"}},"dob":{"type":"string","x-mockingbird-volatile":{"kind":"token"}},"clia_#":{"anyOf":[{"type":"string"},{"type":"null"}]},"patient":{"type":"string","x-mockingbird-volatile":{"kind":"token"}},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"token"}},"laboratory":{"anyOf":[{"type":"string"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"token"}},"date_reported":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}},"date_collected":{"anyOf":[{"type":"string"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"timestamp"}},"specimen_number":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"date_received":{"anyOf":[{"type":"string"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"timestamp"}},"status":{"type":"string","default":"final"},"interpretation":{"anyOf":[{"type":"string"},{"type":"null"}]},"patient_id":{"anyOf":[{"type":"string"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"id"}},"account_id":{"anyOf":[{"type":"string"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"id"}}}},"LabResultsRaw":{"type":"object","required":["metadata","results"],"properties":{"metadata":{"$ref":"#/components/schemas/LabResultsMetadata"},"results":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/BiomarkerResult"}},{"type":"object","additionalProperties":true}],"x-mockingbird-volatile":{"kind":"opaque"}},"missing_results":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/MissingBiomarkerResult"}},{"type":"null"}],"x-mockingbird-volatile":{"kind":"opaque"}},"sample_information":{"anyOf":[{"type":"object","additionalProperties":{"$ref":"#/components/schemas/SampleData"}},{"type":"null"}],"x-mockingbird-volatile":{"kind":"opaque"}},"order_transaction":{"anyOf":[{"$ref":"#/components/schemas/ClientFacingOrderTransaction"},{"type":"null"}]}}},"SimulationFlags":{"type":"object","properties":{"interpretation":{"anyOf":[{"type":"string","enum":["normal","abnormal","critical","unknown"]},{"type":"null"}]},"result_types":{"type":"array","items":{"$ref":"#/components/schemas/ResultType"}},"has_missing_results":{"type":"boolean"}}},"GetOrdersResponse":{"type":"object","required":["orders"],"properties":{"orders":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingOrder"}},"total":{"anyOf":[{"type":"integer"},{"type":"null"}]},"page":{"anyOf":[{"type":"integer"},{"type":"null"}]},"size":{"anyOf":[{"type":"integer"},{"type":"null"}]}}},"HTTPValidationError":{"type":"object","properties":{"detail":{"title":"Detail"}}}}}}`);
|
|
1092
|
-
var
|
|
1093
|
-
|
|
1415
|
+
var adminJson = (status, body) => new Response(JSON.stringify(body), { status, headers: { "content-type": "application/json" } });
|
|
1416
|
+
var adminFail = (status, message) => adminJson(status, { error: { type: "mockingbird_admin", message } });
|
|
1417
|
+
var isObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1418
|
+
var credentialRoutes = (registry) => ({
|
|
1419
|
+
"GET /credentials": () => adminJson(200, {
|
|
1420
|
+
credentials: registry.entries().map(({ credential, namespace }) => ({
|
|
1421
|
+
credential: maskCredential(credential),
|
|
1422
|
+
namespace
|
|
1423
|
+
}))
|
|
1424
|
+
}),
|
|
1425
|
+
"PUT /credentials": ({ body, namespace }) => {
|
|
1426
|
+
const pairs = [];
|
|
1427
|
+
const list = Array.isArray(body) ? body : isObject(body) ? body.credentials : void 0;
|
|
1428
|
+
if (Array.isArray(list)) {
|
|
1429
|
+
for (const each of list) {
|
|
1430
|
+
if (typeof each === "string")
|
|
1431
|
+
pairs.push([each, namespace]);
|
|
1432
|
+
else if (isObject(each) && typeof each.credential === "string") {
|
|
1433
|
+
pairs.push([
|
|
1434
|
+
each.credential,
|
|
1435
|
+
typeof each.namespace === "string" ? each.namespace : namespace
|
|
1436
|
+
]);
|
|
1437
|
+
} else
|
|
1438
|
+
return adminFail(400, "each entry is a credential string or {credential, namespace}");
|
|
1439
|
+
}
|
|
1440
|
+
} else if (isObject(list)) {
|
|
1441
|
+
for (const [credential, target] of Object.entries(list)) {
|
|
1442
|
+
if (typeof target !== "string")
|
|
1443
|
+
return adminFail(400, `namespace for ${credential} must be a string`);
|
|
1444
|
+
pairs.push([credential, target]);
|
|
1445
|
+
}
|
|
1446
|
+
} else if (isObject(body) && typeof body.credential === "string") {
|
|
1447
|
+
pairs.push([body.credential, typeof body.namespace === "string" ? body.namespace : namespace]);
|
|
1448
|
+
} else {
|
|
1449
|
+
return adminFail(400, 'expected {"credentials": {"<credential>": "<namespace>"}}');
|
|
1450
|
+
}
|
|
1451
|
+
for (const [credential, target] of pairs) {
|
|
1452
|
+
if (!NAMESPACE_PATTERN.test(target))
|
|
1453
|
+
return adminFail(400, `bad namespace ${JSON.stringify(target)}`);
|
|
1454
|
+
registry.set(credential, target);
|
|
1455
|
+
}
|
|
1456
|
+
return adminJson(200, { mapped: pairs.length });
|
|
1457
|
+
},
|
|
1458
|
+
"DELETE /credentials": ({ url }) => {
|
|
1459
|
+
const credential = url.searchParams.get("credential");
|
|
1460
|
+
if (credential === null)
|
|
1461
|
+
registry.clear();
|
|
1462
|
+
else
|
|
1463
|
+
registry.remove(credential);
|
|
1464
|
+
return adminJson(200, { status: "ok" });
|
|
1465
|
+
}
|
|
1466
|
+
});
|
|
1467
|
+
var presetRoutes = (presets, runtime) => ({
|
|
1468
|
+
"GET /faults/presets": () => adminJson(200, {
|
|
1469
|
+
presets: Object.entries(presets).map(([name, preset]) => ({ name, ...preset }))
|
|
1470
|
+
}),
|
|
1471
|
+
"POST /faults/presets/:name": ({ params, body, namespace }) => {
|
|
1472
|
+
const name = params.name;
|
|
1473
|
+
if (!presets[name])
|
|
1474
|
+
return adminFail(404, `no fault preset ${name}; GET /__admin/faults/presets`);
|
|
1475
|
+
const overrides = isObject(body) ? body : {};
|
|
1476
|
+
return adminJson(201, { preset: name, rules: runtime.applyPreset(name, namespace, overrides) });
|
|
1477
|
+
}
|
|
1478
|
+
});
|
|
1094
1479
|
|
|
1095
1480
|
// src/catalog.ts
|
|
1096
1481
|
var LAB_TEST_CATALOG = [
|
|
@@ -1821,6 +2206,69 @@ var EXPECTED_RESULTS = {
|
|
|
1821
2206
|
]
|
|
1822
2207
|
};
|
|
1823
2208
|
|
|
2209
|
+
// src/limits.ts
|
|
2210
|
+
var SANDBOX_KEY_PREFIXES = ["sk_us_", "sk_eu_"];
|
|
2211
|
+
var isSandboxKey = (key) => SANDBOX_KEY_PREFIXES.some((prefix) => key.startsWith(prefix));
|
|
2212
|
+
var DEFAULT_LIMITS = {
|
|
2213
|
+
maxUsers: null,
|
|
2214
|
+
simulateRequiresSandbox: false,
|
|
2215
|
+
rateLimitPerSecond: null
|
|
2216
|
+
};
|
|
2217
|
+
var sandboxUserQuotaBody = (max) => ({
|
|
2218
|
+
detail: {
|
|
2219
|
+
error_type: "INVALID_REQUEST",
|
|
2220
|
+
error_message: `You have reached the maximum of ${max} Sandbox users`
|
|
2221
|
+
}
|
|
2222
|
+
});
|
|
2223
|
+
var positiveOrNull = (value, field) => {
|
|
2224
|
+
if (value === null) return null;
|
|
2225
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 0)
|
|
2226
|
+
throw new TypeError(`${field} must be a non-negative integer, or null for unlimited`);
|
|
2227
|
+
return value;
|
|
2228
|
+
};
|
|
2229
|
+
var mergeLimits = (base, input) => {
|
|
2230
|
+
if (typeof input !== "object" || input === null || Array.isArray(input))
|
|
2231
|
+
throw new TypeError("limits must be an object");
|
|
2232
|
+
const record = input;
|
|
2233
|
+
for (const key of Object.keys(record)) {
|
|
2234
|
+
if (!(key in DEFAULT_LIMITS))
|
|
2235
|
+
throw new TypeError(`unknown limit ${key}; one of ${Object.keys(DEFAULT_LIMITS).join(", ")}`);
|
|
2236
|
+
}
|
|
2237
|
+
const next = { ...base };
|
|
2238
|
+
if (record.maxUsers !== void 0) next.maxUsers = positiveOrNull(record.maxUsers, "maxUsers");
|
|
2239
|
+
if (record.rateLimitPerSecond !== void 0)
|
|
2240
|
+
next.rateLimitPerSecond = positiveOrNull(record.rateLimitPerSecond, "rateLimitPerSecond");
|
|
2241
|
+
if (record.simulateRequiresSandbox !== void 0) {
|
|
2242
|
+
if (typeof record.simulateRequiresSandbox !== "boolean")
|
|
2243
|
+
throw new TypeError("simulateRequiresSandbox must be a boolean");
|
|
2244
|
+
next.simulateRequiresSandbox = record.simulateRequiresSandbox;
|
|
2245
|
+
}
|
|
2246
|
+
return next;
|
|
2247
|
+
};
|
|
2248
|
+
var checkUserQuota = (limits, liveUsers) => {
|
|
2249
|
+
if (limits.maxUsers !== null && liveUsers >= limits.maxUsers)
|
|
2250
|
+
throw new HttpError(400, sandboxUserQuotaBody(limits.maxUsers));
|
|
2251
|
+
};
|
|
2252
|
+
var checkSimulateAllowed = (limits, apiKey) => {
|
|
2253
|
+
if (limits.simulateRequiresSandbox && !isSandboxKey(apiKey ?? ""))
|
|
2254
|
+
throw new HttpError(400, { detail: "Order simulation is only available in sandbox" });
|
|
2255
|
+
};
|
|
2256
|
+
var RateWindow = class {
|
|
2257
|
+
windowStart = 0;
|
|
2258
|
+
count = 0;
|
|
2259
|
+
/** Whether one more request fits under `perSecond` now. */
|
|
2260
|
+
take(perSecond, nowMs = Date.now()) {
|
|
2261
|
+
if (perSecond === null) return true;
|
|
2262
|
+
if (nowMs - this.windowStart >= 1e3) {
|
|
2263
|
+
this.windowStart = nowMs;
|
|
2264
|
+
this.count = 0;
|
|
2265
|
+
}
|
|
2266
|
+
if (this.count >= perSecond) return false;
|
|
2267
|
+
this.count++;
|
|
2268
|
+
return true;
|
|
2269
|
+
}
|
|
2270
|
+
};
|
|
2271
|
+
|
|
1824
2272
|
// src/state.ts
|
|
1825
2273
|
var sortedQueryString = (params) => {
|
|
1826
2274
|
const entries = [...params.entries()].sort(([leftKey, leftValue], [rightKey, rightValue]) => {
|
|
@@ -1893,6 +2341,14 @@ var JunctionState = class {
|
|
|
1893
2341
|
getCache;
|
|
1894
2342
|
/** Configuration, not data: set by JunctionAPI and re-applied on reset. */
|
|
1895
2343
|
geoMode = "synthetic";
|
|
2344
|
+
/** The team this mock answers as: `team_id` on users and orders, and lab-account linking. */
|
|
2345
|
+
teamId = MOCK_TEAM_ID;
|
|
2346
|
+
/** Sandbox-only restrictions to enforce; every one is off by default. */
|
|
2347
|
+
limits = { ...DEFAULT_LIMITS };
|
|
2348
|
+
/** Whether an unknown `user_id` is refused (`strict`) or created on first use. */
|
|
2349
|
+
identity = "strict";
|
|
2350
|
+
webhooksMuted = 0;
|
|
2351
|
+
adoptedRequests = /* @__PURE__ */ new WeakSet();
|
|
1896
2352
|
/** ZIPs the loaded corpus has serviceability records for. */
|
|
1897
2353
|
coveredZips = /* @__PURE__ */ new Set();
|
|
1898
2354
|
/** Identifies the loaded corpus in errors and `/health`. */
|
|
@@ -2171,7 +2627,24 @@ var JunctionState = class {
|
|
|
2171
2627
|
testkitIdFor(orderId) {
|
|
2172
2628
|
return deterministicUuid(`junction:testkit:${orderId}`);
|
|
2173
2629
|
}
|
|
2630
|
+
/** Run `fn` without publishing any webhook: how fixtures load without side effects. */
|
|
2631
|
+
muteWebhooks(fn) {
|
|
2632
|
+
this.webhooksMuted++;
|
|
2633
|
+
try {
|
|
2634
|
+
return fn();
|
|
2635
|
+
} finally {
|
|
2636
|
+
this.webhooksMuted--;
|
|
2637
|
+
}
|
|
2638
|
+
}
|
|
2639
|
+
/** Record that handling `request` adopted an unknown user (see `requireUser`). */
|
|
2640
|
+
noteAdoption(request2) {
|
|
2641
|
+
this.adoptedRequests.add(request2);
|
|
2642
|
+
}
|
|
2643
|
+
adopted(request2) {
|
|
2644
|
+
return this.adoptedRequests.has(request2);
|
|
2645
|
+
}
|
|
2174
2646
|
publishWebhook(event, now = Date.now()) {
|
|
2647
|
+
if (this.webhooksMuted > 0) return;
|
|
2175
2648
|
const sequence = this.webhookEvents.nextSequence();
|
|
2176
2649
|
const snapshot = clone(event);
|
|
2177
2650
|
this.webhookEvents.insert(String(sequence), { sequence, event: snapshot });
|
|
@@ -2313,6 +2786,7 @@ var LAB_ACCOUNT_STATUSES = [
|
|
|
2313
2786
|
"ready_to_launch"
|
|
2314
2787
|
];
|
|
2315
2788
|
var MOCK_ORG_ID = deterministicUuid("junction:org:mock");
|
|
2789
|
+
var OTHER_TEAM_ID = deterministicUuid("junction:team:other");
|
|
2316
2790
|
var PLATFORM_BILLING = Object.fromEntries(
|
|
2317
2791
|
BILLING_TYPES.map((type) => [type, ALL_BILLING_STATES])
|
|
2318
2792
|
);
|
|
@@ -2386,18 +2860,19 @@ var TEAM_LAB_ACCOUNTS = [
|
|
|
2386
2860
|
status: "active",
|
|
2387
2861
|
account_name: "IHD \u2014 other team",
|
|
2388
2862
|
allowed_billing: CLIENT_BILL_ONLY,
|
|
2389
|
-
team_id_allowlist: []
|
|
2863
|
+
team_id_allowlist: [OTHER_TEAM_ID]
|
|
2390
2864
|
})
|
|
2391
2865
|
];
|
|
2392
2866
|
var PLATFORM_ACCOUNT_LABS = TEAM_LABS.map((lab) => String(lab.slug));
|
|
2393
|
-
var labAccountById = (id, accounts
|
|
2394
|
-
var
|
|
2867
|
+
var labAccountById = (id, accounts) => accounts.find((entry) => entry.id === id);
|
|
2868
|
+
var isLinkedToTeam = (entry, teamId) => entry.team_id_allowlist.length === 0 || entry.team_id_allowlist.includes(teamId);
|
|
2869
|
+
var linkedLabAccounts = (labSlug, teamId, accounts) => accounts.filter((entry) => entry.lab === labSlug && isLinkedToTeam(entry, teamId));
|
|
2395
2870
|
var effectiveBilling = (account2) => account2 === "platform" ? PLATFORM_BILLING : account2.allowed_billing;
|
|
2396
|
-
var selectLabAccount = (labSlug, requestedId, teamId, accounts
|
|
2871
|
+
var selectLabAccount = (labSlug, requestedId, teamId, accounts) => {
|
|
2397
2872
|
if (requestedId !== null) {
|
|
2398
2873
|
const linked = labAccountById(requestedId, accounts);
|
|
2399
2874
|
if (!linked) throw new HttpError(400, { detail: "Lab account does not exist" });
|
|
2400
|
-
if (!linked
|
|
2875
|
+
if (!isLinkedToTeam(linked, teamId))
|
|
2401
2876
|
throw new HttpError(400, { detail: "Lab account is not linked to your team" });
|
|
2402
2877
|
if (linked.lab !== labSlug)
|
|
2403
2878
|
throw new HttpError(400, { detail: `Lab account is not associated with lab ${labSlug}` });
|
|
@@ -2433,16 +2908,57 @@ var renderLabAccount = (entry) => ({
|
|
|
2433
2908
|
),
|
|
2434
2909
|
team_id_allowlist: [...entry.team_id_allowlist]
|
|
2435
2910
|
});
|
|
2436
|
-
var
|
|
2911
|
+
var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2912
|
+
var DELEGATED_FLOWS = [
|
|
2913
|
+
"order_delegated",
|
|
2914
|
+
"result_delegated",
|
|
2915
|
+
"fully_delegated",
|
|
2916
|
+
"not_delegated"
|
|
2917
|
+
];
|
|
2918
|
+
var checkStates = (value, where) => {
|
|
2919
|
+
if (!Array.isArray(value)) throw new TypeError(`${where} must be an array of US state codes`);
|
|
2920
|
+
for (const state of value) {
|
|
2921
|
+
if (typeof state !== "string" || !US_STATES.includes(state))
|
|
2922
|
+
throw new TypeError(`${where}: ${String(state)} is not a US state code`);
|
|
2923
|
+
}
|
|
2924
|
+
};
|
|
2437
2925
|
var stringList = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string") ? [...value] : void 0;
|
|
2438
|
-
var labAccountFromInput = (input) => {
|
|
2926
|
+
var labAccountFromInput = (input, teamId = MOCK_TEAM_ID) => {
|
|
2927
|
+
if (!isRecord4(input)) throw new TypeError("lab account must be an object");
|
|
2439
2928
|
if (typeof input.id !== "string" || input.id === "")
|
|
2440
2929
|
throw new TypeError("lab account needs a non-empty id");
|
|
2930
|
+
const where = `lab account ${input.id}`;
|
|
2441
2931
|
if (typeof input.lab !== "string" || input.lab === "")
|
|
2442
|
-
throw new TypeError(
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2932
|
+
throw new TypeError(`${where}: lab must be a lab slug such as "quest"`);
|
|
2933
|
+
if (input.status !== void 0 && !LAB_ACCOUNT_STATUSES.includes(input.status))
|
|
2934
|
+
throw new TypeError(`${where}: status must be one of ${LAB_ACCOUNT_STATUSES.join(", ")}`);
|
|
2935
|
+
if (input.delegated_flow !== void 0 && !DELEGATED_FLOWS.includes(input.delegated_flow))
|
|
2936
|
+
throw new TypeError(`${where}: delegated_flow must be one of ${DELEGATED_FLOWS.join(", ")}`);
|
|
2937
|
+
if (input.states !== void 0) checkStates(input.states, `${where}: states`);
|
|
2938
|
+
if (input.allowed_billing !== void 0) {
|
|
2939
|
+
if (!isRecord4(input.allowed_billing))
|
|
2940
|
+
throw new TypeError(`${where}: allowed_billing must map billing types to state lists`);
|
|
2941
|
+
for (const [type, states] of Object.entries(input.allowed_billing)) {
|
|
2942
|
+
if (!BILLING_TYPES.includes(type))
|
|
2943
|
+
throw new TypeError(
|
|
2944
|
+
`${where}: allowed_billing.${type} is not a billing type (${BILLING_TYPES.join(", ")})`
|
|
2945
|
+
);
|
|
2946
|
+
checkStates(states, `${where}: allowed_billing.${type}`);
|
|
2947
|
+
}
|
|
2948
|
+
}
|
|
2949
|
+
if (input.team_id_allowlist !== void 0 && stringList(input.team_id_allowlist) === void 0)
|
|
2950
|
+
throw new TypeError(`${where}: team_id_allowlist must be an array of team ids`);
|
|
2951
|
+
if (input.business_units != null && stringList(input.business_units) === void 0)
|
|
2952
|
+
throw new TypeError(`${where}: business_units must be an array of strings or null`);
|
|
2953
|
+
for (const field of [
|
|
2954
|
+
"account_name",
|
|
2955
|
+
"provider_account_id",
|
|
2956
|
+
"org_id",
|
|
2957
|
+
"default_clinical_notes"
|
|
2958
|
+
]) {
|
|
2959
|
+
const value = input[field];
|
|
2960
|
+
if (value != null && typeof value !== "string")
|
|
2961
|
+
throw new TypeError(`${where}: ${field} must be a string`);
|
|
2446
2962
|
}
|
|
2447
2963
|
return {
|
|
2448
2964
|
id: input.id,
|
|
@@ -2455,13 +2971,31 @@ var labAccountFromInput = (input) => {
|
|
|
2455
2971
|
default_clinical_notes: input.default_clinical_notes ?? null,
|
|
2456
2972
|
business_units: input.business_units ?? null,
|
|
2457
2973
|
allowed_billing: input.allowed_billing ?? { client_bill: input.states ?? ALL_BILLING_STATES },
|
|
2458
|
-
team_id_allowlist: input.team_id_allowlist ?? [
|
|
2974
|
+
team_id_allowlist: input.team_id_allowlist ?? [teamId]
|
|
2459
2975
|
};
|
|
2460
2976
|
};
|
|
2461
|
-
var
|
|
2977
|
+
var patchLabAccount = (existing, patch, teamId) => {
|
|
2978
|
+
if (!isRecord4(patch)) throw new TypeError("lab account patch must be an object");
|
|
2979
|
+
if (patch.id !== void 0 && patch.id !== existing.id)
|
|
2980
|
+
throw new TypeError(`lab account ${existing.id}: id cannot be changed`);
|
|
2981
|
+
const { states, ...rest } = patch;
|
|
2982
|
+
if (states !== void 0) checkStates(states, `lab account ${existing.id}: states`);
|
|
2983
|
+
const allowed_billing = rest.allowed_billing ?? (states !== void 0 ? { ...existing.allowed_billing, client_bill: states } : existing.allowed_billing);
|
|
2984
|
+
return labAccountFromInput(
|
|
2985
|
+
{
|
|
2986
|
+
...existing,
|
|
2987
|
+
business_units: existing.business_units,
|
|
2988
|
+
...rest,
|
|
2989
|
+
id: existing.id,
|
|
2990
|
+
allowed_billing
|
|
2991
|
+
},
|
|
2992
|
+
teamId
|
|
2993
|
+
);
|
|
2994
|
+
};
|
|
2995
|
+
var labAccountFromClientFacing = (value, recordedTeamId) => {
|
|
2462
2996
|
if (typeof value.id !== "string" || typeof value.lab !== "string") return void 0;
|
|
2463
2997
|
const billing = {};
|
|
2464
|
-
if (
|
|
2998
|
+
if (isRecord4(value.allowed_billing)) {
|
|
2465
2999
|
for (const [type, states] of Object.entries(value.allowed_billing)) {
|
|
2466
3000
|
const list = stringList(states);
|
|
2467
3001
|
if (list) billing[type] = list;
|
|
@@ -2479,147 +3013,639 @@ var labAccountFromClientFacing = (value) => {
|
|
|
2479
3013
|
default_clinical_notes: typeof value.default_clinical_notes === "string" ? value.default_clinical_notes : null,
|
|
2480
3014
|
business_units: stringList(value.business_units) ?? null,
|
|
2481
3015
|
allowed_billing: billing,
|
|
2482
|
-
team_id_allowlist: allowlist.includes(MOCK_TEAM_ID) ? allowlist : [...allowlist, MOCK_TEAM_ID]
|
|
3016
|
+
team_id_allowlist: recordedTeamId !== void 0 || allowlist.includes(MOCK_TEAM_ID) ? allowlist : [...allowlist, MOCK_TEAM_ID]
|
|
2483
3017
|
};
|
|
2484
3018
|
};
|
|
2485
3019
|
|
|
2486
|
-
// src/
|
|
2487
|
-
var
|
|
2488
|
-
var
|
|
2489
|
-
var
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
}
|
|
2498
|
-
});
|
|
2499
|
-
};
|
|
2500
|
-
var cachedResponse = (state, context) => {
|
|
2501
|
-
const body = context.request.method.toUpperCase() === "GET" || context.request.method.toUpperCase() === "HEAD" ? void 0 : context.body.kind === "json" ? context.body.value : context.body.kind === "form" ? context.body.value : void 0;
|
|
2502
|
-
const key = state.cacheKeyForRequest(
|
|
2503
|
-
context.request.method,
|
|
2504
|
-
context.url.pathname,
|
|
2505
|
-
context.url.searchParams,
|
|
2506
|
-
body
|
|
2507
|
-
);
|
|
2508
|
-
const cached = state.getGetCache(key);
|
|
2509
|
-
if (!cached) return void 0;
|
|
2510
|
-
if (key.toLowerCase().includes("availability")) {
|
|
2511
|
-
materializeAvailabilityBookingKeys(state, context, cached.body);
|
|
2512
|
-
rotateAvailabilityBookingKeys(state, cached.body, context.now());
|
|
2513
|
-
}
|
|
2514
|
-
const headers = new Headers(cached.headers);
|
|
2515
|
-
headers.delete("content-encoding");
|
|
2516
|
-
headers.delete("content-length");
|
|
2517
|
-
headers.delete("transfer-encoding");
|
|
2518
|
-
if (!headers.has("content-type")) headers.set("content-type", "application/json");
|
|
2519
|
-
return new Response(JSON.stringify(cached.body), { status: cached.status, headers });
|
|
2520
|
-
};
|
|
2521
|
-
var materializeAvailabilityBookingKeys = (state, context, cachedBody) => {
|
|
2522
|
-
const requestBody = context.body.kind === "json" && typeof context.body.value === "object" && context.body.value !== null && !Array.isArray(context.body.value) ? context.body.value : {};
|
|
2523
|
-
const zip = typeof requestBody.zip_code === "string" && /^\d{5}/.test(requestBody.zip_code) ? requestBody.zip_code.slice(0, 5) : "85004";
|
|
2524
|
-
const path = context.url.pathname;
|
|
2525
|
-
const modalityHint = path.includes("psc") ? "patient_service_center" : path.includes("phlebotomy") ? "phlebotomy" : void 0;
|
|
2526
|
-
state.hydrateBookingKeysFromAvailability(cachedBody, context.now(), zip, modalityHint);
|
|
2527
|
-
if (typeof cachedBody === "object" && cachedBody !== null && !Array.isArray(cachedBody)) {
|
|
2528
|
-
const root = cachedBody;
|
|
2529
|
-
const dayBuckets = [];
|
|
2530
|
-
if (Array.isArray(root.days)) dayBuckets.push(...root.days);
|
|
2531
|
-
if (Array.isArray(root.slots)) dayBuckets.push(...root.slots);
|
|
2532
|
-
for (const day of dayBuckets) {
|
|
2533
|
-
if (typeof day !== "object" || day === null || Array.isArray(day)) continue;
|
|
2534
|
-
const daySlots = day.slots;
|
|
2535
|
-
const slots = Array.isArray(daySlots) ? daySlots : [day];
|
|
2536
|
-
for (const entry of slots) {
|
|
2537
|
-
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) continue;
|
|
2538
|
-
const slot = entry;
|
|
2539
|
-
const bookingKey = typeof slot.booking_key === "string" ? slot.booking_key : "";
|
|
2540
|
-
if (!bookingKey) continue;
|
|
2541
|
-
const record = state.bookingKeys.get(bookingKey);
|
|
2542
|
-
if (!record) continue;
|
|
2543
|
-
if (modalityHint) record.modality = modalityHint;
|
|
2544
|
-
record.zip_code = zip;
|
|
2545
|
-
record.provider = record.modality === "patient_service_center" ? "quest" : "getlabs";
|
|
2546
|
-
record.address = addressFromAvailabilityRequest(requestBody, zip);
|
|
2547
|
-
record.location = zipLocation(zip);
|
|
2548
|
-
state.bookingKeys.update(bookingKey, record);
|
|
2549
|
-
}
|
|
2550
|
-
}
|
|
2551
|
-
}
|
|
3020
|
+
// src/lab-account-presets.ts
|
|
3021
|
+
var except = (...excluded) => US_STATES.filter((state) => !excluded.includes(state));
|
|
3022
|
+
var PLATFORM_ACCOUNT_STATES = except("NJ", "NY", "RI");
|
|
3023
|
+
var DELEGATED_ACCOUNT_STATES = except("NJ", "NY");
|
|
3024
|
+
var bioreferenceCustomer = {
|
|
3025
|
+
lab: "bioreference",
|
|
3026
|
+
account_name: "Junction BioReference Account",
|
|
3027
|
+
delegated_flow: "not_delegated",
|
|
3028
|
+
org_id: null,
|
|
3029
|
+
allowed_billing: { client_bill: PLATFORM_ACCOUNT_STATES },
|
|
3030
|
+
team_id_allowlist: []
|
|
2552
3031
|
};
|
|
2553
|
-
var
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
if (typeof day !== "object" || day === null || Array.isArray(day)) continue;
|
|
2561
|
-
const daySlots = day.slots;
|
|
2562
|
-
const slots = Array.isArray(daySlots) ? daySlots : [day];
|
|
2563
|
-
for (let index = 0; index < slots.length; index += 1) {
|
|
2564
|
-
const slot = slots[index];
|
|
2565
|
-
if (typeof slot !== "object" || slot === null || Array.isArray(slot)) continue;
|
|
2566
|
-
const entry = slot;
|
|
2567
|
-
const sealedKey = typeof entry.booking_key === "string" ? entry.booking_key : "";
|
|
2568
|
-
if (!sealedKey) continue;
|
|
2569
|
-
const sealed = state.bookingKeys.get(sealedKey);
|
|
2570
|
-
if (!sealed) continue;
|
|
2571
|
-
const freshKey = bookingKeyRotationFor(state, sealedKey, nowMs, index);
|
|
2572
|
-
const record = {
|
|
2573
|
-
...sealed,
|
|
2574
|
-
key: freshKey,
|
|
2575
|
-
consumed_by_order_id: null,
|
|
2576
|
-
created_at: new Date(nowMs).toISOString()
|
|
2577
|
-
};
|
|
2578
|
-
state.bookingKeys.insert(freshKey, record);
|
|
2579
|
-
entry.booking_key = freshKey;
|
|
2580
|
-
}
|
|
2581
|
-
}
|
|
3032
|
+
var questPlatform = {
|
|
3033
|
+
lab: "quest",
|
|
3034
|
+
account_name: "Junction Quest Account",
|
|
3035
|
+
delegated_flow: "not_delegated",
|
|
3036
|
+
org_id: null,
|
|
3037
|
+
allowed_billing: { client_bill: PLATFORM_ACCOUNT_STATES },
|
|
3038
|
+
team_id_allowlist: []
|
|
2582
3039
|
};
|
|
2583
|
-
var
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
3040
|
+
var labcorpPlatform = {
|
|
3041
|
+
lab: "labcorp",
|
|
3042
|
+
account_name: "Junction Labcorp Account",
|
|
3043
|
+
delegated_flow: "not_delegated",
|
|
3044
|
+
org_id: null,
|
|
3045
|
+
allowed_billing: { client_bill: PLATFORM_ACCOUNT_STATES },
|
|
3046
|
+
team_id_allowlist: []
|
|
3047
|
+
};
|
|
3048
|
+
var LAB_ACCOUNT_PRESETS = {
|
|
3049
|
+
bioreference_ny_nj_delegated: {
|
|
3050
|
+
lab: "bioreference",
|
|
3051
|
+
delegated_flow: "order_delegated",
|
|
3052
|
+
allowed_billing: { client_bill: ["NY", "NJ"] }
|
|
3053
|
+
},
|
|
3054
|
+
bioreference_delegated_multi_state: {
|
|
3055
|
+
lab: "bioreference",
|
|
3056
|
+
delegated_flow: "order_delegated",
|
|
3057
|
+
allowed_billing: { client_bill: DELEGATED_ACCOUNT_STATES }
|
|
3058
|
+
},
|
|
3059
|
+
bioreference_customer_multi_state: bioreferenceCustomer,
|
|
3060
|
+
bioreference_patient_bill_passthrough: {
|
|
3061
|
+
lab: "bioreference",
|
|
3062
|
+
delegated_flow: "not_delegated",
|
|
3063
|
+
allowed_billing: { patient_bill_passthrough: ["NJ", "NY"] }
|
|
3064
|
+
},
|
|
3065
|
+
quest_platform: questPlatform,
|
|
3066
|
+
labcorp_platform: labcorpPlatform,
|
|
3067
|
+
suspended_bioreference: { ...bioreferenceCustomer, status: "suspended" },
|
|
3068
|
+
suspended_quest: { ...questPlatform, status: "suspended" },
|
|
3069
|
+
suspended_labcorp: { ...labcorpPlatform, status: "suspended" }
|
|
3070
|
+
};
|
|
3071
|
+
var presetAccountId = (name) => deterministicUuid(`junction:lab-account:${name}`);
|
|
3072
|
+
var labAccountFromPreset = (name, options) => {
|
|
3073
|
+
const preset = Object.hasOwn(LAB_ACCOUNT_PRESETS, name) ? LAB_ACCOUNT_PRESETS[name] : void 0;
|
|
3074
|
+
if (!preset) return void 0;
|
|
3075
|
+
return labAccountFromInput({ ...preset, id: options.id ?? presetAccountId(name) }, options.teamId);
|
|
3076
|
+
};
|
|
3077
|
+
var expandLabAccountLayout = (layout, teamId) => {
|
|
3078
|
+
if (Array.isArray(layout))
|
|
3079
|
+
return layout.map((input) => labAccountFromInput(input, teamId));
|
|
3080
|
+
if (typeof layout !== "object" || layout === null)
|
|
3081
|
+
throw new TypeError('lab accounts must be an array, or { "presets": [...], "accounts": [...] }');
|
|
3082
|
+
const { presets = [], accounts = [] } = layout;
|
|
3083
|
+
if (!Array.isArray(presets)) throw new TypeError("presets must be an array of preset names");
|
|
3084
|
+
if (!Array.isArray(accounts)) throw new TypeError("accounts must be an array of lab accounts");
|
|
3085
|
+
const records = [];
|
|
3086
|
+
for (const entry of presets) {
|
|
3087
|
+
const name = typeof entry === "string" ? entry : entry?.name;
|
|
3088
|
+
const id = typeof entry === "string" ? void 0 : entry?.id;
|
|
3089
|
+
const record = typeof name === "string" ? labAccountFromPreset(name, { teamId, ...id !== void 0 ? { id } : {} }) : void 0;
|
|
3090
|
+
if (!record)
|
|
3091
|
+
throw new TypeError(
|
|
3092
|
+
`no lab-account preset ${JSON.stringify(name)}; one of ${Object.keys(LAB_ACCOUNT_PRESETS).join(", ")}`
|
|
3093
|
+
);
|
|
3094
|
+
records.push(record);
|
|
2587
3095
|
}
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
)
|
|
2591
|
-
};
|
|
2592
|
-
|
|
2593
|
-
for (const day of days) {
|
|
2594
|
-
const slots = Array.isArray(day.slots) ? day.slots : [];
|
|
2595
|
-
for (let index = 0; index < slots.length; index += 1) {
|
|
2596
|
-
const slot = slots[index];
|
|
2597
|
-
if (typeof slot !== "object" || slot === null || Array.isArray(slot)) continue;
|
|
2598
|
-
const entry = slot;
|
|
2599
|
-
const currentKey = typeof entry.booking_key === "string" ? entry.booking_key : "";
|
|
2600
|
-
if (!currentKey) continue;
|
|
2601
|
-
const current = state.bookingKeys.get(currentKey);
|
|
2602
|
-
if (!current) continue;
|
|
2603
|
-
const freshKey = bookingKeyRotationFor(state, currentKey, nowMs, index);
|
|
2604
|
-
const record = {
|
|
2605
|
-
...current,
|
|
2606
|
-
key: freshKey,
|
|
2607
|
-
...override ?? {},
|
|
2608
|
-
consumed_by_order_id: null,
|
|
2609
|
-
created_at: new Date(nowMs).toISOString()
|
|
2610
|
-
};
|
|
2611
|
-
state.bookingKeys.insert(freshKey, record);
|
|
2612
|
-
entry.booking_key = freshKey;
|
|
2613
|
-
}
|
|
3096
|
+
for (const input of accounts) records.push(labAccountFromInput(input, teamId));
|
|
3097
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3098
|
+
for (const record of records) {
|
|
3099
|
+
if (seen.has(record.id)) throw new TypeError(`lab account ${record.id} is declared twice`);
|
|
3100
|
+
seen.add(record.id);
|
|
2614
3101
|
}
|
|
3102
|
+
return records;
|
|
2615
3103
|
};
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
3104
|
+
|
|
3105
|
+
// src/admin.ts
|
|
3106
|
+
var ORDER_STATUSES_BY_METHOD = {
|
|
3107
|
+
at_home_phlebotomy: [
|
|
3108
|
+
"received.ordered",
|
|
3109
|
+
"received.requisition_created",
|
|
3110
|
+
"received.requisition_bypassed",
|
|
3111
|
+
"collecting_sample.appointment_pending",
|
|
3112
|
+
"collecting_sample.appointment_scheduled",
|
|
3113
|
+
"collecting_sample.appointment_cancelled",
|
|
3114
|
+
"collecting_sample.draw_completed",
|
|
3115
|
+
"sample_with_lab.partial_results",
|
|
3116
|
+
"completed.completed",
|
|
3117
|
+
"completed.corrected",
|
|
3118
|
+
"cancelled.cancelled",
|
|
3119
|
+
"failed.sample_error"
|
|
3120
|
+
],
|
|
3121
|
+
on_site_collection: [
|
|
3122
|
+
"received.ordered",
|
|
3123
|
+
"received.requisition_created",
|
|
3124
|
+
"received.requisition_bypassed",
|
|
3125
|
+
"sample_with_lab.draw_completed",
|
|
3126
|
+
"sample_with_lab.partial_results",
|
|
3127
|
+
"completed.completed",
|
|
3128
|
+
"completed.corrected",
|
|
3129
|
+
"cancelled.cancelled",
|
|
3130
|
+
"failed.sample_error"
|
|
3131
|
+
],
|
|
3132
|
+
testkit: [
|
|
3133
|
+
"received.ordered",
|
|
3134
|
+
"received.awaiting_registration",
|
|
3135
|
+
"received.registered",
|
|
3136
|
+
"received.requisition_created",
|
|
3137
|
+
"received.requisition_bypassed",
|
|
3138
|
+
"collecting_sample.transit_customer",
|
|
3139
|
+
"collecting_sample.out_for_delivery",
|
|
3140
|
+
"collecting_sample.with_customer",
|
|
3141
|
+
"collecting_sample.transit_lab",
|
|
3142
|
+
"collecting_sample.problem_in_transit_customer",
|
|
3143
|
+
"collecting_sample.problem_in_transit_lab",
|
|
3144
|
+
"sample_with_lab.delivered_to_lab",
|
|
3145
|
+
"sample_with_lab.lab_processing_blocked",
|
|
3146
|
+
"completed.completed",
|
|
3147
|
+
"completed.corrected",
|
|
3148
|
+
"cancelled.cancelled",
|
|
3149
|
+
"cancelled.do_not_process",
|
|
3150
|
+
"failed.sample_error",
|
|
3151
|
+
"failed.failure_to_deliver_to_customer",
|
|
3152
|
+
"failed.failure_to_deliver_to_lab",
|
|
3153
|
+
"failed.lost"
|
|
3154
|
+
],
|
|
3155
|
+
walk_in_test: [
|
|
3156
|
+
"received.ordered",
|
|
3157
|
+
"received.requisition_created",
|
|
3158
|
+
"received.requisition_bypassed",
|
|
3159
|
+
"collecting_sample.appointment_pending",
|
|
3160
|
+
"collecting_sample.appointment_scheduled",
|
|
3161
|
+
"collecting_sample.appointment_cancelled",
|
|
3162
|
+
"collecting_sample.redraw_available",
|
|
3163
|
+
"sample_with_lab.partial_results",
|
|
3164
|
+
"completed.completed",
|
|
3165
|
+
"completed.corrected",
|
|
3166
|
+
"cancelled.cancelled",
|
|
3167
|
+
"failed.sample_error"
|
|
3168
|
+
]
|
|
3169
|
+
};
|
|
3170
|
+
var TRANSITION_ALIASES = {
|
|
3171
|
+
at_home_phlebotomy: {
|
|
3172
|
+
requisition_created: "received.requisition_created",
|
|
3173
|
+
scheduled: "collecting_sample.appointment_scheduled",
|
|
3174
|
+
collected: "collecting_sample.draw_completed",
|
|
3175
|
+
partial_results: "sample_with_lab.partial_results",
|
|
3176
|
+
completed: "completed.completed",
|
|
3177
|
+
corrected: "completed.corrected",
|
|
3178
|
+
cancelled: "cancelled.cancelled",
|
|
3179
|
+
failed: "failed.sample_error"
|
|
3180
|
+
},
|
|
3181
|
+
on_site_collection: {
|
|
3182
|
+
requisition_created: "received.requisition_created",
|
|
3183
|
+
collected: "sample_with_lab.draw_completed",
|
|
3184
|
+
at_lab: "sample_with_lab.draw_completed",
|
|
3185
|
+
partial_results: "sample_with_lab.partial_results",
|
|
3186
|
+
completed: "completed.completed",
|
|
3187
|
+
corrected: "completed.corrected",
|
|
3188
|
+
cancelled: "cancelled.cancelled",
|
|
3189
|
+
failed: "failed.sample_error"
|
|
3190
|
+
},
|
|
3191
|
+
testkit: {
|
|
3192
|
+
requisition_created: "received.requisition_created",
|
|
3193
|
+
shipped: "collecting_sample.transit_customer",
|
|
3194
|
+
delivered: "collecting_sample.with_customer",
|
|
3195
|
+
collected: "collecting_sample.transit_lab",
|
|
3196
|
+
at_lab: "sample_with_lab.delivered_to_lab",
|
|
3197
|
+
completed: "completed.completed",
|
|
3198
|
+
corrected: "completed.corrected",
|
|
3199
|
+
cancelled: "cancelled.cancelled",
|
|
3200
|
+
failed: "failed.sample_error"
|
|
3201
|
+
},
|
|
3202
|
+
walk_in_test: {
|
|
3203
|
+
requisition_created: "received.requisition_created",
|
|
3204
|
+
scheduled: "collecting_sample.appointment_scheduled",
|
|
3205
|
+
partial_results: "sample_with_lab.partial_results",
|
|
3206
|
+
completed: "completed.completed",
|
|
3207
|
+
corrected: "completed.corrected",
|
|
3208
|
+
cancelled: "cancelled.cancelled",
|
|
3209
|
+
failed: "failed.sample_error"
|
|
3210
|
+
}
|
|
3211
|
+
};
|
|
3212
|
+
var RESULT_FIXTURES = {
|
|
3213
|
+
normal: { interpretation: "normal" },
|
|
3214
|
+
abnormal: { interpretation: "abnormal" },
|
|
3215
|
+
critical: { interpretation: "critical" },
|
|
3216
|
+
missing_results: { interpretation: "normal", has_missing_results: true }
|
|
3217
|
+
};
|
|
3218
|
+
var FAULT_PRESETS = {
|
|
3219
|
+
sandbox_user_quota: {
|
|
3220
|
+
operationId: "create_user_v2_user_post",
|
|
3221
|
+
status: 400,
|
|
3222
|
+
body: sandboxUserQuotaBody(50)
|
|
3223
|
+
},
|
|
3224
|
+
rate_limited: {
|
|
3225
|
+
status: 429,
|
|
3226
|
+
body: { detail: "Too Many Requests" },
|
|
3227
|
+
headers: { "retry-after": "1" }
|
|
3228
|
+
},
|
|
3229
|
+
server_error: { status: 500, body: { detail: "Internal Server Error" } },
|
|
3230
|
+
bad_gateway: { status: 502, body: { detail: "Bad Gateway" } },
|
|
3231
|
+
unavailable: { status: 503, body: { detail: "Service Unavailable" } }
|
|
3232
|
+
};
|
|
3233
|
+
var methodOf = (order) => typeof order.lab_test.method === "string" && order.lab_test.method !== "" ? order.lab_test.method : typeof order.details?.type === "string" && order.details.type !== "" ? order.details.type : "at_home_phlebotomy";
|
|
3234
|
+
var resolveTransition = (method, target) => {
|
|
3235
|
+
const allowed = ORDER_STATUSES_BY_METHOD[method];
|
|
3236
|
+
if (!allowed) return { error: `unknown collection method ${method}` };
|
|
3237
|
+
const alias = TRANSITION_ALIASES[method]?.[target];
|
|
3238
|
+
const short = alias ?? (target.split(".").length === 3 ? null : target);
|
|
3239
|
+
if (short !== null && allowed.includes(short)) {
|
|
3240
|
+
const [phase, event] = short.split(".");
|
|
3241
|
+
return { status: `${phase}.${method}.${event}` };
|
|
3242
|
+
}
|
|
3243
|
+
if (target.split(".").length === 3) {
|
|
3244
|
+
const [phase, targetMethod, event] = target.split(".");
|
|
3245
|
+
if (targetMethod === method && allowed.includes(`${phase}.${event}`)) return { status: target };
|
|
3246
|
+
}
|
|
3247
|
+
const names = Object.keys(TRANSITION_ALIASES[method] ?? {});
|
|
3248
|
+
return {
|
|
3249
|
+
error: `${JSON.stringify(target)} is not a ${method} status. Use one of: ${names.join(", ")}, or a full status such as ${allowed.slice(0, 2).map((s) => `${s.split(".")[0]}.${method}.${s.split(".")[1]}`).join(", ")}`
|
|
3250
|
+
};
|
|
3251
|
+
};
|
|
3252
|
+
var adminError3 = (status, message) => jsonRes(status, { error: { type: "mockingbird_admin", message } });
|
|
3253
|
+
var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3254
|
+
var guarded = (run) => {
|
|
3255
|
+
try {
|
|
3256
|
+
return run();
|
|
3257
|
+
} catch (error) {
|
|
3258
|
+
if (error instanceof TypeError) return adminError3(400, error.message);
|
|
3259
|
+
if (error instanceof Error && error.name === "LabAccountConflict") {
|
|
3260
|
+
return adminError3(409, error.message);
|
|
3261
|
+
}
|
|
3262
|
+
const { status, message } = fixtureErrorMessage(error);
|
|
3263
|
+
return adminError3(status, message);
|
|
3264
|
+
}
|
|
3265
|
+
};
|
|
3266
|
+
var junctionAdminRoutes = (options) => (runtime) => {
|
|
3267
|
+
const api = (request2) => runtime.instance(request2.namespace);
|
|
3268
|
+
return {
|
|
3269
|
+
"GET /orders": (request2) => jsonRes(200, {
|
|
3270
|
+
orders: api(request2).orders().map((order) => ({
|
|
3271
|
+
id: order.id,
|
|
3272
|
+
user_id: order.user_id,
|
|
3273
|
+
method: methodOf(order),
|
|
3274
|
+
status: order.last_event.status,
|
|
3275
|
+
updated_at: order.updated_at
|
|
3276
|
+
}))
|
|
3277
|
+
}),
|
|
3278
|
+
"POST /orders/:id/transition": (request2) => {
|
|
3279
|
+
const body = isRecord5(request2.body) ? request2.body : {};
|
|
3280
|
+
const order = api(request2).order(request2.params.id);
|
|
3281
|
+
if (!order) return adminError3(404, `no order ${request2.params.id} in ${request2.namespace}`);
|
|
3282
|
+
if (typeof body.to !== "string") {
|
|
3283
|
+
return adminError3(400, 'body needs "to": a status like "completed" or "at_lab"');
|
|
3284
|
+
}
|
|
3285
|
+
const method = methodOf(order);
|
|
3286
|
+
const resolved = resolveTransition(method, body.to);
|
|
3287
|
+
if ("error" in resolved) return adminError3(400, resolved.error);
|
|
3288
|
+
let flags = isRecord5(body.flags) ? body.flags : null;
|
|
3289
|
+
if (typeof body.result === "string") {
|
|
3290
|
+
const named = RESULT_FIXTURES[body.result];
|
|
3291
|
+
if (!named) {
|
|
3292
|
+
return adminError3(
|
|
3293
|
+
400,
|
|
3294
|
+
`no result fixture ${body.result}; one of ${Object.keys(RESULT_FIXTURES).join(", ")}`
|
|
3295
|
+
);
|
|
3296
|
+
}
|
|
3297
|
+
flags = { ...named, ...flags };
|
|
3298
|
+
}
|
|
3299
|
+
const updated = api(request2).transitionOrder(order.id, resolved.status, {
|
|
3300
|
+
now: runtime.clock.now,
|
|
3301
|
+
flags
|
|
3302
|
+
});
|
|
3303
|
+
return jsonRes(200, {
|
|
3304
|
+
id: order.id,
|
|
3305
|
+
status: updated?.last_event.status,
|
|
3306
|
+
events: updated?.events
|
|
3307
|
+
});
|
|
3308
|
+
},
|
|
3309
|
+
"GET /result-fixtures": () => jsonRes(200, { fixtures: RESULT_FIXTURES }),
|
|
3310
|
+
"GET /results/:id": (request2) => {
|
|
3311
|
+
const fixture = api(request2).resultFixture(request2.params.id);
|
|
3312
|
+
return fixture ? jsonRes(200, fixture) : adminError3(404, "no result fixture for that order");
|
|
3313
|
+
},
|
|
3314
|
+
"PUT /results/:id": (request2) => {
|
|
3315
|
+
if (!isRecord5(request2.body)) return adminError3(400, "expected a result fixture object");
|
|
3316
|
+
const fixture = { name: "custom", ...request2.body };
|
|
3317
|
+
if (fixture.results !== void 0 && !Array.isArray(fixture.results)) {
|
|
3318
|
+
return adminError3(400, "results must be an array of result lines");
|
|
3319
|
+
}
|
|
3320
|
+
return api(request2).installResultFixture(request2.params.id, fixture) ? jsonRes(200, fixture) : adminError3(404, `no order ${request2.params.id} in ${request2.namespace}`);
|
|
3321
|
+
},
|
|
3322
|
+
"GET /lab-accounts": (request2) => jsonRes(200, { data: api(request2).labAccounts() }),
|
|
3323
|
+
"PUT /lab-accounts": (request2) => {
|
|
3324
|
+
const body = isRecord5(request2.body) ? request2.body : {};
|
|
3325
|
+
const accounts = body.accounts;
|
|
3326
|
+
const presets = body.presets;
|
|
3327
|
+
if (accounts === void 0 && presets === void 0 || accounts !== void 0 && accounts !== null && !Array.isArray(accounts) || presets !== void 0 && !Array.isArray(presets)) {
|
|
3328
|
+
return adminError3(
|
|
3329
|
+
400,
|
|
3330
|
+
'body needs "accounts": an array (or null to restore the default), and/or "presets": an array of preset names'
|
|
3331
|
+
);
|
|
3332
|
+
}
|
|
3333
|
+
const layout = presets !== void 0 ? {
|
|
3334
|
+
presets,
|
|
3335
|
+
...Array.isArray(accounts) ? { accounts } : {}
|
|
3336
|
+
} : accounts === null ? void 0 : accounts;
|
|
3337
|
+
return guarded(() => jsonRes(200, { data: api(request2).configureLabAccounts(layout) }));
|
|
3338
|
+
},
|
|
3339
|
+
"POST /lab-accounts": (request2) => {
|
|
3340
|
+
if (!isRecord5(request2.body)) return adminError3(400, "expected one lab account object");
|
|
3341
|
+
const input = request2.body;
|
|
3342
|
+
return guarded(() => jsonRes(201, api(request2).addLabAccount(input)));
|
|
3343
|
+
},
|
|
3344
|
+
"PATCH /lab-accounts/:id": (request2) => {
|
|
3345
|
+
if (!isRecord5(request2.body))
|
|
3346
|
+
return adminError3(400, "expected an object of fields to change");
|
|
3347
|
+
const patch = request2.body;
|
|
3348
|
+
return guarded(() => {
|
|
3349
|
+
const updated = api(request2).patchLabAccount(request2.params.id, patch);
|
|
3350
|
+
return updated ? jsonRes(200, updated) : adminError3(404, `no lab account ${request2.params.id} in ${request2.namespace}`);
|
|
3351
|
+
});
|
|
3352
|
+
},
|
|
3353
|
+
"DELETE /lab-accounts/:id": (request2) => api(request2).removeLabAccount(request2.params.id) ? jsonRes(200, { status: "ok" }) : adminError3(404, `no lab account ${request2.params.id} in ${request2.namespace}`),
|
|
3354
|
+
"GET /lab-accounts/presets": (request2) => jsonRes(200, {
|
|
3355
|
+
presets: Object.fromEntries(
|
|
3356
|
+
Object.keys(LAB_ACCOUNT_PRESETS).map((name) => {
|
|
3357
|
+
const record = labAccountFromPreset(name, { teamId: api(request2).teamId });
|
|
3358
|
+
return [name, record ? renderLabAccount(record) : null];
|
|
3359
|
+
})
|
|
3360
|
+
)
|
|
3361
|
+
}),
|
|
3362
|
+
"POST /lab-accounts/presets/:name": (request2) => {
|
|
3363
|
+
const body = isRecord5(request2.body) ? request2.body : {};
|
|
3364
|
+
if (body.id !== void 0 && typeof body.id !== "string") {
|
|
3365
|
+
return adminError3(400, "id must be a string");
|
|
3366
|
+
}
|
|
3367
|
+
const name = request2.params.name;
|
|
3368
|
+
return guarded(() => {
|
|
3369
|
+
const record = api(request2).addLabAccountPreset(name, body.id);
|
|
3370
|
+
return record ? jsonRes(201, record) : adminError3(
|
|
3371
|
+
404,
|
|
3372
|
+
`no lab-account preset ${name}; one of ${Object.keys(LAB_ACCOUNT_PRESETS).join(", ")}`
|
|
3373
|
+
);
|
|
3374
|
+
});
|
|
3375
|
+
},
|
|
3376
|
+
"GET /team": (request2) => jsonRes(200, { teamId: api(request2).teamId }),
|
|
3377
|
+
"GET /limits": (request2) => jsonRes(200, { limits: api(request2).limits }),
|
|
3378
|
+
"PUT /limits": (request2) => {
|
|
3379
|
+
if (!isRecord5(request2.body)) return adminError3(400, "expected an object of limits");
|
|
3380
|
+
const input = request2.body;
|
|
3381
|
+
return guarded(() => jsonRes(200, { limits: api(request2).configureLimits(input) }));
|
|
3382
|
+
},
|
|
3383
|
+
"GET /identity": (request2) => jsonRes(200, { identity: api(request2).identity }),
|
|
3384
|
+
"PUT /identity": (request2) => {
|
|
3385
|
+
const mode = isRecord5(request2.body) ? request2.body.mode : void 0;
|
|
3386
|
+
if (!IDENTITY_MODES.includes(mode)) {
|
|
3387
|
+
return adminError3(400, `body needs "mode": one of ${IDENTITY_MODES.join(", ")}`);
|
|
3388
|
+
}
|
|
3389
|
+
api(request2).identity = mode;
|
|
3390
|
+
return jsonRes(200, { identity: mode });
|
|
3391
|
+
},
|
|
3392
|
+
"POST /users": (request2) => {
|
|
3393
|
+
if (!isRecord5(request2.body)) return adminError3(400, "expected one user object");
|
|
3394
|
+
const input = request2.body;
|
|
3395
|
+
return guarded(() => jsonRes(201, api(request2).insertUsers([input])[0]));
|
|
3396
|
+
},
|
|
3397
|
+
"POST /users/bulk": (request2) => {
|
|
3398
|
+
const users = isRecord5(request2.body) ? request2.body.users : void 0;
|
|
3399
|
+
if (!Array.isArray(users)) return adminError3(400, 'body needs "users": an array');
|
|
3400
|
+
return guarded(
|
|
3401
|
+
() => jsonRes(201, { users: api(request2).insertUsers(users) })
|
|
3402
|
+
);
|
|
3403
|
+
},
|
|
3404
|
+
"DELETE /users/:id": (request2) => api(request2).hardDeleteUser(request2.params.id) ? jsonRes(200, { status: "ok" }) : adminError3(404, `no user ${request2.params.id} in ${request2.namespace}`),
|
|
3405
|
+
"POST /orders": (request2) => {
|
|
3406
|
+
if (!isRecord5(request2.body)) return adminError3(400, "expected one order object");
|
|
3407
|
+
const { emitWebhooks, ...input } = request2.body;
|
|
3408
|
+
return guarded(
|
|
3409
|
+
() => jsonRes(
|
|
3410
|
+
201,
|
|
3411
|
+
api(request2).insertOrders([input], {
|
|
3412
|
+
emitWebhooks: emitWebhooks === true
|
|
3413
|
+
})[0]
|
|
3414
|
+
)
|
|
3415
|
+
);
|
|
3416
|
+
},
|
|
3417
|
+
"POST /import": (request2) => {
|
|
3418
|
+
if (!isRecord5(request2.body)) {
|
|
3419
|
+
return adminError3(400, 'expected { "users": [...], "orders": [...] }');
|
|
3420
|
+
}
|
|
3421
|
+
const { emitWebhooks, ...fixtures } = request2.body;
|
|
3422
|
+
return guarded(() => {
|
|
3423
|
+
const loaded = api(request2).importFixtures(fixtures, {
|
|
3424
|
+
emitWebhooks: emitWebhooks === true
|
|
3425
|
+
});
|
|
3426
|
+
return jsonRes(201, loaded);
|
|
3427
|
+
});
|
|
3428
|
+
},
|
|
3429
|
+
"GET /corpus": (request2) => jsonRes(200, {
|
|
3430
|
+
corpus: api(request2).corpusInfo() ?? null,
|
|
3431
|
+
geo: api(request2).geoMode
|
|
3432
|
+
}),
|
|
3433
|
+
"PUT /geo": (request2) => {
|
|
3434
|
+
const mode = isRecord5(request2.body) ? request2.body.mode : void 0;
|
|
3435
|
+
if (mode !== "corpus" && mode !== "synthetic") {
|
|
3436
|
+
return adminError3(400, 'body needs "mode": "corpus" or "synthetic"');
|
|
3437
|
+
}
|
|
3438
|
+
api(request2).geoMode = mode;
|
|
3439
|
+
return jsonRes(200, { geo: mode });
|
|
3440
|
+
},
|
|
3441
|
+
"GET /webhooks/events": (request2) => jsonRes(200, { events: api(request2).webhookEvents() }),
|
|
3442
|
+
"GET /webhooks": (request2) => options.webhooks ? jsonRes(200, { deliveries: options.webhooks.deliveries(request2.namespace) }) : adminError3(
|
|
3443
|
+
409,
|
|
3444
|
+
"webhook delivery is off; serve with --webhook-url and --webhook-secret"
|
|
3445
|
+
),
|
|
3446
|
+
"POST /webhooks/:id/replay": async (request2) => {
|
|
3447
|
+
if (!options.webhooks) return adminError3(409, "webhook delivery is off");
|
|
3448
|
+
const delivery = await options.webhooks.replay(request2.params.id);
|
|
3449
|
+
return delivery ? jsonRes(200, delivery) : adminError3(404, `no message ${request2.params.id}`);
|
|
3450
|
+
},
|
|
3451
|
+
"POST /webhooks/flush": async () => {
|
|
3452
|
+
if (!options.webhooks) return adminError3(409, "webhook delivery is off");
|
|
3453
|
+
await options.webhooks.flush();
|
|
3454
|
+
return jsonRes(200, { status: "ok" });
|
|
3455
|
+
},
|
|
3456
|
+
"GET /faults/presets": () => jsonRes(200, { presets: FAULT_PRESETS }),
|
|
3457
|
+
"POST /faults/presets/:name": (request2) => {
|
|
3458
|
+
const preset = FAULT_PRESETS[request2.params.name];
|
|
3459
|
+
if (!preset) {
|
|
3460
|
+
return adminError3(
|
|
3461
|
+
404,
|
|
3462
|
+
`no preset ${request2.params.name}; one of ${Object.keys(FAULT_PRESETS).join(", ")}`
|
|
3463
|
+
);
|
|
3464
|
+
}
|
|
3465
|
+
const overrides = isRecord5(request2.body) ? request2.body : {};
|
|
3466
|
+
const rule = runtime.faults.add({
|
|
3467
|
+
namespace: request2.namespace,
|
|
3468
|
+
...preset,
|
|
3469
|
+
...overrides,
|
|
3470
|
+
id: typeof overrides.id === "string" ? overrides.id : request2.params.name
|
|
3471
|
+
});
|
|
3472
|
+
return jsonRes(201, rule);
|
|
3473
|
+
}
|
|
3474
|
+
};
|
|
3475
|
+
};
|
|
3476
|
+
|
|
3477
|
+
// src/not-found.ts
|
|
3478
|
+
var ORDER_NOT_FOUND = {
|
|
3479
|
+
get_order_v3_order__order_id__get: "This order doesn't exist",
|
|
3480
|
+
cancel_order_v3_order__order_id__cancel_post: "Order doesn't exist",
|
|
3481
|
+
simulate_order_v3_order__order_id__test_post: "Order doesn't exist",
|
|
3482
|
+
get_order_requisition_pdf_v3_order__order_id__requisition_pdf_get: "This order doesn't exist",
|
|
3483
|
+
get_result_raw_v3_order__order_id__result_get: "Order not found",
|
|
3484
|
+
get_result_metadata_v3_order__order_id__result_metadata_get: "Order not found",
|
|
3485
|
+
get_result_pdf_v3_order__order_id__result_pdf_get: "Order not found",
|
|
3486
|
+
get_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_get: "This order doesn't exist.",
|
|
3487
|
+
book_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_book_post: "This order doesn't exist.",
|
|
3488
|
+
reschedule_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_reschedule_patch: "This order doesn't exist.",
|
|
3489
|
+
cancel_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_cancel_patch: "This order doesn't exist.",
|
|
3490
|
+
get_psc_appointment_v3_order__order_id__psc_appointment_get: "This order doesn't exist.",
|
|
3491
|
+
book_psc_appointment_v3_order__order_id__psc_appointment_book_post: "This order doesn't exist.",
|
|
3492
|
+
reschedule_psc_appointment_v3_order__order_id__psc_appointment_reschedule_patch: "This order doesn't exist.",
|
|
3493
|
+
cancel_psc_appointment_v3_order__order_id__psc_appointment_cancel_patch: "This order doesn't exist."
|
|
3494
|
+
};
|
|
3495
|
+
var MISS_HEADER = "x-mockingbird-miss";
|
|
3496
|
+
var KNOWN_HEADER = "x-mockingbird-known";
|
|
3497
|
+
function missing(state, kind, id, detail) {
|
|
3498
|
+
throw new HttpError(
|
|
3499
|
+
404,
|
|
3500
|
+
{ detail },
|
|
3501
|
+
{
|
|
3502
|
+
// Percent-encoded: an id is caller input, and a header value must be ASCII.
|
|
3503
|
+
[MISS_HEADER]: `${kind} ${encodeURIComponent(id).replace(/%3A/gi, ":")}`,
|
|
3504
|
+
[KNOWN_HEADER]: `users=${state.users.count()} orders=${state.orders.count()}`
|
|
3505
|
+
}
|
|
3506
|
+
);
|
|
3507
|
+
}
|
|
3508
|
+
function orderMissing(state, operationId, id) {
|
|
3509
|
+
return missing(state, "order", id, ORDER_NOT_FOUND[operationId] ?? "This order doesn't exist");
|
|
3510
|
+
}
|
|
3511
|
+
|
|
3512
|
+
// src/scheduling.ts
|
|
3513
|
+
var UNKNOWN_ZIP_STATUS = 424;
|
|
3514
|
+
var UNKNOWN_ZIP_ERROR_TYPE = "MOCKINGBIRD_UNKNOWN_ZIP";
|
|
3515
|
+
var requireCoveredZip = (state, zip) => {
|
|
3516
|
+
if (state.geoMode !== "corpus" || state.coveredZips.has(zip)) return;
|
|
3517
|
+
throw new HttpError(UNKNOWN_ZIP_STATUS, {
|
|
3518
|
+
detail: {
|
|
3519
|
+
error_type: UNKNOWN_ZIP_ERROR_TYPE,
|
|
3520
|
+
error_message: `ZIP ${zip} is not in the loaded Junction corpus (${String(state.coveredZips.size)} ZIPs covered). Record it with \`mockingbird-junction corpus pull --zip ${zip}\`, or serve with --geo synthetic to invent coverage.`,
|
|
3521
|
+
zip_code: zip,
|
|
3522
|
+
corpus: state.corpusLabel ?? null
|
|
3523
|
+
}
|
|
3524
|
+
});
|
|
3525
|
+
};
|
|
3526
|
+
var cachedResponse = (state, context) => {
|
|
3527
|
+
const body = context.request.method.toUpperCase() === "GET" || context.request.method.toUpperCase() === "HEAD" ? void 0 : context.body.kind === "json" ? context.body.value : context.body.kind === "form" ? context.body.value : void 0;
|
|
3528
|
+
const key = state.cacheKeyForRequest(
|
|
3529
|
+
context.request.method,
|
|
3530
|
+
context.url.pathname,
|
|
3531
|
+
context.url.searchParams,
|
|
3532
|
+
body
|
|
3533
|
+
);
|
|
3534
|
+
const cached = state.getGetCache(key);
|
|
3535
|
+
if (!cached) return void 0;
|
|
3536
|
+
if (key.toLowerCase().includes("availability")) {
|
|
3537
|
+
materializeAvailabilityBookingKeys(state, context, cached.body);
|
|
3538
|
+
rotateAvailabilityBookingKeys(state, cached.body, context.now());
|
|
3539
|
+
}
|
|
3540
|
+
const headers = new Headers(cached.headers);
|
|
3541
|
+
headers.delete("content-encoding");
|
|
3542
|
+
headers.delete("content-length");
|
|
3543
|
+
headers.delete("transfer-encoding");
|
|
3544
|
+
if (!headers.has("content-type")) headers.set("content-type", "application/json");
|
|
3545
|
+
return new Response(JSON.stringify(cached.body), { status: cached.status, headers });
|
|
3546
|
+
};
|
|
3547
|
+
var materializeAvailabilityBookingKeys = (state, context, cachedBody) => {
|
|
3548
|
+
const requestBody = context.body.kind === "json" && typeof context.body.value === "object" && context.body.value !== null && !Array.isArray(context.body.value) ? context.body.value : {};
|
|
3549
|
+
const zip = typeof requestBody.zip_code === "string" && /^\d{5}/.test(requestBody.zip_code) ? requestBody.zip_code.slice(0, 5) : "85004";
|
|
3550
|
+
const path = context.url.pathname;
|
|
3551
|
+
const modalityHint = path.includes("psc") ? "patient_service_center" : path.includes("phlebotomy") ? "phlebotomy" : void 0;
|
|
3552
|
+
state.hydrateBookingKeysFromAvailability(cachedBody, context.now(), zip, modalityHint);
|
|
3553
|
+
if (typeof cachedBody === "object" && cachedBody !== null && !Array.isArray(cachedBody)) {
|
|
3554
|
+
const root = cachedBody;
|
|
3555
|
+
const dayBuckets = [];
|
|
3556
|
+
if (Array.isArray(root.days)) dayBuckets.push(...root.days);
|
|
3557
|
+
if (Array.isArray(root.slots)) dayBuckets.push(...root.slots);
|
|
3558
|
+
for (const day of dayBuckets) {
|
|
3559
|
+
if (typeof day !== "object" || day === null || Array.isArray(day)) continue;
|
|
3560
|
+
const daySlots = day.slots;
|
|
3561
|
+
const slots = Array.isArray(daySlots) ? daySlots : [day];
|
|
3562
|
+
for (const entry of slots) {
|
|
3563
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) continue;
|
|
3564
|
+
const slot = entry;
|
|
3565
|
+
const bookingKey = typeof slot.booking_key === "string" ? slot.booking_key : "";
|
|
3566
|
+
if (!bookingKey) continue;
|
|
3567
|
+
const record = state.bookingKeys.get(bookingKey);
|
|
3568
|
+
if (!record) continue;
|
|
3569
|
+
if (modalityHint) record.modality = modalityHint;
|
|
3570
|
+
record.zip_code = zip;
|
|
3571
|
+
record.provider = record.modality === "patient_service_center" ? "quest" : "getlabs";
|
|
3572
|
+
record.address = addressFromAvailabilityRequest(requestBody, zip);
|
|
3573
|
+
record.location = zipLocation(zip);
|
|
3574
|
+
state.bookingKeys.update(bookingKey, record);
|
|
3575
|
+
}
|
|
3576
|
+
}
|
|
3577
|
+
}
|
|
3578
|
+
};
|
|
3579
|
+
var rotateAvailabilityBookingKeys = (state, cachedBody, nowMs) => {
|
|
3580
|
+
if (typeof cachedBody !== "object" || cachedBody === null || Array.isArray(cachedBody)) return;
|
|
3581
|
+
const root = cachedBody;
|
|
3582
|
+
const dayBuckets = [];
|
|
3583
|
+
if (Array.isArray(root.days)) dayBuckets.push(...root.days);
|
|
3584
|
+
if (Array.isArray(root.slots)) dayBuckets.push(...root.slots);
|
|
3585
|
+
for (const day of dayBuckets) {
|
|
3586
|
+
if (typeof day !== "object" || day === null || Array.isArray(day)) continue;
|
|
3587
|
+
const daySlots = day.slots;
|
|
3588
|
+
const slots = Array.isArray(daySlots) ? daySlots : [day];
|
|
3589
|
+
for (let index = 0; index < slots.length; index += 1) {
|
|
3590
|
+
const slot = slots[index];
|
|
3591
|
+
if (typeof slot !== "object" || slot === null || Array.isArray(slot)) continue;
|
|
3592
|
+
const entry = slot;
|
|
3593
|
+
const sealedKey = typeof entry.booking_key === "string" ? entry.booking_key : "";
|
|
3594
|
+
if (!sealedKey) continue;
|
|
3595
|
+
const sealed = state.bookingKeys.get(sealedKey);
|
|
3596
|
+
if (!sealed) continue;
|
|
3597
|
+
const freshKey = bookingKeyRotationFor(state, sealedKey, nowMs, index);
|
|
3598
|
+
const record = {
|
|
3599
|
+
...sealed,
|
|
3600
|
+
key: freshKey,
|
|
3601
|
+
consumed_by_order_id: null,
|
|
3602
|
+
created_at: new Date(nowMs).toISOString()
|
|
3603
|
+
};
|
|
3604
|
+
state.bookingKeys.insert(freshKey, record);
|
|
3605
|
+
entry.booking_key = freshKey;
|
|
3606
|
+
}
|
|
3607
|
+
}
|
|
3608
|
+
};
|
|
3609
|
+
var bookingKeyRotationFor = (state, sealedKey, nowMs, index) => {
|
|
3610
|
+
for (let attempt = 0; attempt < 64; attempt += 1) {
|
|
3611
|
+
const candidate = state.bookingKeyFor(`rotation:${sealedKey}:${nowMs}:${index}:${attempt}`);
|
|
3612
|
+
if (!state.bookingKeys.has(candidate)) return candidate;
|
|
3613
|
+
}
|
|
3614
|
+
return state.bookingKeyFor(
|
|
3615
|
+
`rotation:${sealedKey}:${nowMs}:${index}:fallback:${state.bookingKeys.list().length}`
|
|
3616
|
+
);
|
|
3617
|
+
};
|
|
3618
|
+
var rotateGeneratedAvailabilityKeys = (state, days, nowMs, override) => {
|
|
3619
|
+
for (const day of days) {
|
|
3620
|
+
const slots = Array.isArray(day.slots) ? day.slots : [];
|
|
3621
|
+
for (let index = 0; index < slots.length; index += 1) {
|
|
3622
|
+
const slot = slots[index];
|
|
3623
|
+
if (typeof slot !== "object" || slot === null || Array.isArray(slot)) continue;
|
|
3624
|
+
const entry = slot;
|
|
3625
|
+
const currentKey = typeof entry.booking_key === "string" ? entry.booking_key : "";
|
|
3626
|
+
if (!currentKey) continue;
|
|
3627
|
+
const current = state.bookingKeys.get(currentKey);
|
|
3628
|
+
if (!current) continue;
|
|
3629
|
+
const freshKey = bookingKeyRotationFor(state, currentKey, nowMs, index);
|
|
3630
|
+
const record = {
|
|
3631
|
+
...current,
|
|
3632
|
+
key: freshKey,
|
|
3633
|
+
...override ?? {},
|
|
3634
|
+
consumed_by_order_id: null,
|
|
3635
|
+
created_at: new Date(nowMs).toISOString()
|
|
3636
|
+
};
|
|
3637
|
+
state.bookingKeys.insert(freshKey, record);
|
|
3638
|
+
entry.booking_key = freshKey;
|
|
3639
|
+
}
|
|
3640
|
+
}
|
|
3641
|
+
};
|
|
3642
|
+
var zipTimezone = (zip) => {
|
|
3643
|
+
const prefix = Number(zip.slice(0, 3));
|
|
3644
|
+
if (!Number.isFinite(prefix) || prefix === 0) return "America/New_York";
|
|
3645
|
+
if (prefix < 500) return "America/New_York";
|
|
3646
|
+
if (prefix < 800) return "America/Chicago";
|
|
3647
|
+
if (prefix >= 850 && prefix <= 865) return "America/Phoenix";
|
|
3648
|
+
if (prefix < 900) return "America/Denver";
|
|
2623
3649
|
return "America/Los_Angeles";
|
|
2624
3650
|
};
|
|
2625
3651
|
var ZIP_LOCATION = {
|
|
@@ -2934,7 +3960,7 @@ var zipCodeOf = (context, required) => {
|
|
|
2934
3960
|
if (typeof raw !== "string") throw new HttpError(422, { detail: "zip_code must be a string" });
|
|
2935
3961
|
return raw;
|
|
2936
3962
|
};
|
|
2937
|
-
var labAccountOf = (context) => {
|
|
3963
|
+
var labAccountOf = (state, context) => {
|
|
2938
3964
|
const raw = context.query.lab_account_id;
|
|
2939
3965
|
if (raw === void 0) return null;
|
|
2940
3966
|
if (typeof raw !== "string" || !isUuid(raw)) {
|
|
@@ -2942,8 +3968,8 @@ var labAccountOf = (context) => {
|
|
|
2942
3968
|
detail: [uuidError(typeof raw === "string" ? raw : String(raw), ["query", "lab_account_id"])]
|
|
2943
3969
|
});
|
|
2944
3970
|
}
|
|
2945
|
-
const account2 = labAccountById(raw);
|
|
2946
|
-
if (!account2
|
|
3971
|
+
const account2 = labAccountById(raw, state.listLabAccounts());
|
|
3972
|
+
if (!account2 || !isLinkedToTeam(account2, state.teamId))
|
|
2947
3973
|
throw new HttpError(404, { detail: "Lab account does not exist" });
|
|
2948
3974
|
if (account2.status !== "active") throw new HttpError(400, { detail: "Lab account is not active" });
|
|
2949
3975
|
return account2;
|
|
@@ -3137,19 +4163,15 @@ function notFound(message) {
|
|
|
3137
4163
|
function invalidBookingKey() {
|
|
3138
4164
|
throw new HttpError(400, { detail: "Invalid or expired booking key" });
|
|
3139
4165
|
}
|
|
3140
|
-
var requireOrder = (state, orderId, context
|
|
4166
|
+
var requireOrder = (state, orderId, context) => {
|
|
3141
4167
|
if (!isUuid(orderId)) {
|
|
3142
4168
|
throw new HttpError(422, { detail: [uuidError(orderId, ["path", "order_id"])] });
|
|
3143
4169
|
}
|
|
3144
|
-
const
|
|
3145
|
-
if (order === void 0) notFound(missingMessage);
|
|
3146
|
-
const loaded = order;
|
|
4170
|
+
const loaded = state.orders.get(orderId) ?? orderMissing(state, context.operation.operationId, orderId);
|
|
3147
4171
|
state.applyDueSimulateTransitions(context.now(), (_due, finalStatus, flags) => {
|
|
3148
4172
|
applySimulateTransition(state, loaded, finalStatus, flags, context);
|
|
3149
4173
|
});
|
|
3150
|
-
|
|
3151
|
-
if (fresh === void 0) notFound(missingMessage);
|
|
3152
|
-
return fresh;
|
|
4174
|
+
return state.orders.get(orderId) ?? orderMissing(state, context.operation.operationId, orderId);
|
|
3153
4175
|
};
|
|
3154
4176
|
var orderCollectionMethod = (order) => {
|
|
3155
4177
|
if (typeof order.lab_test.method === "string" && order.lab_test.method.length > 0) {
|
|
@@ -3376,7 +4398,7 @@ var schedulingHandlers = (state) => ({
|
|
|
3376
4398
|
}
|
|
3377
4399
|
const zip = rawZip.slice(0, 5);
|
|
3378
4400
|
requireCoveredZip(state, zip);
|
|
3379
|
-
return jsonRes(200, areaInfoFor(zip, radius, labAccountOf(context)));
|
|
4401
|
+
return jsonRes(200, areaInfoFor(zip, radius, labAccountOf(state, context)));
|
|
3380
4402
|
},
|
|
3381
4403
|
get_psc_info_v3_order_psc_info_get: async (context) => {
|
|
3382
4404
|
const hit = cachedResponse(state, context);
|
|
@@ -4066,6 +5088,80 @@ var labAccountStatusFilter = (context) => {
|
|
|
4066
5088
|
]
|
|
4067
5089
|
});
|
|
4068
5090
|
};
|
|
5091
|
+
var enumError = (name, value, allowed) => {
|
|
5092
|
+
const expected = allowed.length === 1 ? `'${allowed[0]}'` : `${allowed.slice(0, -1).map((entry) => `'${entry}'`).join(", ")} or '${allowed.at(-1)}'`;
|
|
5093
|
+
return {
|
|
5094
|
+
type: "enum",
|
|
5095
|
+
loc: ["query", name],
|
|
5096
|
+
msg: `Input should be ${expected}`,
|
|
5097
|
+
input: value,
|
|
5098
|
+
ctx: { expected }
|
|
5099
|
+
};
|
|
5100
|
+
};
|
|
5101
|
+
var listParam = (params, name) => {
|
|
5102
|
+
const values = params.getAll(name);
|
|
5103
|
+
if (values.length === 0) return null;
|
|
5104
|
+
if (values.length === 1 && values[0]?.trim().startsWith("[")) {
|
|
5105
|
+
try {
|
|
5106
|
+
const parsed = JSON.parse(values[0]);
|
|
5107
|
+
if (Array.isArray(parsed)) return parsed.map(String);
|
|
5108
|
+
} catch {
|
|
5109
|
+
}
|
|
5110
|
+
}
|
|
5111
|
+
return values;
|
|
5112
|
+
};
|
|
5113
|
+
var LAB_TEST_FILTER_ENUMS = {
|
|
5114
|
+
generation_method: ["auto", "manual", "all"],
|
|
5115
|
+
collection_method: ["testkit", "walk_in_test", "at_home_phlebotomy", "on_site_collection"],
|
|
5116
|
+
status: ["active", "pending_approval", "inactive"],
|
|
5117
|
+
order_key: ["price", "created_at", "updated_at"],
|
|
5118
|
+
order_direction: ["asc", "desc"]
|
|
5119
|
+
};
|
|
5120
|
+
var filterLabTests = (tests, params) => {
|
|
5121
|
+
const errors = [];
|
|
5122
|
+
const pick = (name2) => {
|
|
5123
|
+
const value = params.get(name2);
|
|
5124
|
+
if (value === null) return null;
|
|
5125
|
+
const allowed = LAB_TEST_FILTER_ENUMS[name2];
|
|
5126
|
+
if (allowed && !allowed.includes(value)) errors.push(enumError(name2, value, allowed));
|
|
5127
|
+
return value;
|
|
5128
|
+
};
|
|
5129
|
+
const generation = pick("generation_method") ?? "manual";
|
|
5130
|
+
const collection = pick("collection_method");
|
|
5131
|
+
const status = pick("status");
|
|
5132
|
+
const orderKey = pick("order_key");
|
|
5133
|
+
const direction = pick("order_direction") ?? "asc";
|
|
5134
|
+
const labSlug = params.get("lab_slug");
|
|
5135
|
+
const name = params.get("name")?.toLowerCase() ?? null;
|
|
5136
|
+
const markerIds = listParam(params, "marker_ids");
|
|
5137
|
+
const providerIds = listParam(params, "provider_ids");
|
|
5138
|
+
for (const id of markerIds ?? []) {
|
|
5139
|
+
if (!/^-?\d+$/.test(id))
|
|
5140
|
+
errors.push({
|
|
5141
|
+
type: "int_parsing",
|
|
5142
|
+
loc: ["query", "marker_ids", markerIds?.indexOf(id) ?? 0],
|
|
5143
|
+
msg: "Input should be a valid integer, unable to parse string as an integer",
|
|
5144
|
+
input: id
|
|
5145
|
+
});
|
|
5146
|
+
}
|
|
5147
|
+
if (errors.length > 0) throw new HttpError(422, { detail: errors });
|
|
5148
|
+
const filtered = tests.filter((test) => {
|
|
5149
|
+
if (generation === "manual" && test.auto_generated === true) return false;
|
|
5150
|
+
if (generation === "auto" && test.auto_generated !== true) return false;
|
|
5151
|
+
if (collection !== null && test.method !== collection) return false;
|
|
5152
|
+
if (status !== null && test.status !== status) return false;
|
|
5153
|
+
if (labSlug !== null && String(test.lab?.slug ?? "") !== labSlug) return false;
|
|
5154
|
+
if (name !== null && !test.name.toLowerCase().includes(name)) return false;
|
|
5155
|
+
const markers = test.markers ?? [];
|
|
5156
|
+
if (markerIds && !markerIds.every((id) => markers.some((m) => String(m.id) === id)))
|
|
5157
|
+
return false;
|
|
5158
|
+
if (providerIds && !providerIds.every((id) => markers.some((m) => String(m.provider_id ?? "") === id)))
|
|
5159
|
+
return false;
|
|
5160
|
+
return true;
|
|
5161
|
+
});
|
|
5162
|
+
const ordered = orderKey === "price" ? [...filtered].sort((a, b) => (a.price ?? 0) - (b.price ?? 0)) : filtered;
|
|
5163
|
+
return direction === "desc" ? [...ordered].reverse() : ordered;
|
|
5164
|
+
};
|
|
4069
5165
|
var isValidPhone = (value) => {
|
|
4070
5166
|
const digits = value.replace(/\D/g, "");
|
|
4071
5167
|
if (digits.length < 10 || digits.length > 15) return false;
|
|
@@ -4452,6 +5548,9 @@ var orderHandlers = (state) => ({
|
|
|
4452
5548
|
data: state.listLabTests().filter((test) => test.auto_generated !== true),
|
|
4453
5549
|
next_cursor: null
|
|
4454
5550
|
}),
|
|
5551
|
+
// Deprecated upstream but still what team-catalog readers call: the same records as the
|
|
5552
|
+
// paged `/v3/lab_test`, as a bare array, filtered the way the real endpoint documents.
|
|
5553
|
+
get_lab_tests_for_team_v3_lab_tests_get: async (context) => jsonRes(200, filterLabTests(state.listLabTests(), context.url.searchParams)),
|
|
4455
5554
|
get_lab_test_for_team_v3_lab_tests__lab_test_id__get: async (context) => {
|
|
4456
5555
|
const id = context.params.lab_test_id ?? "";
|
|
4457
5556
|
const test = state.labTestById(id);
|
|
@@ -4462,7 +5561,7 @@ var orderHandlers = (state) => ({
|
|
|
4462
5561
|
get_team_lab_accounts_v3_lab_test_lab_account_get: async (context) => {
|
|
4463
5562
|
const requestedId = labAccountIdFilter(context);
|
|
4464
5563
|
const status = labAccountStatusFilter(context);
|
|
4465
|
-
const data = state.listLabAccounts().filter((entry) => entry.
|
|
5564
|
+
const data = state.listLabAccounts().filter((entry) => isLinkedToTeam(entry, state.teamId)).filter((entry) => requestedId === null || entry.id === requestedId).filter((entry) => status === null || entry.status === status).map(renderLabAccount);
|
|
4466
5565
|
return jsonRes(200, { data });
|
|
4467
5566
|
},
|
|
4468
5567
|
get_markers_for_lab_test_v3_lab_tests__lab_test_id__markers_get: async (context) => {
|
|
@@ -4527,8 +5626,7 @@ var orderHandlers = (state) => ({
|
|
|
4527
5626
|
}
|
|
4528
5627
|
const userId = body.user_id;
|
|
4529
5628
|
if (typeof userId !== "string") throw new HttpError(422, { detail: "user_id must be a string" });
|
|
4530
|
-
|
|
4531
|
-
notFound2("User does not exist on this team");
|
|
5629
|
+
requireUser(state, userId, context);
|
|
4532
5630
|
const phone = body.patient_details?.phone_number;
|
|
4533
5631
|
if (typeof phone === "string" && /^\+1\d{10}$/.test(phone) && phone.slice(2, 5) === "555") {
|
|
4534
5632
|
throw new HttpError(400, { detail: "Phone number is not correct" });
|
|
@@ -4577,7 +5675,7 @@ var orderHandlers = (state) => ({
|
|
|
4577
5675
|
const labAccount = selectLabAccount(
|
|
4578
5676
|
labSlug,
|
|
4579
5677
|
typeof body.lab_account_id === "string" ? body.lab_account_id : null,
|
|
4580
|
-
|
|
5678
|
+
state.teamId,
|
|
4581
5679
|
state.listLabAccounts()
|
|
4582
5680
|
);
|
|
4583
5681
|
const billingType = typeof body.billing_type === "string" && body.billing_type !== "" ? body.billing_type : "client_bill";
|
|
@@ -4596,10 +5694,7 @@ var orderHandlers = (state) => ({
|
|
|
4596
5694
|
throw new HttpError(400, {
|
|
4597
5695
|
detail: "Commercial insurance orders require at least one ICD code in icd_codes"
|
|
4598
5696
|
});
|
|
4599
|
-
const nowIso = state.isoNow(context.now);
|
|
4600
|
-
const nowMicro = new Date(context.now()).toISOString();
|
|
4601
5697
|
const orderId = state.nextOrderId();
|
|
4602
|
-
const transactionId = state.transactionIdFor(orderId);
|
|
4603
5698
|
const method = typeof body.collection_method === "string" ? body.collection_method : labTest.method;
|
|
4604
5699
|
const nativeMethod = typeof labTest.method === "string" && labTest.method.length > 0 ? labTest.method : method;
|
|
4605
5700
|
const markerCount = labTests.reduce((sum, test) => sum + (test.markers?.length ?? 0), 0);
|
|
@@ -4616,117 +5711,28 @@ var orderHandlers = (state) => ({
|
|
|
4616
5711
|
if (replay.fingerprint !== requestFingerprint)
|
|
4617
5712
|
throw new HttpError(400, {
|
|
4618
5713
|
detail: "Idempotency key was reused with a different request"
|
|
4619
|
-
});
|
|
4620
|
-
return jsonRes(200, replay.response);
|
|
4621
|
-
}
|
|
4622
|
-
}
|
|
4623
|
-
const eventStatus = `received.${method}.ordered`;
|
|
4624
|
-
const eventId = Number.parseInt(opaqueToken(`junction:order-event:${orderId}`, 8), 16) % 1e9 || 1;
|
|
4625
|
-
const event = {
|
|
4626
|
-
id: eventId,
|
|
4627
|
-
created_at: nowIso,
|
|
4628
|
-
status: eventStatus,
|
|
4629
|
-
status_detail: null
|
|
4630
|
-
};
|
|
4631
|
-
let orderLabTest;
|
|
4632
|
-
if (method === nativeMethod) {
|
|
4633
|
-
orderLabTest = labTest;
|
|
4634
|
-
} else {
|
|
4635
|
-
const markerFingerprint = (test) => (test.markers ?? []).map((marker) => marker.id).sort((a, b) => a - b).join(",");
|
|
4636
|
-
const sourceFingerprint = markerFingerprint(labTest);
|
|
4637
|
-
const existingAuto = state.listLabTests().find(
|
|
4638
|
-
(test) => test.auto_generated === true && test.method === method && markerFingerprint(test) === sourceFingerprint
|
|
4639
|
-
);
|
|
4640
|
-
const orderLabTestKey = `${labTest.id}:${method}`;
|
|
4641
|
-
const orderLabTestName = existingAuto?.name ?? opaqueToken(`junction:order-lab-name:${orderLabTestKey}`, 16);
|
|
4642
|
-
orderLabTest = existingAuto ?? {
|
|
4643
|
-
...labTest,
|
|
4644
|
-
id: deterministicUuid(`junction:order-lab-test:${orderLabTestKey}`),
|
|
4645
|
-
method,
|
|
4646
|
-
auto_generated: true,
|
|
4647
|
-
name: orderLabTestName,
|
|
4648
|
-
slug: `afd62b39-${orderLabTestName}`
|
|
4649
|
-
};
|
|
4650
|
-
}
|
|
4651
|
-
const order = {
|
|
4652
|
-
id: orderId,
|
|
4653
|
-
user_id: userId,
|
|
4654
|
-
team_id: MOCK_TEAM_ID,
|
|
4655
|
-
patient_details: patientDetails(details),
|
|
4656
|
-
patient_address: patientAddress(address),
|
|
4657
|
-
lab_test: orderLabTest,
|
|
4658
|
-
details: METHOD_DETAILS(method, state.testkitIdFor(orderId), nowIso),
|
|
4659
|
-
sample_id: null,
|
|
4660
|
-
notes: null,
|
|
4661
|
-
clinical_notes: typeof body.clinical_notes === "string" ? body.clinical_notes : null,
|
|
4662
|
-
passthrough: typeof body.passthrough === "string" ? body.passthrough : null,
|
|
4663
|
-
...typeof body.lab_account_id === "string" ? { lab_account_id: body.lab_account_id } : {},
|
|
4664
|
-
created_at: nowIso,
|
|
4665
|
-
updated_at: nowIso,
|
|
4666
|
-
events: [event],
|
|
4667
|
-
status: "received",
|
|
4668
|
-
last_event: event,
|
|
4669
|
-
physician: PHYSICIAN,
|
|
4670
|
-
health_insurance_id: null,
|
|
4671
|
-
requisition_form_url: null,
|
|
4672
|
-
shipping_details: null,
|
|
4673
|
-
has_abn: false,
|
|
4674
|
-
billing_type: billingType,
|
|
4675
|
-
priority: false,
|
|
4676
|
-
activate_by: null,
|
|
4677
|
-
icd_codes: icdCodes,
|
|
4678
|
-
interpretation: null,
|
|
4679
|
-
has_missing_results: null,
|
|
4680
|
-
result_types: null,
|
|
4681
|
-
expected_result_by_date: null,
|
|
4682
|
-
worst_case_result_by_date: null,
|
|
4683
|
-
origin: "initial",
|
|
4684
|
-
order_transaction: {
|
|
4685
|
-
id: transactionId,
|
|
4686
|
-
status: "active",
|
|
4687
|
-
orders: [
|
|
4688
|
-
{
|
|
4689
|
-
id: orderId,
|
|
4690
|
-
low_level_status: "ordered",
|
|
4691
|
-
low_level_status_created_at: nowMicro,
|
|
4692
|
-
origin: "initial",
|
|
4693
|
-
parent_id: null,
|
|
4694
|
-
created_at: nowMicro,
|
|
4695
|
-
updated_at: nowMicro
|
|
4696
|
-
}
|
|
4697
|
-
]
|
|
5714
|
+
});
|
|
5715
|
+
return jsonRes(200, replay.response);
|
|
4698
5716
|
}
|
|
4699
|
-
}
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
5717
|
+
}
|
|
5718
|
+
const order = buildOrderRecord(
|
|
5719
|
+
state,
|
|
5720
|
+
{
|
|
5721
|
+
orderId,
|
|
5722
|
+
userId,
|
|
5723
|
+
labTest,
|
|
5724
|
+
method,
|
|
5725
|
+
patientDetails: details,
|
|
5726
|
+
patientAddress: address,
|
|
5727
|
+
billingType,
|
|
5728
|
+
icdCodes,
|
|
5729
|
+
clinicalNotes: typeof body.clinical_notes === "string" ? body.clinical_notes : null,
|
|
5730
|
+
passthrough: typeof body.passthrough === "string" ? body.passthrough : null,
|
|
5731
|
+
...typeof body.lab_account_id === "string" ? { labAccountId: body.lab_account_id } : {}
|
|
5732
|
+
},
|
|
5733
|
+
context.now
|
|
4705
5734
|
);
|
|
4706
|
-
|
|
4707
|
-
first_name: details.first_name ?? null,
|
|
4708
|
-
last_name: details.last_name ?? null,
|
|
4709
|
-
dob: details.dob ?? null,
|
|
4710
|
-
gender: details.gender ?? null,
|
|
4711
|
-
phone_number: details.phone_number ?? null,
|
|
4712
|
-
email: details.email ?? null,
|
|
4713
|
-
gender_identity: null,
|
|
4714
|
-
sexual_orientation: null,
|
|
4715
|
-
race: null,
|
|
4716
|
-
ethnicity: null,
|
|
4717
|
-
medical_proxy: null,
|
|
4718
|
-
address: {
|
|
4719
|
-
first_line: address.first_line ?? "",
|
|
4720
|
-
second_line: typeof address.second_line === "string" ? address.second_line : "",
|
|
4721
|
-
country: address.country ?? "",
|
|
4722
|
-
zip: address.zip ?? "",
|
|
4723
|
-
city: address.city ?? "",
|
|
4724
|
-
state: address.state ?? "",
|
|
4725
|
-
access_notes: null
|
|
4726
|
-
}
|
|
4727
|
-
};
|
|
4728
|
-
if (state.userInfo.get(userId)) state.userInfo.update(userId, demographics);
|
|
4729
|
-
else state.userInfo.insert(userId, demographics);
|
|
5735
|
+
persistOrderRecord(state, order, labTest, details, address);
|
|
4730
5736
|
const responseOrder = { ...order };
|
|
4731
5737
|
if (responseOrder.result_types === null) delete responseOrder.result_types;
|
|
4732
5738
|
const response = {
|
|
@@ -4745,8 +5751,7 @@ var orderHandlers = (state) => ({
|
|
|
4745
5751
|
},
|
|
4746
5752
|
cancel_order_v3_order__order_id__cancel_post: async (context) => {
|
|
4747
5753
|
const id = context.params.order_id ?? "";
|
|
4748
|
-
const order = state.orders.get(id);
|
|
4749
|
-
if (!order) notFound2("Order doesn't exist");
|
|
5754
|
+
const order = state.orders.get(id) ?? orderMissing(state, context.operation.operationId, id);
|
|
4750
5755
|
const alreadyCancelled = order.events.some(
|
|
4751
5756
|
(entry) => typeof entry.status === "string" && entry.status.startsWith("cancelled.")
|
|
4752
5757
|
);
|
|
@@ -4828,8 +5833,8 @@ var orderHandlers = (state) => ({
|
|
|
4828
5833
|
});
|
|
4829
5834
|
}
|
|
4830
5835
|
}
|
|
4831
|
-
const order = state.orders.get(id);
|
|
4832
|
-
|
|
5836
|
+
const order = state.orders.get(id) ?? orderMissing(state, context.operation.operationId, id);
|
|
5837
|
+
checkSimulateAllowed(state.limits, context.request.headers.get("x-vital-api-key"));
|
|
4833
5838
|
if (typeof finalStatus !== "string" || !FINAL_STATUSES.includes(finalStatus))
|
|
4834
5839
|
throw new HttpError(422, { detail: [finalStatusError(String(finalStatus ?? ""))] });
|
|
4835
5840
|
const flags = simulationFlagsOf(context);
|
|
@@ -4847,13 +5852,11 @@ var orderHandlers = (state) => ({
|
|
|
4847
5852
|
},
|
|
4848
5853
|
get_order_v3_order__order_id__get: async (context) => {
|
|
4849
5854
|
const id = context.params.order_id ?? "";
|
|
4850
|
-
|
|
4851
|
-
if (!order) notFound2("This order doesn't exist");
|
|
5855
|
+
if (!state.orders.has(id)) orderMissing(state, context.operation.operationId, id);
|
|
4852
5856
|
state.applyDueSimulateTransitions(context.now(), (due, finalStatus, flags) => {
|
|
4853
5857
|
applySimulateTransition(state, due, finalStatus, flags, context);
|
|
4854
5858
|
});
|
|
4855
|
-
const fresh = state.orders.get(id);
|
|
4856
|
-
if (!fresh) notFound2("This order doesn't exist");
|
|
5859
|
+
const fresh = state.orders.get(id) ?? orderMissing(state, context.operation.operationId, id);
|
|
4857
5860
|
const body = { ...fresh };
|
|
4858
5861
|
if (body.result_types === null) delete body.result_types;
|
|
4859
5862
|
return jsonRes(200, body);
|
|
@@ -4866,8 +5869,7 @@ var orderHandlers = (state) => ({
|
|
|
4866
5869
|
const userId = context.query.user_id;
|
|
4867
5870
|
if (userId !== void 0 && typeof userId !== "string")
|
|
4868
5871
|
throw new HttpError(422, { detail: "user_id must be a string" });
|
|
4869
|
-
if (userId !== void 0
|
|
4870
|
-
notFound2("User does not exist on this team");
|
|
5872
|
+
if (userId !== void 0) requireUser(state, userId, context);
|
|
4871
5873
|
let all = state.orders.list({ order: "oldest" });
|
|
4872
5874
|
if (userId !== void 0) all = all.filter((entry) => entry.value.user_id === userId);
|
|
4873
5875
|
all = [...all].sort((left, right) => {
|
|
@@ -4895,7 +5897,7 @@ var orderHandlers = (state) => ({
|
|
|
4895
5897
|
if (!order) notFound2("Order transaction not found");
|
|
4896
5898
|
return jsonRes(200, {
|
|
4897
5899
|
id,
|
|
4898
|
-
team_id:
|
|
5900
|
+
team_id: state.teamId,
|
|
4899
5901
|
status: order.order_transaction.status,
|
|
4900
5902
|
orders: [orderSummary(order)]
|
|
4901
5903
|
});
|
|
@@ -4930,6 +5932,126 @@ var orderHandlers = (state) => ({
|
|
|
4930
5932
|
});
|
|
4931
5933
|
}
|
|
4932
5934
|
});
|
|
5935
|
+
var buildOrderRecord = (state, draft, now) => {
|
|
5936
|
+
const { orderId, userId, labTest, method, billingType, icdCodes } = draft;
|
|
5937
|
+
const details = draft.patientDetails;
|
|
5938
|
+
const nowIso = state.isoNow(now);
|
|
5939
|
+
const nowMicro = new Date(now()).toISOString();
|
|
5940
|
+
const transactionId = state.transactionIdFor(orderId);
|
|
5941
|
+
const nativeMethod = typeof labTest.method === "string" && labTest.method.length > 0 ? labTest.method : method;
|
|
5942
|
+
const eventStatus = `received.${method}.ordered`;
|
|
5943
|
+
const eventId = Number.parseInt(opaqueToken(`junction:order-event:${orderId}`, 8), 16) % 1e9 || 1;
|
|
5944
|
+
const event = {
|
|
5945
|
+
id: eventId,
|
|
5946
|
+
created_at: nowIso,
|
|
5947
|
+
status: eventStatus,
|
|
5948
|
+
status_detail: null
|
|
5949
|
+
};
|
|
5950
|
+
let orderLabTest;
|
|
5951
|
+
if (method === nativeMethod) {
|
|
5952
|
+
orderLabTest = labTest;
|
|
5953
|
+
} else {
|
|
5954
|
+
const markerFingerprint = (test) => (test.markers ?? []).map((marker) => marker.id).sort((a, b) => a - b).join(",");
|
|
5955
|
+
const sourceFingerprint = markerFingerprint(labTest);
|
|
5956
|
+
const existingAuto = state.listLabTests().find(
|
|
5957
|
+
(test) => test.auto_generated === true && test.method === method && markerFingerprint(test) === sourceFingerprint
|
|
5958
|
+
);
|
|
5959
|
+
const orderLabTestKey = `${labTest.id}:${method}`;
|
|
5960
|
+
const orderLabTestName = existingAuto?.name ?? opaqueToken(`junction:order-lab-name:${orderLabTestKey}`, 16);
|
|
5961
|
+
orderLabTest = existingAuto ?? {
|
|
5962
|
+
...labTest,
|
|
5963
|
+
id: deterministicUuid(`junction:order-lab-test:${orderLabTestKey}`),
|
|
5964
|
+
method,
|
|
5965
|
+
auto_generated: true,
|
|
5966
|
+
name: orderLabTestName,
|
|
5967
|
+
slug: `afd62b39-${orderLabTestName}`
|
|
5968
|
+
};
|
|
5969
|
+
}
|
|
5970
|
+
const order = {
|
|
5971
|
+
id: orderId,
|
|
5972
|
+
user_id: userId,
|
|
5973
|
+
team_id: state.teamId,
|
|
5974
|
+
patient_details: patientDetails(details),
|
|
5975
|
+
patient_address: patientAddress(draft.patientAddress),
|
|
5976
|
+
lab_test: orderLabTest,
|
|
5977
|
+
details: METHOD_DETAILS(method, state.testkitIdFor(orderId), nowIso),
|
|
5978
|
+
sample_id: null,
|
|
5979
|
+
notes: null,
|
|
5980
|
+
clinical_notes: draft.clinicalNotes,
|
|
5981
|
+
passthrough: draft.passthrough,
|
|
5982
|
+
...draft.labAccountId !== void 0 ? { lab_account_id: draft.labAccountId } : {},
|
|
5983
|
+
created_at: nowIso,
|
|
5984
|
+
updated_at: nowIso,
|
|
5985
|
+
events: [event],
|
|
5986
|
+
status: "received",
|
|
5987
|
+
last_event: event,
|
|
5988
|
+
physician: PHYSICIAN,
|
|
5989
|
+
health_insurance_id: null,
|
|
5990
|
+
requisition_form_url: null,
|
|
5991
|
+
shipping_details: null,
|
|
5992
|
+
has_abn: false,
|
|
5993
|
+
billing_type: billingType,
|
|
5994
|
+
priority: false,
|
|
5995
|
+
activate_by: null,
|
|
5996
|
+
icd_codes: icdCodes,
|
|
5997
|
+
interpretation: null,
|
|
5998
|
+
has_missing_results: null,
|
|
5999
|
+
result_types: null,
|
|
6000
|
+
expected_result_by_date: null,
|
|
6001
|
+
worst_case_result_by_date: null,
|
|
6002
|
+
origin: "initial",
|
|
6003
|
+
order_transaction: {
|
|
6004
|
+
id: transactionId,
|
|
6005
|
+
status: "active",
|
|
6006
|
+
orders: [
|
|
6007
|
+
{
|
|
6008
|
+
id: orderId,
|
|
6009
|
+
low_level_status: "ordered",
|
|
6010
|
+
low_level_status_created_at: nowMicro,
|
|
6011
|
+
origin: "initial",
|
|
6012
|
+
parent_id: null,
|
|
6013
|
+
created_at: nowMicro,
|
|
6014
|
+
updated_at: nowMicro
|
|
6015
|
+
}
|
|
6016
|
+
]
|
|
6017
|
+
}
|
|
6018
|
+
};
|
|
6019
|
+
return order;
|
|
6020
|
+
};
|
|
6021
|
+
var persistOrderRecord = (state, order, labTest, details, address) => {
|
|
6022
|
+
const userId = order.user_id;
|
|
6023
|
+
const orderLabTest = order.lab_test;
|
|
6024
|
+
state.orders.insert(order.id, order);
|
|
6025
|
+
state.orderByTransaction.insert(order.order_transaction.id, { order_id: order.id });
|
|
6026
|
+
state.upsertLabTest(
|
|
6027
|
+
orderLabTest,
|
|
6028
|
+
orderLabTest.id === labTest.id ? void 0 : state.expectedResultsFor(labTest.id)
|
|
6029
|
+
);
|
|
6030
|
+
const demographics = {
|
|
6031
|
+
first_name: details.first_name ?? null,
|
|
6032
|
+
last_name: details.last_name ?? null,
|
|
6033
|
+
dob: details.dob ?? null,
|
|
6034
|
+
gender: details.gender ?? null,
|
|
6035
|
+
phone_number: details.phone_number ?? null,
|
|
6036
|
+
email: details.email ?? null,
|
|
6037
|
+
gender_identity: null,
|
|
6038
|
+
sexual_orientation: null,
|
|
6039
|
+
race: null,
|
|
6040
|
+
ethnicity: null,
|
|
6041
|
+
medical_proxy: null,
|
|
6042
|
+
address: {
|
|
6043
|
+
first_line: address.first_line ?? "",
|
|
6044
|
+
second_line: typeof address.second_line === "string" ? address.second_line : "",
|
|
6045
|
+
country: address.country ?? "",
|
|
6046
|
+
zip: address.zip ?? "",
|
|
6047
|
+
city: address.city ?? "",
|
|
6048
|
+
state: address.state ?? "",
|
|
6049
|
+
access_notes: null
|
|
6050
|
+
}
|
|
6051
|
+
};
|
|
6052
|
+
if (state.userInfo.get(userId)) state.userInfo.update(userId, demographics);
|
|
6053
|
+
else state.userInfo.insert(userId, demographics);
|
|
6054
|
+
};
|
|
4933
6055
|
var orderSummary = (order) => {
|
|
4934
6056
|
const transactionOrder = order.order_transaction.orders.find((entry) => entry.id === order.id);
|
|
4935
6057
|
return {
|
|
@@ -4955,6 +6077,201 @@ var biomarker = (order, created_at) => (order.lab_test.markers ?? []).map((marke
|
|
|
4955
6077
|
source_sample_id: null
|
|
4956
6078
|
}));
|
|
4957
6079
|
|
|
6080
|
+
// src/fixtures.ts
|
|
6081
|
+
var IDENTITY_MODES = ["strict", "adopt-users"];
|
|
6082
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
6083
|
+
var isUuid3 = (value) => UUID.test(value);
|
|
6084
|
+
var FixtureError = class extends Error {
|
|
6085
|
+
constructor(status, message) {
|
|
6086
|
+
super(message);
|
|
6087
|
+
this.status = status;
|
|
6088
|
+
this.name = "FixtureError";
|
|
6089
|
+
}
|
|
6090
|
+
status;
|
|
6091
|
+
};
|
|
6092
|
+
var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6093
|
+
var instant = (value, field, fallback) => {
|
|
6094
|
+
if (value === void 0) return fallback;
|
|
6095
|
+
if (typeof value !== "string" || Number.isNaN(Date.parse(value)))
|
|
6096
|
+
throw new FixtureError(400, `${field} must be an ISO-8601 timestamp`);
|
|
6097
|
+
return value;
|
|
6098
|
+
};
|
|
6099
|
+
var DEFAULT_PATIENT_DETAILS = {
|
|
6100
|
+
first_name: "Mockingbird",
|
|
6101
|
+
last_name: "Fixture",
|
|
6102
|
+
dob: "1990-01-01",
|
|
6103
|
+
gender: "female",
|
|
6104
|
+
phone_number: "+14155550100",
|
|
6105
|
+
email: "fixture@example.com"
|
|
6106
|
+
};
|
|
6107
|
+
var DEFAULT_PATIENT_ADDRESS = {
|
|
6108
|
+
first_line: "1 Main St",
|
|
6109
|
+
second_line: "",
|
|
6110
|
+
city: "New York",
|
|
6111
|
+
state: "NY",
|
|
6112
|
+
zip: "10001",
|
|
6113
|
+
country: "US"
|
|
6114
|
+
};
|
|
6115
|
+
var buildUser = (state, input, now) => {
|
|
6116
|
+
if (!isRecord6(input)) throw new FixtureError(400, "a user fixture must be an object");
|
|
6117
|
+
if (typeof input.client_user_id !== "string" || input.client_user_id === "")
|
|
6118
|
+
throw new FixtureError(400, "client_user_id must be a non-empty string");
|
|
6119
|
+
if (input.user_id !== void 0 && (typeof input.user_id !== "string" || !isUuid3(input.user_id)))
|
|
6120
|
+
throw new FixtureError(400, `user_id must be a UUID (got ${JSON.stringify(input.user_id)})`);
|
|
6121
|
+
const createdOn = instant(input.created_on, "created_on", state.isoNow(now));
|
|
6122
|
+
const zone = input.fallback_time_zone;
|
|
6123
|
+
const birth = input.fallback_birth_date;
|
|
6124
|
+
return {
|
|
6125
|
+
user_id: input.user_id ?? state.nextUserId(),
|
|
6126
|
+
team_id: state.teamId,
|
|
6127
|
+
client_user_id: input.client_user_id,
|
|
6128
|
+
created_on: createdOn,
|
|
6129
|
+
connected_sources: [],
|
|
6130
|
+
fallback_time_zone: typeof zone === "string" ? { id: zone, source_slug: "manual", updated_at: createdOn } : zone ?? null,
|
|
6131
|
+
fallback_birth_date: typeof birth === "string" ? { value: birth, source_slug: "manual", updated_at: createdOn } : birth ?? null,
|
|
6132
|
+
ingestion_start: input.ingestion_start ?? null,
|
|
6133
|
+
ingestion_end: input.ingestion_end ?? null
|
|
6134
|
+
};
|
|
6135
|
+
};
|
|
6136
|
+
var insertUsers = (state, inputs, now) => {
|
|
6137
|
+
const users = [];
|
|
6138
|
+
const ids = /* @__PURE__ */ new Set();
|
|
6139
|
+
const clientIds = /* @__PURE__ */ new Set();
|
|
6140
|
+
for (const input of inputs) {
|
|
6141
|
+
const user = buildUser(state, input, now);
|
|
6142
|
+
if (ids.has(user.user_id) || state.users.has(user.user_id) || state.deletedUsers.has(user.user_id))
|
|
6143
|
+
throw new FixtureError(409, `user ${user.user_id} already exists`);
|
|
6144
|
+
if (clientIds.has(user.client_user_id) || state.byClientId.has(user.client_user_id))
|
|
6145
|
+
throw new FixtureError(409, `client_user_id ${user.client_user_id} already exists`);
|
|
6146
|
+
ids.add(user.user_id);
|
|
6147
|
+
clientIds.add(user.client_user_id);
|
|
6148
|
+
users.push(user);
|
|
6149
|
+
}
|
|
6150
|
+
for (const user of users) state.insertUser(user);
|
|
6151
|
+
return users;
|
|
6152
|
+
};
|
|
6153
|
+
var hardDeleteUser = (state, userId) => {
|
|
6154
|
+
const user = state.users.get(userId);
|
|
6155
|
+
const tombstoned = state.deletedUsers.delete(userId);
|
|
6156
|
+
if (!user) return tombstoned;
|
|
6157
|
+
state.users.delete(userId);
|
|
6158
|
+
state.byClientId.delete(user.client_user_id);
|
|
6159
|
+
state.userInfo.delete(userId);
|
|
6160
|
+
return true;
|
|
6161
|
+
};
|
|
6162
|
+
var insertOrders = (state, inputs, now, options = {}) => {
|
|
6163
|
+
const prepared = [];
|
|
6164
|
+
const ids = /* @__PURE__ */ new Set();
|
|
6165
|
+
for (const input of inputs) {
|
|
6166
|
+
if (!isRecord6(input)) throw new FixtureError(400, "an order fixture must be an object");
|
|
6167
|
+
if (typeof input.user_id !== "string") throw new FixtureError(400, "user_id is required");
|
|
6168
|
+
if (typeof input.lab_test_id !== "string")
|
|
6169
|
+
throw new FixtureError(400, "lab_test_id is required");
|
|
6170
|
+
if (!state.users.has(input.user_id) && !state.deletedUsers.has(input.user_id))
|
|
6171
|
+
throw new FixtureError(404, `no user ${input.user_id} in this namespace`);
|
|
6172
|
+
const labTest = state.labTestById(input.lab_test_id);
|
|
6173
|
+
if (!labTest) throw new FixtureError(404, `no lab test ${input.lab_test_id} in the catalog`);
|
|
6174
|
+
if (input.order_id !== void 0 && (typeof input.order_id !== "string" || !isUuid3(input.order_id)))
|
|
6175
|
+
throw new FixtureError(400, `order_id must be a UUID (got ${JSON.stringify(input.order_id)})`);
|
|
6176
|
+
const orderId = input.order_id ?? state.nextOrderId();
|
|
6177
|
+
if (ids.has(orderId) || state.orders.has(orderId))
|
|
6178
|
+
throw new FixtureError(409, `order ${orderId} already exists`);
|
|
6179
|
+
ids.add(orderId);
|
|
6180
|
+
const method = typeof input.collection_method === "string" && input.collection_method !== "" ? input.collection_method : typeof labTest.method === "string" && labTest.method !== "" ? labTest.method : "at_home_phlebotomy";
|
|
6181
|
+
let status;
|
|
6182
|
+
if (input.status !== void 0) {
|
|
6183
|
+
if (typeof input.status !== "string") throw new FixtureError(400, "status must be a string");
|
|
6184
|
+
const resolved = resolveTransition(method, input.status);
|
|
6185
|
+
if ("error" in resolved) throw new FixtureError(400, resolved.error);
|
|
6186
|
+
status = resolved.status;
|
|
6187
|
+
}
|
|
6188
|
+
const details = { ...DEFAULT_PATIENT_DETAILS, ...input.patient_details };
|
|
6189
|
+
const address = { ...DEFAULT_PATIENT_ADDRESS, ...input.patient_address };
|
|
6190
|
+
const createdAt = input.created_at;
|
|
6191
|
+
const createdMs = createdAt === void 0 ? void 0 : Date.parse(instant(createdAt, "created_at", ""));
|
|
6192
|
+
const order = buildOrderRecord(
|
|
6193
|
+
state,
|
|
6194
|
+
{
|
|
6195
|
+
orderId,
|
|
6196
|
+
userId: input.user_id,
|
|
6197
|
+
labTest,
|
|
6198
|
+
method,
|
|
6199
|
+
patientDetails: details,
|
|
6200
|
+
patientAddress: address,
|
|
6201
|
+
billingType: input.billing_type ?? "client_bill",
|
|
6202
|
+
icdCodes: input.icd_codes ?? null,
|
|
6203
|
+
clinicalNotes: input.clinical_notes ?? null,
|
|
6204
|
+
passthrough: input.passthrough ?? null,
|
|
6205
|
+
...input.lab_account_id !== void 0 ? { labAccountId: input.lab_account_id } : {}
|
|
6206
|
+
},
|
|
6207
|
+
createdMs === void 0 ? now : () => createdMs
|
|
6208
|
+
);
|
|
6209
|
+
prepared.push({ input, order, labTest, details, address, status });
|
|
6210
|
+
}
|
|
6211
|
+
const run = () => {
|
|
6212
|
+
for (const { input, order, labTest, details, address, status } of prepared) {
|
|
6213
|
+
persistOrderRecord(state, order, labTest, details, address);
|
|
6214
|
+
state.publishOrderWebhook(order, "labtest.order.created", now());
|
|
6215
|
+
if (status !== void 0) {
|
|
6216
|
+
const stored = state.orders.get(order.id);
|
|
6217
|
+
if (stored) forceOrderStatus(state, stored, status, null, { now });
|
|
6218
|
+
}
|
|
6219
|
+
if (input.result_fixture !== void 0) {
|
|
6220
|
+
state.resultFixtures.insert(order.id, { name: "custom", ...input.result_fixture });
|
|
6221
|
+
if (input.result_fixture.interpretation !== void 0) {
|
|
6222
|
+
const stored = state.orders.get(order.id);
|
|
6223
|
+
if (stored) {
|
|
6224
|
+
stored.interpretation = input.result_fixture.interpretation;
|
|
6225
|
+
state.orders.update(order.id, stored);
|
|
6226
|
+
}
|
|
6227
|
+
}
|
|
6228
|
+
}
|
|
6229
|
+
}
|
|
6230
|
+
};
|
|
6231
|
+
if (options.emitWebhooks === true) run();
|
|
6232
|
+
else state.muteWebhooks(run);
|
|
6233
|
+
return prepared.flatMap(({ order }) => state.orders.get(order.id) ?? []);
|
|
6234
|
+
};
|
|
6235
|
+
var importFixtures = (state, fixtures, now, options = {}) => {
|
|
6236
|
+
if (!isRecord6(fixtures))
|
|
6237
|
+
throw new FixtureError(400, 'fixtures must be { "users": [...], "orders": [...] }');
|
|
6238
|
+
const { users = [], orders = [] } = fixtures;
|
|
6239
|
+
if (!Array.isArray(users)) throw new FixtureError(400, "users must be an array");
|
|
6240
|
+
if (!Array.isArray(orders)) throw new FixtureError(400, "orders must be an array");
|
|
6241
|
+
const inserted = insertUsers(state, users, now);
|
|
6242
|
+
try {
|
|
6243
|
+
return { users: inserted, orders: insertOrders(state, orders, now, options) };
|
|
6244
|
+
} catch (error) {
|
|
6245
|
+
for (const user of inserted) hardDeleteUser(state, user.user_id);
|
|
6246
|
+
throw error;
|
|
6247
|
+
}
|
|
6248
|
+
};
|
|
6249
|
+
var requireUser = (state, userId, context, detail = "User does not exist on this team") => {
|
|
6250
|
+
if (state.users.has(userId) || state.deletedUsers.has(userId)) return;
|
|
6251
|
+
if (state.identity === "adopt-users" && isUuid3(userId)) {
|
|
6252
|
+
insertUsers(state, [{ user_id: userId, client_user_id: userId }], context.now);
|
|
6253
|
+
state.noteAdoption(context.request);
|
|
6254
|
+
return;
|
|
6255
|
+
}
|
|
6256
|
+
missing(state, "user", userId, detail);
|
|
6257
|
+
};
|
|
6258
|
+
var fixtureErrorMessage = (error) => {
|
|
6259
|
+
if (error instanceof FixtureError) return { status: error.status, message: error.message };
|
|
6260
|
+
if (error instanceof HttpError) {
|
|
6261
|
+
const detail = isRecord6(error.body) ? error.body.detail : error.body;
|
|
6262
|
+
return {
|
|
6263
|
+
status: error.status,
|
|
6264
|
+
message: typeof detail === "string" ? detail : JSON.stringify(detail)
|
|
6265
|
+
};
|
|
6266
|
+
}
|
|
6267
|
+
throw error;
|
|
6268
|
+
};
|
|
6269
|
+
|
|
6270
|
+
// src/generated/openapi.ts
|
|
6271
|
+
var document = JSON.parse(`{"openapi":"3.1.0","info":{"title":"Junction API (Mockingbird subset)","description":"Stateful mock subset of the Junction (Vital) API.\\nDocs: https://docs.junction.com/\\nAuth: https://docs.junction.com/api-details/junction-api\\nCreate user: https://docs.junction.com/api-reference/user/create-user\\nGet user: https://docs.junction.com/api-reference/user/get-user\\nDelete user: https://docs.junction.com/api-reference/user/delete-user\\nResolve user: https://docs.junction.com/api-reference/user/resolve-user\\nUpdate user: https://docs.junction.com/api-reference/user/patch-user\\nGet users: https://docs.junction.com/api-reference/user/get-users\\nCreate order: https://docs.junction.com/api-reference/lab-testing/create-order\\nGet order: https://docs.junction.com/api-reference/lab-testing/get-order\\nGet orders: https://docs.junction.com/api-reference/lab-testing/get-orders\\nGet order transaction: https://docs.junction.com/api-reference/lab-testing/order-transactions/get-order-transaction\\nGet order transaction result: https://docs.junction.com/api-reference/lab-testing/results/get-order-transaction-results\\nLab tests: https://docs.junction.com/api-reference/lab-testing/tests-paginated\\nLab tests (deprecated, unpaged): https://docs.junction.com/api-reference/lab-testing/tests/get-lab-tests\\nAppointment availability: https://docs.junction.com/api-reference/lab-testing/appointments/get-appointment-availability\\nBook appointment: https://docs.junction.com/api-reference/lab-testing/appointments/book-appointment\\nGet appointment: https://docs.junction.com/api-reference/lab-testing/appointments/get-appointment\\nReschedule appointment: https://docs.junction.com/api-reference/lab-testing/appointments/reschedule-appointment\\nCancel appointment: https://docs.junction.com/api-reference/lab-testing/appointments/cancel-appointment\\nAppointment cancellation reasons: https://docs.junction.com/api-reference/lab-testing/appointments/get-appointment-cancellation-reasons\\nArea info: https://docs.junction.com/api-reference/lab-testing/service-areas/get-area-info\\nPSC info: https://docs.junction.com/api-reference/lab-testing/service-areas/get-psc-info\\nLab locations: https://docs.junction.com/api-reference/lab-testing/service-areas/get-lab-locations\\nRaw results: https://docs.junction.com/api-reference/lab-testing/results/get-lab-results\\nResult metadata: https://docs.junction.com/api-reference/lab-testing/results/get-lab-results-metadata\\nResult PDF: https://docs.junction.com/api-reference/lab-testing/results/get-lab-result-pdf\\nRequisition form PDF: https://docs.junction.com/api-reference/lab-testing/orders/get-requisition-form\\nList labs: https://docs.junction.com/api-reference/lab-testing/tests/get-labs\\nTest markers: https://docs.junction.com/api-reference/lab-testing/tests/get-markers-for-lab-test\\nOrder-set markers: https://docs.junction.com/api-reference/lab-testing/results/get-order-set-markers\\nLatest user info: https://docs.junction.com/api-reference/user/get-latest-user-info\\n","version":"0.4.758","x-mockingbird-upstream":{"docs":"https://docs.junction.com/","note":"Hand-authored subset from Junction public OpenAPI snippets in the docs."}},"servers":[{"url":"https://api.sandbox.us.junction.com"}],"security":[{"apiKeyAuth":[]}],"paths":{"/v2/user":{"post":{"operationId":"create_user_v2_user_post","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserCreateBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingUser"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientUserIDConflictResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"get":{"operationId":"get_teams_users_v2_user_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"offset","in":"query","required":false,"schema":{"type":"integer"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaginatedUsersResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"500":{"description":"Internal Server Error","content":{"text/plain":{"schema":{"type":"string"}}}}}}},"/v2/user/{user_id}":{"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"user","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_user_v2_user__user_id__get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingUser"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"operationId":"delete_user_v2_user__user_id__delete","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserSuccessResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"patch":{"operationId":"patch_user_v2_user__user_id__patch","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserPatchBody"}}}},"responses":{"204":{"description":"Successful Response"},"400":{"description":"Nothing to patch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"409":{"description":"Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/user/resolve/{client_user_id}":{"parameters":[{"name":"client_user_id","in":"path","required":true,"schema":{"type":"string","minLength":1}}],"get":{"operationId":"get_user_by_client_user_id_v2_user_resolve__client_user_id__get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"anyOf":[{"$ref":"#/components/schemas/ClientFacingUser"},{"$ref":"#/components/schemas/PaginatedUsersResponse"}]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/user/{user_id}/info":{"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"user","missing":"00000000-0000-4000-8000-000000000000"}}}],"patch":{"operationId":"patch_user_info_v2_user__user_id__info_patch","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserInfoPatchBody"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserInfo"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v2/user/{user_id}/info/latest":{"parameters":[{"name":"user_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"user","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_latest_user_info_user_v2_user__user_id__info_latest_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/UserInfo"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/lab_test":{"get":{"operationId":"get_paginated_lab_tests_for_team_v3_lab_test_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabTestResourcesResponse"}}}}}}},"/v3/lab_test/lab_account":{"get":{"operationId":"get_team_lab_accounts_v3_lab_test_lab_account_get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"lab-account inventory is fixture-owned; the mock models the documented shape, filters and routing rules, not a live account inventory"}},"parameters":[{"name":"lab_account_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/LabAccountStatus"},{"type":"null"}]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetTeamLabAccountsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/lab_tests":{"get":{"operationId":"get_lab_tests_for_team_v3_lab_tests_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"summary":"Get Lab Tests For Team","description":"GET all the lab tests the team has access to.","deprecated":true,"parameters":[{"name":"generation_method","in":"query","required":false,"schema":{"$ref":"#/components/schemas/LabTestGenerationMethodFilter","description":"Filter on whether auto-generated lab tests created by Vital, manually created lab tests, or all lab tests should be returned.","default":"manual"},"description":"Filter on whether auto-generated lab tests created by Vital, manually created lab tests, or all lab tests should be returned."},{"name":"lab_slug","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by the slug of the lab for these lab tests.","title":"Lab Slug"},"description":"Filter by the slug of the lab for these lab tests."},{"name":"collection_method","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/LabTestCollectionMethod"},{"type":"null"}],"description":"Filter by the collection method for these lab tests.","title":"Collection Method"},"description":"Filter by the collection method for these lab tests."},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"$ref":"#/components/schemas/LabTestStatus"},{"type":"null"}],"description":"Filter by the status of these lab tests.","title":"Status"},"description":"Filter by the status of these lab tests."},{"name":"marker_ids","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"integer"}},{"type":"null"}],"description":"Filter to only include lab tests containing these marker IDs.","title":"Marker Ids"},"description":"Filter to only include lab tests containing these marker IDs."},{"name":"provider_ids","in":"query","required":false,"schema":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}],"description":"Filter to only include lab tests containing these provider IDs.","title":"Provider Ids"},"description":"Filter to only include lab tests containing these provider IDs."},{"name":"name","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by the name of the lab test (a case-insensitive substring search).","title":"Name"},"description":"Filter by the name of the lab test (a case-insensitive substring search)."},{"name":"order_key","in":"query","required":false,"schema":{"anyOf":[{"enum":["price","created_at","updated_at"],"type":"string"},{"type":"null"}],"title":"Order Key"}},{"name":"order_direction","in":"query","required":false,"schema":{"enum":["asc","desc"],"type":"string","default":"asc","title":"Order Direction"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingLabTest"},"title":"Response Get Lab Tests For Team V3 Lab Tests Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/lab_tests/{lab_test_id}":{"parameters":[{"name":"lab_test_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"lab_test","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_lab_test_for_team_v3_lab_tests__lab_test_id__get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingLabTest"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/lab_tests/labs":{"get":{"operationId":"get_labs_v3_lab_tests_labs_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingLab"}}}}}}}},"/v3/lab_tests/{lab_test_id}/markers":{"parameters":[{"name":"lab_test_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"lab_test","missing":"00000000-0000-4000-8000-000000000000"}}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1}},{"name":"size","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50}},{"name":"provider_id","in":"query","required":false,"schema":{"type":"string"}}],"get":{"operationId":"get_markers_for_lab_test_v3_lab_tests__lab_test_id__markers_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMarkersResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/lab_tests/list_order_set_markers":{"post":{"operationId":"list_order_set_markers_v3_lab_tests_list_order_set_markers_post","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListOrderSetMarkersRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMarkersResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order":{"post":{"operationId":"create_order_v3_order_post","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrderRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostOrderResponse"}}}},"400":{"description":"Bad Request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"500":{"description":"Internal Server Error"}}}},"/v3/order/area/info":{"get":{"operationId":"get_area_info_v3_order_area_info_get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"central_labs presence, within_radius counts and getlabs market coverage are provider-owned geo data that a mock cannot replicate per-zip; covered by the client-parity scenario shape checks instead"}},"parameters":[{"name":"zip_code","in":"query","required":true,"schema":{"type":"string","pattern":"^\\\\d{5}(?:-?\\\\d{4})?$"}},{"name":"radius","in":"query","required":false,"schema":{"$ref":"#/components/schemas/AllowedRadius"}},{"name":"lab_account_id","in":"query","required":false,"description":"Lab Account ID to use for availability checks","schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AreaInfo"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/psc/info":{"get":{"operationId":"get_psc_info_v3_order_psc_info_get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"patient_service_centers are real site inventories per zip (thousands of sites, result-capped at 30) that a fixture mock cannot replicate per-zip; covered by the client-parity scenario shape checks instead"}},"parameters":[{"name":"zip_code","in":"query","required":true,"schema":{"type":"string","pattern":"^\\\\d{5}$"}},{"name":"lab_id","in":"query","required":true,"schema":{"type":"integer"}},{"name":"radius","in":"query","required":false,"schema":{"$ref":"#/components/schemas/AllowedRadius"}},{"name":"capabilities","in":"query","required":false,"schema":{"type":"array","items":{"$ref":"#/components/schemas/LabLocationCapability"}}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PscInfo"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/phlebotomy/appointment/availability":{"post":{"operationId":"get_phlebotomy_appointment_availability_v3_order_phlebotomy_appointment_availability_post","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"availability slots are provider-side data whose values shift between sandbox calls; covered by the mock-internal scheduling property suite instead"}},"parameters":[{"name":"start_date","in":"query","required":false,"schema":{"type":"string","format":"date"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UsAddress"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentAvailabilitySlots"}}}},"400":{"description":"Availability not bookable for this address","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"No availability found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/phlebotomy/appointment/cancellation-reasons":{"get":{"operationId":"get_phlebotomy_appointment_cancellation_reason_v3_order_phlebotomy_appointment_cancellation_reasons_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingAppointmentCancellationReason"}}}}}}}},"/v3/order/psc/appointment/availability":{"post":{"operationId":"get_psc_appointment_availability_v3_order_psc_appointment_availability_post","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"availability slots are provider-side data whose values shift between sandbox calls; covered by the mock-internal scheduling property suite instead"}},"parameters":[{"name":"lab","in":"query","required":true,"schema":{"$ref":"#/components/schemas/AppointmentPscLabs"}},{"name":"start_date","in":"query","required":false,"schema":{"type":"string","format":"date"}},{"name":"site_codes","in":"query","required":false,"schema":{"type":"string","description":"JSON-encoded array of site codes"}},{"name":"zip_code","in":"query","required":false,"schema":{"type":"string"}},{"name":"radius","in":"query","required":false,"schema":{"$ref":"#/components/schemas/AllowedRadius"}},{"name":"allow_stale","in":"query","required":false,"schema":{"type":"boolean"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentAvailabilitySlots"}}}},"400":{"description":"Availability not bookable for this address","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"No slots found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/psc/appointment/cancellation-reasons":{"get":{"operationId":"get_psc_appointment_cancellation_reason_v3_order_psc_appointment_cancellation_reasons_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingAppointmentCancellationReason"}}}}}}}},"/v3/order/{order_id}/phlebotomy/appointment":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"appointment payloads embed provider-side slot data; covered by the mock-internal scheduling property suite instead"}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingAppointment"}}}},"404":{"description":"No appointment for this order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/phlebotomy/appointment/book":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"post":{"operationId":"book_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_book_post","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"booking depends on live provider slot state; covered by the mock-internal scheduling property suite instead"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentBookingRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingAppointment"}}}},"400":{"description":"Booking failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"Order not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/phlebotomy/appointment/reschedule":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"patch":{"operationId":"reschedule_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_reschedule_patch","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"rescheduling depends on live provider slot state; covered by the mock-internal scheduling property suite instead"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentRescheduleRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingAppointment"}}}},"400":{"description":"Invalid or expired booking key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"No appointment for this order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/phlebotomy/appointment/cancel":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"patch":{"operationId":"cancel_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_cancel_patch","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"cancellation depends on live provider appointment state; covered by the mock-internal scheduling property suite instead"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PhlebotomyAppointmentCancelRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingAppointment"}}}},"400":{"description":"Invalid cancellation reason","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"No appointment for this order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/psc/appointment":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_psc_appointment_v3_order__order_id__psc_appointment_get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"appointment payloads embed provider-side slot data; covered by the mock-internal scheduling property suite instead"}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingAppointment"}}}},"404":{"description":"No appointment for this order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/psc/appointment/book":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"post":{"operationId":"book_psc_appointment_v3_order__order_id__psc_appointment_book_post","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"booking depends on live provider slot state; covered by the mock-internal scheduling property suite instead"}},"parameters":[{"name":"x-idempotency-key","in":"header","required":false,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PscAppointmentBookingRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingAppointment"}}}},"400":{"description":"Booking failed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"Order not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/psc/appointment/reschedule":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"patch":{"operationId":"reschedule_psc_appointment_v3_order__order_id__psc_appointment_reschedule_patch","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"rescheduling depends on live provider slot state; covered by the mock-internal scheduling property suite instead"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AppointmentRescheduleRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingAppointment"}}}},"400":{"description":"Invalid or expired booking key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"No appointment for this order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/psc/appointment/cancel":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"patch":{"operationId":"cancel_psc_appointment_v3_order__order_id__psc_appointment_cancel_patch","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"cancellation depends on live provider appointment state; covered by the mock-internal scheduling property suite instead"}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PscAppointmentCancelRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingAppointment"}}}},"400":{"description":"Invalid cancellation reason","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"No appointment for this order","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_order_v3_order__order_id__get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ClientFacingOrder"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/cancel":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"post":{"operationId":"cancel_order_v3_order__order_id__cancel_post","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PostOrderResponse"}}}},"400":{"description":"Order is not in a cancellable status","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/test":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"post":{"operationId":"simulate_order_v3_order__order_id__test_post","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"final_status","in":"query","required":true,"schema":{"type":"string","enum":["received.walk_in_test.ordered","received.walk_in_test.requisition_created","received.walk_in_test.requisition_bypassed","completed.walk_in_test.completed","sample_with_lab.walk_in_test.partial_results","failed.walk_in_test.sample_error","cancelled.walk_in_test.cancelled","collecting_sample.walk_in_test.appointment_pending","collecting_sample.walk_in_test.appointment_scheduled","collecting_sample.walk_in_test.appointment_cancelled","collecting_sample.walk_in_test.redraw_available","received.at_home_phlebotomy.ordered","received.at_home_phlebotomy.requisition_created","received.at_home_phlebotomy.requisition_bypassed","collecting_sample.at_home_phlebotomy.appointment_pending","collecting_sample.at_home_phlebotomy.appointment_scheduled","collecting_sample.at_home_phlebotomy.draw_completed","collecting_sample.at_home_phlebotomy.appointment_cancelled","completed.at_home_phlebotomy.completed","sample_with_lab.at_home_phlebotomy.partial_results","cancelled.at_home_phlebotomy.cancelled","failed.at_home_phlebotomy.sample_error","received.testkit.ordered","received.testkit.awaiting_registration","received.testkit.requisition_created","received.testkit.requisition_bypassed","received.testkit.registered","collecting_sample.testkit.transit_customer","collecting_sample.testkit.out_for_delivery","collecting_sample.testkit.with_customer","collecting_sample.testkit.transit_lab","sample_with_lab.testkit.delivered_to_lab","sample_with_lab.testkit.lab_processing_blocked","completed.testkit.completed","failed.testkit.failure_to_deliver_to_customer","failed.testkit.failure_to_deliver_to_lab","failed.testkit.sample_error","failed.testkit.lost","cancelled.testkit.cancelled","cancelled.testkit.do_not_process","collecting_sample.testkit.problem_in_transit_customer","collecting_sample.testkit.problem_in_transit_lab","received.on_site_collection.ordered","received.on_site_collection.requisition_created","received.on_site_collection.requisition_bypassed","sample_with_lab.on_site_collection.draw_completed","completed.on_site_collection.completed","cancelled.on_site_collection.cancelled","sample_with_lab.on_site_collection.partial_results","failed.on_site_collection.sample_error","completed.walk_in_test.corrected","completed.at_home_phlebotomy.corrected","completed.on_site_collection.corrected","completed.testkit.corrected"]}},{"name":"delay","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Seconds to defer the status transition"}}],"requestBody":{"required":false,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SimulationFlags"}}}},"responses":{"200":{"description":"Successful Response","content":{"text/plain":{"schema":{"type":"string","examples":["Success"]}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/result":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_result_raw_v3_order__order_id__result_get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"results carry provider-side specimen data; covered by the mock-internal results property suite instead"}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabResultsRaw"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/result/metadata":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_result_metadata_v3_order__order_id__result_metadata_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabResultsMetadata"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/result/pdf":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_result_pdf_v3_order__order_id__result_pdf_get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"PDF bytes are provider-rendered; compared by shape (byteLength) only in the scenario script"}},"responses":{"200":{"description":"Successful Response","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order/{order_id}/requisition/pdf":{"parameters":[{"name":"order_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_order_requisition_pdf_v3_order__order_id__requisition_pdf_get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"PDF bytes are provider-rendered; compared by shape (byteLength) only in the scenario script"}},"responses":{"200":{"description":"Successful Response","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/orders":{"get":{"operationId":"get_orders_v3_orders_get","x-mockingbird":{"supported":true,"parity":{"enabled":true,"safe":true}},"parameters":[{"name":"user_id","in":"query","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"user","missing":"00000000-0000-4000-8000-000000000000"}}},{"name":"page","in":"query","required":false,"schema":{"type":"integer","minimum":1,"default":1}},{"name":"size","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":50}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetOrdersResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order_transaction/{transaction_id}":{"parameters":[{"name":"transaction_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order_transaction","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_order_transaction_v3_order_transaction__transaction_id__get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"order transactions are not available to the sandbox team (feature not available)"}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetOrderTransactionResponse"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v3/order_transaction/{transaction_id}/result":{"parameters":[{"name":"transaction_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order_transaction","missing":"00000000-0000-4000-8000-000000000000"}}}],"get":{"operationId":"get_order_transaction_result_v3_order_transaction__transaction_id__result_get","x-mockingbird":{"supported":true,"parity":{"enabled":false,"safe":true,"reason":"lab results are not available to the sandbox team (feature not available)"}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LabResultsRaw"}}}},"404":{"description":"Not Found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"securitySchemes":{"apiKeyAuth":{"type":"apiKey","in":"header","name":"x-vital-api-key"}},"schemas":{"UserCreateBody":{"type":"object","required":["client_user_id"],"properties":{"client_user_id":{"type":"string","minLength":1,"maxLength":64,"examples":["mbr_8891","patient-1042","user.profile_52"]},"fallback_time_zone":{"type":["string","null"],"enum":["America/Los_Angeles","America/New_York","Europe/London","UTC",null]}}},"ClientFacingUser":{"type":"object","required":["user_id","team_id","client_user_id","created_on","connected_sources","fallback_time_zone"],"properties":{"user_id":{"type":"string","format":"uuid","x-mockingbird-resource":{"type":"user","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"team_id":{"type":"string","format":"uuid","x-mockingbird-volatile":{"kind":"account"}},"client_user_id":{"type":"string"},"created_on":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"connected_sources":{"type":"array","items":{"type":"object","additionalProperties":true}},"fallback_time_zone":{"type":["object","null"],"properties":{"id":{"type":"string"},"source_slug":{"type":"string"},"updated_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}}}},"fallback_birth_date":{"type":["object","null"],"properties":{"value":{"type":"string"},"source_slug":{"type":"string"},"updated_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}}}},"ingestion_start":{"type":["string","null"]},"ingestion_end":{"type":["string","null"]}}},"UserInfoPatchBody":{"type":"object","properties":{"first_name":{"type":["string","null"]},"last_name":{"type":["string","null"]},"dob":{"type":["string","null"]},"gender":{"type":["string","null"]},"phone_number":{"type":["string","null"]},"email":{"type":["string","null"]},"address":{"type":["object","null"],"additionalProperties":true}}},"UserInfo":{"type":"object","additionalProperties":true},"ClientUserIDConflictResponse":{"type":"object","required":["detail"],"properties":{"detail":{"type":"object","required":["error_type","error_message","user_id","created_on"],"properties":{"error_type":{"type":"string"},"error_message":{"type":"string"},"user_id":{"type":"string","format":"uuid","x-mockingbird-volatile":{"kind":"id"}},"created_on":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}}}}}},"UserSuccessResponse":{"type":"object","required":["success"],"properties":{"success":{"type":"boolean"}}},"PaginatedUsersResponse":{"type":"object","required":["users","total","offset","limit"],"properties":{"users":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingUser"}},"total":{"type":"integer"},"offset":{"type":"integer"},"limit":{"type":"integer"}}},"UserPatchBody":{"type":"object","properties":{"fallback_time_zone":{"type":["string","null"]},"fallback_birth_date":{"type":["string","null"],"format":"date"},"ingestion_start":{"type":["string","null"],"format":"date"},"ingestion_end":{"type":["string","null"],"format":"date"},"client_user_id":{"type":["string","null"]}}},"LabTestCollectionMethod":{"type":"string","enum":["testkit","walk_in_test","at_home_phlebotomy","on_site_collection"]},"LabTestGenerationMethodFilter":{"type":"string","enum":["auto","manual","all"]},"LabTestSampleType":{"type":"string","enum":["dried_blood_spot","arm_collector","serum","saliva","urine","stool"]},"LabTestStatus":{"type":"string","enum":["active","pending_approval","inactive"]},"MarkerType":{"type":"string","enum":["biomarker","panel"]},"ClientFacingLab":{"type":"object","required":["id","slug","name","first_line_address","city","zipcode","collection_methods","sample_types"],"properties":{"id":{"type":"integer","x-mockingbird-volatile":{"kind":"id"}},"slug":{"type":"string"},"name":{"type":"string"},"first_line_address":{"type":"string"},"city":{"type":"string"},"zipcode":{"type":"string"},"collection_methods":{"type":"array","items":{"$ref":"#/components/schemas/LabTestCollectionMethod"}},"sample_types":{"type":"array","items":{"$ref":"#/components/schemas/LabTestSampleType"}},"logo_url":{"type":["string","null"]}}},"ClientFacingMarker":{"type":"object","required":["id","name","slug"],"properties":{"id":{"type":"integer","x-mockingbird-volatile":{"kind":"id"}},"name":{"type":"string"},"slug":{"type":"string"},"description":{"type":["string","null"]},"lab_id":{"type":["integer","null"]},"provider_id":{"type":["string","null"]},"type":{"anyOf":[{"$ref":"#/components/schemas/MarkerType"},{"type":"null"}]},"unit":{"type":["string","null"]},"price":{"type":["string","null"]},"aoe":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}]},"a_la_carte_enabled":{"type":"boolean","default":false},"common_tat_days":{"type":["integer","null"]},"worst_case_tat_days":{"type":["integer","null"]},"is_orderable":{"type":["boolean","null"],"default":true}}},"ClientFacingLabTest":{"type":"object","required":["id","slug","name","sample_type","method","price","is_active","status","lab","markers"],"properties":{"id":{"type":"string","format":"uuid","x-mockingbird-resource":{"type":"lab_test","identity":true}},"slug":{"type":"string","x-mockingbird-volatile":{"kind":"token"}},"name":{"type":"string","x-mockingbird-volatile":{"kind":"token"}},"sample_type":{"$ref":"#/components/schemas/LabTestSampleType"},"method":{"$ref":"#/components/schemas/LabTestCollectionMethod"},"price":{"type":"number"},"is_active":{"type":"boolean"},"status":{"$ref":"#/components/schemas/LabTestStatus"},"fasting":{"type":"boolean","default":false},"lab":{"anyOf":[{"$ref":"#/components/schemas/ClientFacingLab"},{"type":"null"}]},"markers":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingMarker"}},{"type":"null"}]},"is_delegated":{"type":"boolean","default":false},"auto_generated":{"type":"boolean","default":false},"has_collection_instructions":{"type":["boolean","null"]},"common_tat_days":{"type":["integer","null"]},"worst_case_tat_days":{"type":["integer","null"]}}},"LabTestResourcesResponse":{"type":"object","required":["data","next_cursor"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingLabTest"}},"next_cursor":{"type":["string","null"]}}},"LabAccountStatus":{"type":"string","enum":["active","pending","suspended","ready_to_launch"]},"LabAccountDelegatedFlow":{"type":"string","enum":["order_delegated","result_delegated","fully_delegated","not_delegated"]},"USState":{"type":"string","enum":["AL","AK","AZ","AR","CA","CO","CT","DE","FL","GA","HI","ID","IL","IN","IA","KS","KY","LA","ME","MD","MA","MI","MN","MS","MO","MT","NE","NV","NH","NJ","NM","NY","NC","ND","OH","OK","OR","PA","RI","SC","SD","TN","TX","UT","VT","VA","WA","WV","WI","WY"]},"Labs":{"type":"string","enum":["ayumetrix","spiriplex","ussl","quest","sonora_quest","labcorp","bioreference","us_biotek","manual","sanocardio","ihd","nexus","my_uti","crl","mtl"]},"ClientFacingLabAccount":{"type":"object","required":["id","lab","status","delegated_flow","provider_account_id","allowed_billing","team_id_allowlist"],"properties":{"id":{"type":"string","format":"uuid","x-mockingbird-resource":{"type":"lab_account","identity":true}},"lab":{"$ref":"#/components/schemas/Labs"},"org_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"status":{"$ref":"#/components/schemas/LabAccountStatus"},"delegated_flow":{"$ref":"#/components/schemas/LabAccountDelegatedFlow"},"provider_account_id":{"type":"string"},"account_name":{"anyOf":[{"type":"string"},{"type":"null"}]},"default_clinical_notes":{"anyOf":[{"type":"string"},{"type":"null"}]},"business_units":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"allowed_billing":{"type":"object","propertyNames":{"$ref":"#/components/schemas/Billing"},"additionalProperties":{"type":"array","items":{"$ref":"#/components/schemas/USState"}}},"team_id_allowlist":{"type":"array","items":{"type":"string","format":"uuid"}}}},"GetTeamLabAccountsResponse":{"type":"object","required":["data"],"properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingLabAccount"}}}},"ClientFacingResult":{"type":"object","required":["name","slug","type"],"properties":{"name":{"type":"string"},"slug":{"type":"string"},"provider_id":{"type":["string","null"]},"type":{"$ref":"#/components/schemas/ResultType"},"value":{"type":["number","null"]},"unit":{"type":["string","null"]},"low_value":{"type":["number","null"]},"high_value":{"type":["number","null"]},"string_value":{"type":["string","null"]},"coded_value":{"type":["string","null"]},"is_abnormal":{"type":"boolean","default":false},"notes":{"type":["string","null"]}}},"GetMarkersResponse":{"type":"object","required":["markers"],"properties":{"markers":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingMarkerComplete"}},"total":{"type":["integer","null"]},"page":{"type":["integer","null"]},"size":{"type":["integer","null"]},"pages":{"type":["integer","null"]}}},"ClientFacingExpectedResult":{"type":"object","required":["id","name","slug","required"],"properties":{"id":{"type":"integer","x-mockingbird-volatile":{"kind":"id"}},"name":{"type":"string"},"slug":{"type":"string"},"lab_id":{"type":["integer","null"]},"provider_id":{"type":["string","null"]},"required":{"type":"boolean"},"loinc":{"type":["object","null"],"required":["id","name","slug","code"],"properties":{"id":{"type":"integer","x-mockingbird-volatile":{"kind":"id"}},"name":{"type":"string"},"slug":{"type":"string"},"code":{"type":"string"},"unit":{"type":["string","null"]}}}}},"ClientFacingMarkerComplete":{"type":"object","required":["id","name","slug","expected_results"],"properties":{"id":{"type":"integer","x-mockingbird-volatile":{"kind":"id"}},"name":{"type":"string"},"slug":{"type":"string"},"description":{"type":["string","null"]},"lab_id":{"type":["integer","null"]},"provider_id":{"type":["string","null"]},"type":{"anyOf":[{"$ref":"#/components/schemas/MarkerType"},{"type":"null"}]},"unit":{"type":["string","null"]},"price":{"type":["string","null"]},"a_la_carte_enabled":{"type":"boolean","default":false},"common_tat_days":{"type":["integer","null"]},"worst_case_tat_days":{"type":["integer","null"]},"is_orderable":{"type":["boolean","null"],"default":true},"expected_results":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingExpectedResult"}}}},"ListOrderSetMarkersRequest":{"type":"object","required":["order_set_id"],"properties":{"order_set_id":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order_transaction","missing":"00000000-0000-4000-8000-000000000000"}},"provider_id":{"type":["string","null"]}}},"AllowedRadius":{"type":"integer","enum":[10,20,25,50,100]},"AppointmentPscLabs":{"type":"string","enum":["quest"]},"AppointmentProvider":{"type":"string","enum":["getlabs","phlebfinders","quest"]},"AppointmentType":{"type":"string","enum":["phlebotomy","patient_service_center"]},"AppointmentStatus":{"type":"string","enum":["confirmed","pending","reserved","in_progress","completed","cancelled"]},"AppointmentEventStatus":{"type":"string","enum":["pending","reserved","scheduled","completed","cancelled","in_progress"]},"AppointmentServiceType":{"type":"string","enum":["appointment-ready","appointment-request"]},"Billing":{"type":"string","enum":["client_bill","commercial_insurance","patient_bill_passthrough","patient_bill"]},"LabLocationCapability":{"type":"string","enum":["stat","appointment_scheduling_via_junction","appointment_scheduling_with_lab"]},"LngLat":{"type":"object","required":["lng","lat"],"properties":{"lng":{"type":"number"},"lat":{"type":"number"}}},"UsAddress":{"type":"object","required":["first_line","city","state","zip_code"],"properties":{"first_line":{"type":"string","minLength":1},"second_line":{"type":["string","null"]},"city":{"type":"string","minLength":1},"state":{"type":"string","minLength":1},"zip_code":{"type":"string","minLength":1},"unit":{"type":["string","null"]}}},"AppointmentLocation":{"type":"object","required":["location","address","code","name"],"properties":{"location":{"$ref":"#/components/schemas/LngLat"},"distance":{"type":["number","null"]},"address":{"$ref":"#/components/schemas/UsAddress"},"code":{"type":"string"},"name":{"type":"string"},"iana_timezone":{"type":["string","null"]}}},"TimeSlot":{"type":"object","required":["start","end","price","is_priority","num_appointments_available"],"properties":{"booking_key":{"type":["string","null"],"x-mockingbird-resource":{"type":"booking_key","identity":true},"x-mockingbird-volatile":{"kind":"token"}},"start":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"end":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"expires_at":{"type":["string","null"],"x-mockingbird-volatile":{"kind":"timestamp"}},"price":{"type":"number"},"is_priority":{"type":"boolean"},"num_appointments_available":{"type":"integer"}}},"DaySlots":{"type":"object","required":["date","slots"],"properties":{"location":{"anyOf":[{"$ref":"#/components/schemas/AppointmentLocation"},{"type":"null"}]},"date":{"type":"string","format":"date"},"slots":{"type":"array","items":{"$ref":"#/components/schemas/TimeSlot"}}}},"AppointmentAvailabilitySlots":{"type":"object","required":["slots"],"properties":{"slots":{"type":"array","items":{"$ref":"#/components/schemas/DaySlots"}},"timezone":{"type":["string","null"]}}},"AppointmentBookingRequest":{"type":"object","required":["booking_key"],"properties":{"booking_key":{"type":"string","minLength":1,"x-mockingbird-resource-ref":{"type":"booking_key"}}}},"AppointmentRescheduleRequest":{"type":"object","required":["booking_key"],"properties":{"booking_key":{"type":"string","minLength":1,"x-mockingbird-resource-ref":{"type":"booking_key"}}}},"PscAppointmentBookingRequest":{"type":"object","required":["booking_key","site_code"],"properties":{"booking_key":{"type":"string","minLength":1,"x-mockingbird-resource-ref":{"type":"booking_key"}},"site_code":{"type":"string","minLength":1}}},"PhlebotomyAppointmentCancelRequest":{"type":"object","required":["cancellation_reason_id"],"properties":{"cancellation_reason_id":{"type":"string","minLength":1},"notes":{"type":["string","null"]}}},"PscAppointmentCancelRequest":{"type":"object","required":["cancellationReasonId"],"properties":{"cancellationReasonId":{"type":"string","minLength":1},"note":{"type":["string","null"]}}},"ClientFacingAppointmentEvent":{"type":"object","required":["created_at","status"],"properties":{"created_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"status":{"$ref":"#/components/schemas/AppointmentEventStatus"},"data":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}]}}},"ClientFacingAppointment":{"type":"object","required":["id","user_id","order_id","address","location","type","provider","status","provider_id","can_reschedule","event_status","events"],"properties":{"id":{"type":"string","format":"uuid","x-mockingbird-resource":{"type":"appointment","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"user_id":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"user"}},"order_id":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"order"}},"address":{"$ref":"#/components/schemas/UsAddress"},"location":{"$ref":"#/components/schemas/LngLat"},"start_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"timestamp"}},"end_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"timestamp"}},"iana_timezone":{"type":["string","null"]},"type":{"$ref":"#/components/schemas/AppointmentType"},"provider":{"$ref":"#/components/schemas/AppointmentProvider"},"status":{"$ref":"#/components/schemas/AppointmentStatus"},"provider_id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"external_id":{"type":["string","null"],"x-mockingbird-volatile":{"kind":"id"}},"can_reschedule":{"type":"boolean"},"event_status":{"$ref":"#/components/schemas/AppointmentEventStatus"},"event_data":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}]},"order_transaction_id":{"type":["string","null"],"x-mockingbird-resource-ref":{"type":"order_transaction"}},"appointment_notes":{"type":["string","null"]},"events":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingAppointmentEvent"}}}},"ClientFacingAppointmentCancellationReason":{"type":"object","required":["id","name","is_refundable"],"properties":{"id":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"name":{"type":"string"},"is_refundable":{"type":"boolean"}}},"PhlebotomyAreaInfo":{"type":"object","required":["is_served","providers"],"properties":{"is_served":{"type":"boolean"},"providers":{"type":"array","items":{"$ref":"#/components/schemas/PhlebotomyProviderInfo"}}}},"PhlebotomyProviderInfo":{"type":"object","required":["name","service_types"],"properties":{"name":{"$ref":"#/components/schemas/AppointmentProvider"},"service_types":{"type":"array","items":{"$ref":"#/components/schemas/AppointmentServiceType"}}}},"PscAreaInfoDetails":{"type":"object","required":["appointment_with_vital","within_radius","radius"],"properties":{"appointment_with_vital":{"type":"boolean"},"within_radius":{"type":"number"},"radius":{"type":"string"},"capabilities":{"type":["array","null"],"items":{"$ref":"#/components/schemas/LabLocationCapability"}}}},"PscAreaInfo":{"type":"object","required":["patient_service_centers","supported_bill_types","lab_id"],"properties":{"patient_service_centers":{"$ref":"#/components/schemas/PscAreaInfoDetails"},"supported_bill_types":{"type":"array","items":{"$ref":"#/components/schemas/Billing"}},"lab_id":{"type":"integer"}}},"AreaInfo":{"type":"object","required":["zip_code","phlebotomy","central_labs"],"properties":{"zip_code":{"type":"string"},"phlebotomy":{"$ref":"#/components/schemas/PhlebotomyAreaInfo"},"central_labs":{"type":"object","additionalProperties":{"$ref":"#/components/schemas/PscAreaInfo"}}}},"LabLocationMetadata":{"type":"object","required":["name","state","city","zip_code","first_line"],"properties":{"name":{"type":"string"},"state":{"type":"string"},"city":{"type":"string"},"zip_code":{"type":"string"},"first_line":{"type":"string"},"second_line":{"type":["string","null"]},"phone_number":{"type":["string","null"]},"fax_number":{"type":["string","null"]},"hours":{"type":["object","null"],"additionalProperties":true}}},"ClientFacingLabLocation":{"type":"object","required":["metadata","distance","site_code","supported_bill_types","location","capabilities"],"properties":{"metadata":{"$ref":"#/components/schemas/LabLocationMetadata"},"distance":{"type":"number"},"site_code":{"type":"string"},"supported_bill_types":{"type":"array","items":{"$ref":"#/components/schemas/Billing"}},"location":{"$ref":"#/components/schemas/LngLat"},"capabilities":{"type":"array","items":{"$ref":"#/components/schemas/LabLocationCapability"}}}},"PscInfo":{"type":"object","required":["lab_id","slug","patient_service_centers"],"properties":{"lab_id":{"type":"integer"},"slug":{"type":"string"},"patient_service_centers":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingLabLocation"}}}},"OrderTopLevelStatus":{"type":"string","enum":["received","collecting_sample","sample_with_lab","completed","cancelled","failed"]},"OrderLowLevelStatus":{"type":"string","enum":["ordered","requisition_created","requisition_bypassed","transit_customer","out_for_delivery","with_customer","transit_lab","delivered_to_lab","completed","failure_to_deliver_to_lab","failure_to_deliver_to_customer","problem_in_transit_lab","problem_in_transit_customer","sample_error","appointment_scheduled","appointment_cancelled","appointment_pending","draw_completed","cancelled","lost","do_not_process","partial_results","awaiting_registration","registered","redraw_available","corrected","lab_processing_blocked"]},"OrderStatus":{"type":"string","enum":["received.walk_in_test.ordered","received.walk_in_test.requisition_created","received.walk_in_test.requisition_bypassed","completed.walk_in_test.completed","sample_with_lab.walk_in_test.partial_results","failed.walk_in_test.sample_error","cancelled.walk_in_test.cancelled","collecting_sample.walk_in_test.appointment_pending","collecting_sample.walk_in_test.appointment_scheduled","collecting_sample.walk_in_test.appointment_cancelled","collecting_sample.walk_in_test.redraw_available","received.at_home_phlebotomy.ordered","received.at_home_phlebotomy.requisition_created","received.at_home_phlebotomy.requisition_bypassed","collecting_sample.at_home_phlebotomy.appointment_pending","collecting_sample.at_home_phlebotomy.appointment_scheduled","collecting_sample.at_home_phlebotomy.draw_completed","collecting_sample.at_home_phlebotomy.appointment_cancelled","completed.at_home_phlebotomy.completed","sample_with_lab.at_home_phlebotomy.partial_results","cancelled.at_home_phlebotomy.cancelled","failed.at_home_phlebotomy.sample_error","received.testkit.ordered","received.testkit.awaiting_registration","received.testkit.requisition_created","received.testkit.requisition_bypassed","received.testkit.registered","collecting_sample.testkit.transit_customer","collecting_sample.testkit.out_for_delivery","collecting_sample.testkit.with_customer","collecting_sample.testkit.transit_lab","sample_with_lab.testkit.delivered_to_lab","sample_with_lab.testkit.lab_processing_blocked","completed.testkit.completed","failed.testkit.failure_to_deliver_to_customer","failed.testkit.failure_to_deliver_to_lab","failed.testkit.sample_error","failed.testkit.lost","cancelled.testkit.cancelled","cancelled.testkit.do_not_process","collecting_sample.testkit.problem_in_transit_customer","collecting_sample.testkit.problem_in_transit_lab","received.on_site_collection.ordered","received.on_site_collection.requisition_created","received.on_site_collection.requisition_bypassed","sample_with_lab.on_site_collection.draw_completed","completed.on_site_collection.completed","cancelled.on_site_collection.cancelled","sample_with_lab.on_site_collection.partial_results","failed.on_site_collection.sample_error","completed.walk_in_test.corrected","completed.at_home_phlebotomy.corrected","completed.on_site_collection.corrected","completed.testkit.corrected"]},"OrderStatusDetail":{"type":"string","enum":["fulfillment_error","date_of_collection_unspecified","demographic_information_unspecified","demographic_information_mismatch","sample_quantity_not_sufficient","sample_contaminated","sample_stability_exceeded","sample_hemolyzed","sample_improper_collection"]},"OrderTransactionStatus":{"type":"string","enum":["active","completed","cancelled"]},"ClientFacingOrderEvent":{"type":"object","required":["id","created_at","status"],"properties":{"id":{"type":"integer","x-mockingbird-volatile":{"kind":"opaque"}},"created_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"status":{"$ref":"#/components/schemas/OrderStatus"},"status_detail":{"anyOf":[{"$ref":"#/components/schemas/OrderStatusDetail"},{"type":"null"}]}}},"ShippingAddress":{"type":"object","required":["receiver_name","first_line","city","state","zip","country","phone_number"],"properties":{"receiver_name":{"type":"string"},"first_line":{"type":"string"},"second_line":{"type":["string","null"]},"city":{"type":"string"},"state":{"type":"string"},"zip":{"type":"string"},"country":{"type":"string"},"phone_number":{"type":"string"}}},"ClientFacingPhysician":{"type":"object","required":["first_name","last_name","npi"],"properties":{"first_name":{"type":"string"},"last_name":{"type":"string"},"npi":{"type":"string"}}},"ClientFacingShipment":{"type":"object","required":["id","outbound_tracking_number","outbound_tracking_url","inbound_tracking_number","inbound_tracking_url","outbound_courier","inbound_courier","notes"],"properties":{"id":{"type":"string","format":"uuid","x-mockingbird-volatile":{"kind":"id"}},"outbound_tracking_number":{"type":["string","null"]},"outbound_tracking_url":{"type":["string","null"]},"inbound_tracking_number":{"type":["string","null"]},"inbound_tracking_url":{"type":["string","null"]},"outbound_courier":{"type":["string","null"]},"inbound_courier":{"type":["string","null"]},"notes":{"type":["string","null"]}}},"ClientFacingTestkitOrder":{"type":"object","required":["id","shipment","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","x-mockingbird-volatile":{"kind":"id"}},"shipment":{"anyOf":[{"$ref":"#/components/schemas/ClientFacingShipment"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"opaque"}},"created_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"updated_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}}}},"ClientFacingWalkInOrder":{"type":"object","required":["id","appointment_id","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid","x-mockingbird-volatile":{"kind":"id"}},"appointment_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"created_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"updated_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}}}},"ClientFacingTestKitOrderDetails":{"type":"object","required":["type"],"properties":{"type":{"type":"string","enum":["testkit","walk_in_test","at_home_phlebotomy","on_site_collection"]},"data":{"anyOf":[{"$ref":"#/components/schemas/ClientFacingTestkitOrder"},{"$ref":"#/components/schemas/ClientFacingWalkInOrder"},{"type":"null"}]}}},"ClientFacingPatientDetailsCompatible":{"type":"object","required":["first_name","last_name","dob","gender","phone_number","email"],"properties":{"first_name":{"type":"string","minLength":1,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9 .,'-]*$","examples":["Ada","Chris","Maria","Jun"]},"last_name":{"type":"string","minLength":1,"pattern":"^[a-zA-Z0-9][a-zA-Z0-9 .,'-]*$","examples":["Lovelace","Vouga","Delgado","Park"]},"dob":{"type":"string","format":"date-time","examples":["1990-06-15T00:00:00.000Z","1983-02-02T00:00:00.000Z"]},"gender":{"type":"string","minLength":1,"pattern":"\\\\S","examples":["male","female","other","prefer_not_to_say"]},"phone_number":{"type":"string","minLength":1,"pattern":"^\\\\+1[2-9]\\\\d{9}$","examples":["+14155550123","+12125550987","+13105554444"]},"email":{"type":"string","format":"email","minLength":1,"examples":["ada@example.com","chris@example.com","maria@example.com"]}}},"PatientAddressCompatible":{"type":"object","required":["first_line","city","state","zip","country"],"properties":{"receiver_name":{"type":"string","default":"","examples":["Ada Lovelace","Chris Vouga"]},"first_line":{"type":"string","minLength":1,"pattern":"\\\\S","examples":["1 Main St","450 Market St","88 Kearny St"]},"second_line":{"type":["string","null"]},"city":{"type":"string","minLength":1,"pattern":"\\\\S","examples":["San Diego","San Francisco","New York"]},"state":{"type":"string","minLength":1,"enum":["AL","AK","AZ","AR","CA","CO","CT","DE","FL","GA","HI","ID","IL","IN","IA","KS","KY","LA","ME","MD","MA","MI","MN","MS","MO","MT","NE","NV","NH","NJ","NM","NY","NC","ND","OH","OK","OR","PA","RI","SC","SD","TN","TX","UT","VT","VA","WA","WV","WI","WY"]},"zip":{"type":"string","pattern":"^\\\\d{5}(-\\\\d{4})?$","examples":["92101","94105","10001"]},"country":{"type":"string","minLength":1,"pattern":"\\\\S","examples":["US"]},"access_notes":{"type":["string","null"],"examples":[null]},"phone_number":{"type":["string","null"],"examples":["+14155550123",null]}}},"AoeAnswerRequest":{"type":"object","required":["marker_id","question_id","answer"],"properties":{"marker_id":{"type":"integer","examples":[2075,1975]},"question_id":{"type":"integer","examples":[1]},"answer":{"type":"string","minLength":1,"examples":["No","Yes"]}}},"ClientFacingOrderInTransaction":{"type":"object","required":["id","created_at","updated_at"],"properties":{"id":{"type":"string","format":"uuid"},"low_level_status":{"anyOf":[{"$ref":"#/components/schemas/OrderLowLevelStatus"},{"type":"null"}]},"low_level_status_created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"timestamp"}},"created_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"updated_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}}}},"ClientFacingOrderTransaction":{"type":"object","required":["id","status","orders"],"properties":{"id":{"type":"string","format":"uuid","x-mockingbird-resource":{"type":"order_transaction","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"status":{"$ref":"#/components/schemas/OrderTransactionStatus"},"orders":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingOrderInTransaction"}}}},"OrderSummary":{"type":"object","required":["id","last_status","last_status_created_at","updated_at","created_at"],"properties":{"id":{"type":"string","format":"uuid"},"origin":{"anyOf":[{"type":"string","enum":["initial","redraw","recreation"]},{"type":"null"}]},"parent_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"last_status":{"$ref":"#/components/schemas/OrderLowLevelStatus"},"last_status_created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"created_at":{"type":"string","format":"date-time"}}},"GetOrderTransactionResponse":{"type":"object","required":["id","team_id","status"],"properties":{"id":{"type":"string","format":"uuid","x-mockingbird-resource":{"type":"order_transaction","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"team_id":{"type":"string","format":"uuid","x-mockingbird-volatile":{"kind":"account"}},"status":{"$ref":"#/components/schemas/OrderTransactionStatus"},"orders":{"type":"array","items":{"$ref":"#/components/schemas/OrderSummary"}}}},"ClientFacingOrder":{"type":"object","required":["user_id","id","team_id","patient_details","patient_address","lab_test","details","sample_id","notes","created_at","updated_at","events","last_event","health_insurance_id","requisition_form_url","shipping_details","has_abn"],"properties":{"user_id":{"type":"string","format":"uuid"},"id":{"type":"string","format":"uuid","x-mockingbird-resource":{"type":"order","identity":true},"x-mockingbird-volatile":{"kind":"id"}},"team_id":{"type":"string","format":"uuid","x-mockingbird-volatile":{"kind":"account"}},"patient_details":{"anyOf":[{"$ref":"#/components/schemas/ClientFacingPatientDetailsCompatible"},{"type":"null"}]},"patient_address":{"anyOf":[{"$ref":"#/components/schemas/PatientAddressCompatible"},{"type":"null"}]},"lab_test":{"$ref":"#/components/schemas/ClientFacingLabTest"},"details":{"$ref":"#/components/schemas/ClientFacingTestKitOrderDetails"},"sample_id":{"anyOf":[{"type":"string"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"opaque"}},"notes":{"anyOf":[{"type":"string"},{"type":"null"}]},"clinical_notes":{"anyOf":[{"type":"string"},{"type":"null"}]},"created_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"updated_at":{"type":"string","format":"date-time","x-mockingbird-volatile":{"kind":"timestamp"}},"events":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingOrderEvent"}},"status":{"anyOf":[{"$ref":"#/components/schemas/OrderTopLevelStatus"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"opaque"}},"last_event":{"$ref":"#/components/schemas/ClientFacingOrderEvent"},"physician":{"anyOf":[{"$ref":"#/components/schemas/ClientFacingPhysician"},{"type":"null"}]},"health_insurance_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"requisition_form_url":{"anyOf":[{"type":"string"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"url"}},"priority":{"type":"boolean","default":false},"shipping_details":{"anyOf":[{"$ref":"#/components/schemas/ShippingAddress"},{"type":"null"}]},"activate_by":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}]},"passthrough":{"anyOf":[{"type":"string"},{"type":"null"}]},"billing_type":{"type":"string","enum":["client_bill","commercial_insurance","patient_bill_passthrough","patient_bill","upfront_payment"],"default":"client_bill"},"icd_codes":{"anyOf":[{"type":"array","items":{"type":"string"}},{"type":"null"}]},"has_abn":{"type":"boolean"},"interpretation":{"anyOf":[{"type":"string","enum":["normal","abnormal","critical","unknown"]},{"type":"null"}]},"has_missing_results":{"anyOf":[{"type":"boolean"},{"type":"null"}]},"expected_result_by_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}]},"worst_case_result_by_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}]},"origin":{"anyOf":[{"type":"string","enum":["initial","redraw","recreation"]},{"type":"null"}]},"order_transaction":{"anyOf":[{"$ref":"#/components/schemas/ClientFacingOrderTransaction"},{"type":"null"}]}}},"OrderSetRequest":{"type":"object","required":["lab_test_ids"],"properties":{"lab_test_ids":{"type":"array","minItems":1,"items":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"lab_test","missing":"00000000-0000-4000-8000-000000000000"}}}}},"CreateOrderRequest":{"type":"object","required":["user_id","patient_details","patient_address","order_set"],"properties":{"user_id":{"type":"string","format":"uuid","x-mockingbird-resource-ref":{"type":"user","missing":"00000000-0000-4000-8000-000000000000"}},"patient_details":{"$ref":"#/components/schemas/ClientFacingPatientDetailsCompatible"},"patient_address":{"$ref":"#/components/schemas/PatientAddressCompatible"},"order_set":{"$ref":"#/components/schemas/OrderSetRequest"},"clinical_notes":{"anyOf":[{"type":"string"},{"type":"null"}]},"passthrough":{"anyOf":[{"type":"string"},{"type":"null"}]},"collection_method":{"$ref":"#/components/schemas/LabTestCollectionMethod"},"lab_account_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}]},"idempotency_key":{"type":["string","null"]},"idempotency_error":{"type":["string","null"]},"aoe_answers":{"type":["array","null"],"items":{"$ref":"#/components/schemas/AoeAnswerRequest"}}}},"PostOrderResponse":{"type":"object","required":["order","status","message"],"properties":{"order":{"$ref":"#/components/schemas/ClientFacingOrder"},"status":{"type":"string"},"message":{"type":"string"}}},"ResultType":{"type":"string","enum":["numeric","range","comment","coded_value"]},"ParentBiomarkerData":{"type":"object","required":["marker_id","name","slug"],"properties":{"marker_id":{"type":"integer"},"name":{"type":"string"},"slug":{"type":"string"},"provider_id":{"type":["string","null"]}}},"BiomarkerResult":{"type":"object","required":["name","result","type"],"properties":{"name":{"type":"string"},"slug":{"type":"string","default":""},"value":{"anyOf":[{"type":"number"},{"type":"null"}]},"result":{"type":"string"},"type":{"$ref":"#/components/schemas/ResultType"},"unit":{"type":["string","null"]},"timestamp":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"timestamp"}},"notes":{"type":["string","null"]},"reference_range":{"type":["string","null"]},"min_range_value":{"type":["number","null"]},"max_range_value":{"type":["number","null"]},"is_above_max_range":{"type":["boolean","null"]},"is_below_min_range":{"type":["boolean","null"]},"interpretation":{"type":"string","default":"normal"},"source_interpretation":{"type":"string","default":""},"loinc":{"type":["string","null"]},"loinc_slug":{"type":["string","null"]},"provider_id":{"type":["string","null"]},"source_markers":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ParentBiomarkerData"}},{"type":"null"}]},"performing_laboratory":{"type":["string","null"]},"source_sample_id":{"type":["string","null"]}}},"FailureType":{"type":"string","enum":["quantity_not_sufficient_failure","collection_process_failure","drop_off_failure","internal_lab_failure","order_entry_failure","non_failure","unknown_failure","patient_condition_failure","missing_result_calc_failure","missing_demo_aoe_calc_failure","insufficient_volume"]},"MissingBiomarkerResult":{"type":"object","required":["name","slug","inferred_failure_type"],"properties":{"name":{"type":"string"},"slug":{"type":"string"},"inferred_failure_type":{"$ref":"#/components/schemas/FailureType"},"note":{"type":["string","null"]},"loinc":{"type":["string","null"]},"loinc_slug":{"type":["string","null"]},"provider_id":{"type":["string","null"]},"source_markers":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/ParentBiomarkerData"}},{"type":"null"}]}}},"SampleData":{"type":"object","properties":{"sample_id":{"type":["string","null"]},"control_number":{"type":["string","null"]},"date_collected":{"type":["string","null"]},"date_received":{"type":["string","null"]},"date_reported":{"type":["string","null"]},"performing_laboratories":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}]},"clinical_information":{"anyOf":[{"type":"object","additionalProperties":true},{"type":"null"}]}}},"LabResultsMetadata":{"type":"object","required":["age","dob","patient","date_reported","specimen_number"],"properties":{"age":{"type":"string","x-mockingbird-volatile":{"kind":"token"}},"dob":{"type":"string","x-mockingbird-volatile":{"kind":"token"}},"clia_#":{"anyOf":[{"type":"string"},{"type":"null"}]},"patient":{"type":"string","x-mockingbird-volatile":{"kind":"token"}},"provider":{"anyOf":[{"type":"string"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"token"}},"laboratory":{"anyOf":[{"type":"string"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"token"}},"date_reported":{"type":"string","x-mockingbird-volatile":{"kind":"timestamp"}},"date_collected":{"anyOf":[{"type":"string"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"timestamp"}},"specimen_number":{"type":"string","x-mockingbird-volatile":{"kind":"id"}},"date_received":{"anyOf":[{"type":"string"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"timestamp"}},"status":{"type":"string","default":"final"},"interpretation":{"anyOf":[{"type":"string"},{"type":"null"}]},"patient_id":{"anyOf":[{"type":"string"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"id"}},"account_id":{"anyOf":[{"type":"string"},{"type":"null"}],"x-mockingbird-volatile":{"kind":"id"}}}},"LabResultsRaw":{"type":"object","required":["metadata","results"],"properties":{"metadata":{"$ref":"#/components/schemas/LabResultsMetadata"},"results":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/BiomarkerResult"}},{"type":"object","additionalProperties":true}],"x-mockingbird-volatile":{"kind":"opaque"}},"missing_results":{"anyOf":[{"type":"array","items":{"$ref":"#/components/schemas/MissingBiomarkerResult"}},{"type":"null"}],"x-mockingbird-volatile":{"kind":"opaque"}},"sample_information":{"anyOf":[{"type":"object","additionalProperties":{"$ref":"#/components/schemas/SampleData"}},{"type":"null"}],"x-mockingbird-volatile":{"kind":"opaque"}},"order_transaction":{"anyOf":[{"$ref":"#/components/schemas/ClientFacingOrderTransaction"},{"type":"null"}]}}},"SimulationFlags":{"type":"object","properties":{"interpretation":{"anyOf":[{"type":"string","enum":["normal","abnormal","critical","unknown"]},{"type":"null"}]},"result_types":{"type":"array","items":{"$ref":"#/components/schemas/ResultType"}},"has_missing_results":{"type":"boolean"}}},"GetOrdersResponse":{"type":"object","required":["orders"],"properties":{"orders":{"type":"array","items":{"$ref":"#/components/schemas/ClientFacingOrder"}},"total":{"anyOf":[{"type":"integer"},{"type":"null"}]},"page":{"anyOf":[{"type":"integer"},{"type":"null"}]},"size":{"anyOf":[{"type":"integer"},{"type":"null"}]}}},"HTTPValidationError":{"type":"object","properties":{"detail":{"title":"Detail"}}}}}}`);
|
|
6272
|
+
var operationIds = ["get_teams_users_v2_user_get", "create_user_v2_user_post", "get_user_v2_user__user_id__get", "delete_user_v2_user__user_id__delete", "patch_user_v2_user__user_id__patch", "get_user_by_client_user_id_v2_user_resolve__client_user_id__get", "patch_user_info_v2_user__user_id__info_patch", "get_latest_user_info_user_v2_user__user_id__info_latest_get", "get_paginated_lab_tests_for_team_v3_lab_test_get", "get_team_lab_accounts_v3_lab_test_lab_account_get", "get_lab_tests_for_team_v3_lab_tests_get", "get_lab_test_for_team_v3_lab_tests__lab_test_id__get", "get_labs_v3_lab_tests_labs_get", "get_markers_for_lab_test_v3_lab_tests__lab_test_id__markers_get", "list_order_set_markers_v3_lab_tests_list_order_set_markers_post", "create_order_v3_order_post", "get_area_info_v3_order_area_info_get", "get_psc_info_v3_order_psc_info_get", "get_phlebotomy_appointment_availability_v3_order_phlebotomy_appointment_availability_post", "get_phlebotomy_appointment_cancellation_reason_v3_order_phlebotomy_appointment_cancellation_reasons_get", "get_psc_appointment_availability_v3_order_psc_appointment_availability_post", "get_psc_appointment_cancellation_reason_v3_order_psc_appointment_cancellation_reasons_get", "get_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_get", "book_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_book_post", "reschedule_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_reschedule_patch", "cancel_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_cancel_patch", "get_psc_appointment_v3_order__order_id__psc_appointment_get", "book_psc_appointment_v3_order__order_id__psc_appointment_book_post", "reschedule_psc_appointment_v3_order__order_id__psc_appointment_reschedule_patch", "cancel_psc_appointment_v3_order__order_id__psc_appointment_cancel_patch", "get_order_v3_order__order_id__get", "cancel_order_v3_order__order_id__cancel_post", "simulate_order_v3_order__order_id__test_post", "get_result_raw_v3_order__order_id__result_get", "get_result_metadata_v3_order__order_id__result_metadata_get", "get_result_pdf_v3_order__order_id__result_pdf_get", "get_order_requisition_pdf_v3_order__order_id__requisition_pdf_get", "get_orders_v3_orders_get", "get_order_transaction_v3_order_transaction__transaction_id__get", "get_order_transaction_result_v3_order_transaction__transaction_id__result_get"];
|
|
6273
|
+
var supportedOperationIds = ["get_teams_users_v2_user_get", "create_user_v2_user_post", "get_user_v2_user__user_id__get", "delete_user_v2_user__user_id__delete", "patch_user_v2_user__user_id__patch", "get_user_by_client_user_id_v2_user_resolve__client_user_id__get", "patch_user_info_v2_user__user_id__info_patch", "get_latest_user_info_user_v2_user__user_id__info_latest_get", "get_paginated_lab_tests_for_team_v3_lab_test_get", "get_team_lab_accounts_v3_lab_test_lab_account_get", "get_lab_tests_for_team_v3_lab_tests_get", "get_lab_test_for_team_v3_lab_tests__lab_test_id__get", "get_labs_v3_lab_tests_labs_get", "get_markers_for_lab_test_v3_lab_tests__lab_test_id__markers_get", "list_order_set_markers_v3_lab_tests_list_order_set_markers_post", "create_order_v3_order_post", "get_area_info_v3_order_area_info_get", "get_psc_info_v3_order_psc_info_get", "get_phlebotomy_appointment_availability_v3_order_phlebotomy_appointment_availability_post", "get_phlebotomy_appointment_cancellation_reason_v3_order_phlebotomy_appointment_cancellation_reasons_get", "get_psc_appointment_availability_v3_order_psc_appointment_availability_post", "get_psc_appointment_cancellation_reason_v3_order_psc_appointment_cancellation_reasons_get", "get_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_get", "book_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_book_post", "reschedule_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_reschedule_patch", "cancel_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_cancel_patch", "get_psc_appointment_v3_order__order_id__psc_appointment_get", "book_psc_appointment_v3_order__order_id__psc_appointment_book_post", "reschedule_psc_appointment_v3_order__order_id__psc_appointment_reschedule_patch", "cancel_psc_appointment_v3_order__order_id__psc_appointment_cancel_patch", "get_order_v3_order__order_id__get", "cancel_order_v3_order__order_id__cancel_post", "simulate_order_v3_order__order_id__test_post", "get_result_raw_v3_order__order_id__result_get", "get_result_metadata_v3_order__order_id__result_metadata_get", "get_result_pdf_v3_order__order_id__result_pdf_get", "get_order_requisition_pdf_v3_order__order_id__requisition_pdf_get", "get_orders_v3_orders_get", "get_order_transaction_v3_order_transaction__transaction_id__get", "get_order_transaction_result_v3_order_transaction__transaction_id__result_get"];
|
|
6274
|
+
|
|
4958
6275
|
// src/results.ts
|
|
4959
6276
|
var RESULT_TYPES2 = ["numeric", "range", "comment", "coded_value"];
|
|
4960
6277
|
var INTERPRETATIONS2 = ["normal", "abnormal", "critical", "unknown"];
|
|
@@ -5103,13 +6420,13 @@ function resultsNotAvailable(orderId) {
|
|
|
5103
6420
|
var resultsHandlers = (state) => ({
|
|
5104
6421
|
get_result_raw_v3_order__order_id__result_get: async (context) => {
|
|
5105
6422
|
const orderId = context.params.order_id ?? "";
|
|
5106
|
-
const order = requireOrder(state, orderId, context
|
|
6423
|
+
const order = requireOrder(state, orderId, context);
|
|
5107
6424
|
const user = state.users.get(order.user_id);
|
|
5108
6425
|
if (!resultsReady(order)) resultsNotAvailable(order.id);
|
|
5109
6426
|
const flags = flagsOf(order);
|
|
5110
6427
|
const fixture = state.resultFixtures.get(order.id);
|
|
5111
6428
|
const results = fixture?.results ?? biomarkerLines(order, flags);
|
|
5112
|
-
const
|
|
6429
|
+
const missing2 = fixture?.missing_results !== void 0 ? fixture.missing_results : flags.hasMissingResults ? [
|
|
5113
6430
|
{
|
|
5114
6431
|
name: (order.lab_test.markers ?? [])[0]?.name ?? "Unknown marker",
|
|
5115
6432
|
slug: (order.lab_test.markers ?? [])[0]?.slug ?? "unknown-marker",
|
|
@@ -5124,7 +6441,7 @@ var resultsHandlers = (state) => ({
|
|
|
5124
6441
|
return jsonRes(200, {
|
|
5125
6442
|
metadata: metadataOf(order, user?.client_user_id ?? order.user_id),
|
|
5126
6443
|
results,
|
|
5127
|
-
missing_results:
|
|
6444
|
+
missing_results: missing2,
|
|
5128
6445
|
sample_information: {
|
|
5129
6446
|
[order.sample_id ?? opaqueToken(`junction:sample:${order.id}`, 16)]: {
|
|
5130
6447
|
sample_id: order.sample_id ?? opaqueToken(`junction:sample:${order.id}`, 16),
|
|
@@ -5141,14 +6458,14 @@ var resultsHandlers = (state) => ({
|
|
|
5141
6458
|
},
|
|
5142
6459
|
get_result_metadata_v3_order__order_id__result_metadata_get: async (context) => {
|
|
5143
6460
|
const orderId = context.params.order_id ?? "";
|
|
5144
|
-
const order = requireOrder(state, orderId, context
|
|
6461
|
+
const order = requireOrder(state, orderId, context);
|
|
5145
6462
|
if (!resultsReady(order)) resultsNotAvailable(order.id);
|
|
5146
6463
|
const user = state.users.get(order.user_id);
|
|
5147
6464
|
return jsonRes(200, metadataOf(order, user?.client_user_id ?? order.user_id));
|
|
5148
6465
|
},
|
|
5149
6466
|
get_result_pdf_v3_order__order_id__result_pdf_get: async (context) => {
|
|
5150
6467
|
const orderId = context.params.order_id ?? "";
|
|
5151
|
-
const order = requireOrder(state, orderId, context
|
|
6468
|
+
const order = requireOrder(state, orderId, context);
|
|
5152
6469
|
if (!resultsReady(order)) resultsNotAvailable(order.id);
|
|
5153
6470
|
const fixturePdf = state.resultFixtures.get(order.id)?.pdf_base64;
|
|
5154
6471
|
const bytes = fixturePdf !== void 0 ? Uint8Array.from(atob(fixturePdf), (char) => char.charCodeAt(0)) : deterministicPdf(`Lab results ${order.id}`);
|
|
@@ -5159,7 +6476,7 @@ var resultsHandlers = (state) => ({
|
|
|
5159
6476
|
},
|
|
5160
6477
|
get_order_requisition_pdf_v3_order__order_id__requisition_pdf_get: async (context) => {
|
|
5161
6478
|
const orderId = context.params.order_id ?? "";
|
|
5162
|
-
const order = requireOrder(state, orderId, context
|
|
6479
|
+
const order = requireOrder(state, orderId, context);
|
|
5163
6480
|
const bytes = deterministicPdf(`Requisition ${order.id}`);
|
|
5164
6481
|
return new Response(bytes, {
|
|
5165
6482
|
status: 200,
|
|
@@ -5624,8 +6941,8 @@ var listUsers = (state, offset, limit) => {
|
|
|
5624
6941
|
};
|
|
5625
6942
|
};
|
|
5626
6943
|
var isValidUuid = (value) => /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/.test(value);
|
|
5627
|
-
function notFound3(detail) {
|
|
5628
|
-
|
|
6944
|
+
function notFound3(state, id, detail) {
|
|
6945
|
+
return missing(state, "user", id, detail);
|
|
5629
6946
|
}
|
|
5630
6947
|
var queryInt2 = (context, name, fallback) => {
|
|
5631
6948
|
const raw = context.query[name];
|
|
@@ -5951,7 +7268,7 @@ var userHandlers = (state) => ({
|
|
|
5951
7268
|
const body = jsonBody(context);
|
|
5952
7269
|
const errors = userInfoValidationErrors(body);
|
|
5953
7270
|
if (errors.length > 0) throw new HttpError(422, { detail: errors });
|
|
5954
|
-
if (!state.users.has(id)) notFound3("User not found");
|
|
7271
|
+
if (!state.users.has(id)) notFound3(state, id, "User not found");
|
|
5955
7272
|
const existing = state.userInfo.get(id) ?? {};
|
|
5956
7273
|
const rawAddress = typeof body.address === "object" && body.address !== null && !Array.isArray(body.address) ? body.address : void 0;
|
|
5957
7274
|
const info = {
|
|
@@ -5987,477 +7304,204 @@ var userHandlers = (state) => ({
|
|
|
5987
7304
|
]
|
|
5988
7305
|
});
|
|
5989
7306
|
}
|
|
5990
|
-
if (typeof clientUserId !== "string") {
|
|
5991
|
-
throw new HttpError(422, {
|
|
5992
|
-
detail: [
|
|
5993
|
-
{
|
|
5994
|
-
type: "string_type",
|
|
5995
|
-
loc: ["body", "client_user_id"],
|
|
5996
|
-
msg: "Input should be a valid string",
|
|
5997
|
-
input: clientUserId
|
|
5998
|
-
}
|
|
5999
|
-
]
|
|
6000
|
-
});
|
|
6001
|
-
}
|
|
6002
|
-
const existing = state.byClientId.get(clientUserId);
|
|
6003
|
-
if (existing) {
|
|
6004
|
-
const user2 = state.users.get(existing.user_id);
|
|
6005
|
-
throw new HttpError(400, {
|
|
6006
|
-
detail: {
|
|
6007
|
-
error_type: "INVALID_REQUEST",
|
|
6008
|
-
error_message: "Client user id already exists.",
|
|
6009
|
-
user_id: existing.user_id,
|
|
6010
|
-
created_on: user2?.created_on ?? state.isoNow(context.now)
|
|
6011
|
-
}
|
|
6012
|
-
});
|
|
6013
|
-
}
|
|
6014
|
-
const userId = state.nextUserId();
|
|
6015
|
-
const createdOn = state.isoNow(context.now);
|
|
6016
|
-
const tz = typeof body.fallback_time_zone === "string" ? { id: body.fallback_time_zone, source_slug: "manual", updated_at: createdOn } : null;
|
|
6017
|
-
const user = {
|
|
6018
|
-
user_id: userId,
|
|
6019
|
-
team_id: MOCK_TEAM_ID,
|
|
6020
|
-
client_user_id: clientUserId,
|
|
6021
|
-
created_on: createdOn,
|
|
6022
|
-
connected_sources: [],
|
|
6023
|
-
fallback_time_zone: tz,
|
|
6024
|
-
fallback_birth_date: null,
|
|
6025
|
-
ingestion_start: null,
|
|
6026
|
-
ingestion_end: null
|
|
6027
|
-
};
|
|
6028
|
-
state.users.insert(userId, user);
|
|
6029
|
-
state.byClientId.insert(clientUserId, { user_id: userId });
|
|
6030
|
-
return jsonRes(200, render(user));
|
|
6031
|
-
},
|
|
6032
|
-
get_user_v2_user__user_id__get: async (context) => {
|
|
6033
|
-
const id = context.params.user_id ?? "";
|
|
6034
|
-
if (!isValidUuid(id)) {
|
|
6035
|
-
throw new HttpError(422, {
|
|
6036
|
-
detail: `Invalid format for parameter user_id: error unmarshaling '${id}' text as *uuid.UUID: invalid UUID length: ${id.length}`
|
|
6037
|
-
});
|
|
6038
|
-
}
|
|
6039
|
-
const user = state.users.get(id);
|
|
6040
|
-
if (user) return jsonRes(200, render(user));
|
|
6041
|
-
if (state.deletedUsers.has(id)) {
|
|
6042
|
-
throw new HttpError(404, { detail: "You have scheduled this user for deletion." });
|
|
6043
|
-
}
|
|
6044
|
-
notFound3("Not found");
|
|
6045
|
-
},
|
|
6046
|
-
delete_user_v2_user__user_id__delete: async (context) => {
|
|
6047
|
-
const id = context.params.user_id ?? "";
|
|
6048
|
-
const user = state.users.get(id);
|
|
6049
|
-
if (!user) {
|
|
6050
|
-
if (state.deletedUsers.has(id)) {
|
|
6051
|
-
throw new HttpError(404, {
|
|
6052
|
-
detail: "The user has been scheduled for deletion as per your previous request"
|
|
6053
|
-
});
|
|
6054
|
-
}
|
|
6055
|
-
notFound3("The user does not or no longer exists in this team");
|
|
6056
|
-
}
|
|
6057
|
-
state.users.delete(id);
|
|
6058
|
-
state.byClientId.delete(user.client_user_id);
|
|
6059
|
-
state.deletedUsers.insert(id, { user_id: id });
|
|
6060
|
-
return jsonRes(200, { success: true });
|
|
6061
|
-
},
|
|
6062
|
-
get_user_by_client_user_id_v2_user_resolve__client_user_id__get: async (context) => {
|
|
6063
|
-
const clientUserId = context.params.client_user_id ?? "";
|
|
6064
|
-
const binding = state.byClientId.get(clientUserId);
|
|
6065
|
-
if (!binding) notFound3("User not found");
|
|
6066
|
-
const user = state.users.get(binding.user_id);
|
|
6067
|
-
if (!user) notFound3("User not found");
|
|
6068
|
-
return jsonRes(200, render(user));
|
|
6069
|
-
},
|
|
6070
|
-
patch_user_v2_user__user_id__patch: async (context) => {
|
|
6071
|
-
const id = context.params.user_id ?? "";
|
|
6072
|
-
const user = state.users.get(id);
|
|
6073
|
-
if (!user) {
|
|
6074
|
-
if (state.deletedUsers.has(id)) {
|
|
6075
|
-
throw new HttpError(404, {
|
|
6076
|
-
detail: "The user has been scheduled for deletion as per your previous request"
|
|
6077
|
-
});
|
|
6078
|
-
}
|
|
6079
|
-
notFound3("The user does not or no longer exists in this team");
|
|
6080
|
-
}
|
|
6081
|
-
const body = jsonBody(context);
|
|
6082
|
-
const now = state.isoNow(context.now);
|
|
6083
|
-
const patchable = [
|
|
6084
|
-
"client_user_id",
|
|
6085
|
-
"fallback_time_zone",
|
|
6086
|
-
"fallback_birth_date",
|
|
6087
|
-
"ingestion_start",
|
|
6088
|
-
"ingestion_end"
|
|
6089
|
-
];
|
|
6090
|
-
if (!patchable.some((key) => body[key] !== void 0)) {
|
|
6091
|
-
throw new HttpError(400, { detail: "Nothing to patch" });
|
|
6092
|
-
}
|
|
6093
|
-
const validationError = () => {
|
|
6094
|
-
const tz = body.fallback_time_zone;
|
|
6095
|
-
if (tz !== void 0 && tz !== null) {
|
|
6096
|
-
if (typeof tz !== "string") return stringError("fallback_time_zone", tz);
|
|
6097
|
-
if (!isValidIanaTimezone(tz)) {
|
|
6098
|
-
return {
|
|
6099
|
-
type: "value_error",
|
|
6100
|
-
loc: ["body", "fallback_time_zone"],
|
|
6101
|
-
msg: `Value error, Invalid IANA time zone: ${tz}`,
|
|
6102
|
-
input: tz,
|
|
6103
|
-
ctx: { error: {} }
|
|
6104
|
-
};
|
|
6105
|
-
}
|
|
6106
|
-
}
|
|
6107
|
-
const birth = body.fallback_birth_date;
|
|
6108
|
-
if (birth !== void 0 && birth !== null) {
|
|
6109
|
-
if (typeof birth !== "string") return stringError("fallback_birth_date", birth);
|
|
6110
|
-
if (!isValidDate(birth)) return dateError("fallback_birth_date", birth);
|
|
6111
|
-
}
|
|
6112
|
-
const start = body.ingestion_start;
|
|
6113
|
-
if (start !== void 0 && start !== null) {
|
|
6114
|
-
if (typeof start !== "string") return stringError("ingestion_start", start);
|
|
6115
|
-
if (!isValidDate(start)) return dateError("ingestion_start", start);
|
|
6116
|
-
}
|
|
6117
|
-
const end = body.ingestion_end;
|
|
6118
|
-
if (end !== void 0 && end !== null) {
|
|
6119
|
-
if (typeof end !== "string") return stringError("ingestion_end", end);
|
|
6120
|
-
if (!isValidDate(end)) return dateError("ingestion_end", end);
|
|
6121
|
-
}
|
|
6122
|
-
const cid = body.client_user_id;
|
|
6123
|
-
if (cid !== void 0) {
|
|
6124
|
-
if (cid === null) {
|
|
6125
|
-
return {
|
|
6126
|
-
type: "value_error",
|
|
6127
|
-
loc: ["body"],
|
|
6128
|
-
msg: "Value error, client_user_id is not a field that can be reset to null.",
|
|
6129
|
-
input: body,
|
|
6130
|
-
ctx: { error: {} }
|
|
6131
|
-
};
|
|
6132
|
-
}
|
|
6133
|
-
if (typeof cid !== "string") return stringError("client_user_id", cid);
|
|
6134
|
-
}
|
|
6135
|
-
return void 0;
|
|
6136
|
-
};
|
|
6137
|
-
const error = validationError();
|
|
6138
|
-
if (error) throw new HttpError(422, { detail: [error] });
|
|
6139
|
-
if (body.client_user_id !== void 0) {
|
|
6140
|
-
const raw = body.client_user_id;
|
|
6141
|
-
const taken = state.byClientId.get(raw);
|
|
6142
|
-
if (taken && taken.user_id !== id)
|
|
6143
|
-
throw new HttpError(409, { detail: "Client user id already exists" });
|
|
6144
|
-
state.byClientId.delete(user.client_user_id);
|
|
6145
|
-
state.byClientId.insert(raw, { user_id: id });
|
|
6146
|
-
user.client_user_id = raw;
|
|
6147
|
-
}
|
|
6148
|
-
if (body.fallback_time_zone !== void 0) {
|
|
6149
|
-
const raw = body.fallback_time_zone;
|
|
6150
|
-
user.fallback_time_zone = typeof raw === "string" ? { id: raw, source_slug: "manual", updated_at: now } : null;
|
|
6151
|
-
}
|
|
6152
|
-
if (body.fallback_birth_date !== void 0) {
|
|
6153
|
-
const raw = body.fallback_birth_date;
|
|
6154
|
-
user.fallback_birth_date = typeof raw === "string" ? { value: raw, source_slug: "manual", updated_at: now } : null;
|
|
6155
|
-
}
|
|
6156
|
-
const startProvided = body.ingestion_start !== void 0;
|
|
6157
|
-
const endProvided = body.ingestion_end !== void 0;
|
|
6158
|
-
if (startProvided) {
|
|
6159
|
-
const raw = body.ingestion_start;
|
|
6160
|
-
user.ingestion_start = typeof raw === "string" ? raw : null;
|
|
6161
|
-
}
|
|
6162
|
-
if (endProvided) {
|
|
6163
|
-
const rawEnd = body.ingestion_end;
|
|
6164
|
-
user.ingestion_end = typeof rawEnd === "string" ? rawEnd : null;
|
|
7307
|
+
if (typeof clientUserId !== "string") {
|
|
7308
|
+
throw new HttpError(422, {
|
|
7309
|
+
detail: [
|
|
7310
|
+
{
|
|
7311
|
+
type: "string_type",
|
|
7312
|
+
loc: ["body", "client_user_id"],
|
|
7313
|
+
msg: "Input should be a valid string",
|
|
7314
|
+
input: clientUserId
|
|
7315
|
+
}
|
|
7316
|
+
]
|
|
7317
|
+
});
|
|
6165
7318
|
}
|
|
6166
|
-
state.
|
|
6167
|
-
|
|
7319
|
+
const existing = state.byClientId.get(clientUserId);
|
|
7320
|
+
if (existing) {
|
|
7321
|
+
const user2 = state.users.get(existing.user_id);
|
|
7322
|
+
throw new HttpError(400, {
|
|
7323
|
+
detail: {
|
|
7324
|
+
error_type: "INVALID_REQUEST",
|
|
7325
|
+
error_message: "Client user id already exists.",
|
|
7326
|
+
user_id: existing.user_id,
|
|
7327
|
+
created_on: user2?.created_on ?? state.isoNow(context.now)
|
|
7328
|
+
}
|
|
7329
|
+
});
|
|
7330
|
+
}
|
|
7331
|
+
checkUserQuota(state.limits, state.users.count());
|
|
7332
|
+
const userId = state.nextUserId();
|
|
7333
|
+
const createdOn = state.isoNow(context.now);
|
|
7334
|
+
const tz = typeof body.fallback_time_zone === "string" ? { id: body.fallback_time_zone, source_slug: "manual", updated_at: createdOn } : null;
|
|
7335
|
+
const user = {
|
|
7336
|
+
user_id: userId,
|
|
7337
|
+
team_id: state.teamId,
|
|
7338
|
+
client_user_id: clientUserId,
|
|
7339
|
+
created_on: createdOn,
|
|
7340
|
+
connected_sources: [],
|
|
7341
|
+
fallback_time_zone: tz,
|
|
7342
|
+
fallback_birth_date: null,
|
|
7343
|
+
ingestion_start: null,
|
|
7344
|
+
ingestion_end: null
|
|
7345
|
+
};
|
|
7346
|
+
state.users.insert(userId, user);
|
|
7347
|
+
state.byClientId.insert(clientUserId, { user_id: userId });
|
|
7348
|
+
return jsonRes(200, render(user));
|
|
6168
7349
|
},
|
|
6169
|
-
|
|
7350
|
+
get_user_v2_user__user_id__get: async (context) => {
|
|
6170
7351
|
const id = context.params.user_id ?? "";
|
|
6171
7352
|
if (!isValidUuid(id)) {
|
|
6172
7353
|
throw new HttpError(422, {
|
|
6173
7354
|
detail: `Invalid format for parameter user_id: error unmarshaling '${id}' text as *uuid.UUID: invalid UUID length: ${id.length}`
|
|
6174
7355
|
});
|
|
6175
7356
|
}
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
if (
|
|
6179
|
-
|
|
6180
|
-
},
|
|
6181
|
-
get_teams_users_v2_user_get: async (context) => {
|
|
6182
|
-
const offset = queryInt2(context, "offset", 0);
|
|
6183
|
-
const limit = queryInt2(context, "limit", 100);
|
|
6184
|
-
if (offset < 0 || limit < 0 || limit > 500) {
|
|
6185
|
-
throw new HttpError(500, "Internal Server Error", { "content-type": "text/plain" });
|
|
7357
|
+
const user = state.users.get(id);
|
|
7358
|
+
if (user) return jsonRes(200, render(user));
|
|
7359
|
+
if (state.deletedUsers.has(id)) {
|
|
7360
|
+
throw new HttpError(404, { detail: "You have scheduled this user for deletion." });
|
|
6186
7361
|
}
|
|
6187
|
-
|
|
6188
|
-
}
|
|
6189
|
-
});
|
|
6190
|
-
|
|
6191
|
-
// src/admin.ts
|
|
6192
|
-
var ORDER_STATUSES_BY_METHOD = {
|
|
6193
|
-
at_home_phlebotomy: [
|
|
6194
|
-
"received.ordered",
|
|
6195
|
-
"received.requisition_created",
|
|
6196
|
-
"received.requisition_bypassed",
|
|
6197
|
-
"collecting_sample.appointment_pending",
|
|
6198
|
-
"collecting_sample.appointment_scheduled",
|
|
6199
|
-
"collecting_sample.appointment_cancelled",
|
|
6200
|
-
"collecting_sample.draw_completed",
|
|
6201
|
-
"sample_with_lab.partial_results",
|
|
6202
|
-
"completed.completed",
|
|
6203
|
-
"completed.corrected",
|
|
6204
|
-
"cancelled.cancelled",
|
|
6205
|
-
"failed.sample_error"
|
|
6206
|
-
],
|
|
6207
|
-
on_site_collection: [
|
|
6208
|
-
"received.ordered",
|
|
6209
|
-
"received.requisition_created",
|
|
6210
|
-
"received.requisition_bypassed",
|
|
6211
|
-
"sample_with_lab.draw_completed",
|
|
6212
|
-
"sample_with_lab.partial_results",
|
|
6213
|
-
"completed.completed",
|
|
6214
|
-
"completed.corrected",
|
|
6215
|
-
"cancelled.cancelled",
|
|
6216
|
-
"failed.sample_error"
|
|
6217
|
-
],
|
|
6218
|
-
testkit: [
|
|
6219
|
-
"received.ordered",
|
|
6220
|
-
"received.awaiting_registration",
|
|
6221
|
-
"received.registered",
|
|
6222
|
-
"received.requisition_created",
|
|
6223
|
-
"received.requisition_bypassed",
|
|
6224
|
-
"collecting_sample.transit_customer",
|
|
6225
|
-
"collecting_sample.out_for_delivery",
|
|
6226
|
-
"collecting_sample.with_customer",
|
|
6227
|
-
"collecting_sample.transit_lab",
|
|
6228
|
-
"collecting_sample.problem_in_transit_customer",
|
|
6229
|
-
"collecting_sample.problem_in_transit_lab",
|
|
6230
|
-
"sample_with_lab.delivered_to_lab",
|
|
6231
|
-
"sample_with_lab.lab_processing_blocked",
|
|
6232
|
-
"completed.completed",
|
|
6233
|
-
"completed.corrected",
|
|
6234
|
-
"cancelled.cancelled",
|
|
6235
|
-
"cancelled.do_not_process",
|
|
6236
|
-
"failed.sample_error",
|
|
6237
|
-
"failed.failure_to_deliver_to_customer",
|
|
6238
|
-
"failed.failure_to_deliver_to_lab",
|
|
6239
|
-
"failed.lost"
|
|
6240
|
-
],
|
|
6241
|
-
walk_in_test: [
|
|
6242
|
-
"received.ordered",
|
|
6243
|
-
"received.requisition_created",
|
|
6244
|
-
"received.requisition_bypassed",
|
|
6245
|
-
"collecting_sample.appointment_pending",
|
|
6246
|
-
"collecting_sample.appointment_scheduled",
|
|
6247
|
-
"collecting_sample.appointment_cancelled",
|
|
6248
|
-
"collecting_sample.redraw_available",
|
|
6249
|
-
"sample_with_lab.partial_results",
|
|
6250
|
-
"completed.completed",
|
|
6251
|
-
"completed.corrected",
|
|
6252
|
-
"cancelled.cancelled",
|
|
6253
|
-
"failed.sample_error"
|
|
6254
|
-
]
|
|
6255
|
-
};
|
|
6256
|
-
var TRANSITION_ALIASES = {
|
|
6257
|
-
at_home_phlebotomy: {
|
|
6258
|
-
requisition_created: "received.requisition_created",
|
|
6259
|
-
scheduled: "collecting_sample.appointment_scheduled",
|
|
6260
|
-
collected: "collecting_sample.draw_completed",
|
|
6261
|
-
partial_results: "sample_with_lab.partial_results",
|
|
6262
|
-
completed: "completed.completed",
|
|
6263
|
-
corrected: "completed.corrected",
|
|
6264
|
-
cancelled: "cancelled.cancelled",
|
|
6265
|
-
failed: "failed.sample_error"
|
|
6266
|
-
},
|
|
6267
|
-
on_site_collection: {
|
|
6268
|
-
requisition_created: "received.requisition_created",
|
|
6269
|
-
collected: "sample_with_lab.draw_completed",
|
|
6270
|
-
at_lab: "sample_with_lab.draw_completed",
|
|
6271
|
-
partial_results: "sample_with_lab.partial_results",
|
|
6272
|
-
completed: "completed.completed",
|
|
6273
|
-
corrected: "completed.corrected",
|
|
6274
|
-
cancelled: "cancelled.cancelled",
|
|
6275
|
-
failed: "failed.sample_error"
|
|
6276
|
-
},
|
|
6277
|
-
testkit: {
|
|
6278
|
-
requisition_created: "received.requisition_created",
|
|
6279
|
-
shipped: "collecting_sample.transit_customer",
|
|
6280
|
-
delivered: "collecting_sample.with_customer",
|
|
6281
|
-
collected: "collecting_sample.transit_lab",
|
|
6282
|
-
at_lab: "sample_with_lab.delivered_to_lab",
|
|
6283
|
-
completed: "completed.completed",
|
|
6284
|
-
corrected: "completed.corrected",
|
|
6285
|
-
cancelled: "cancelled.cancelled",
|
|
6286
|
-
failed: "failed.sample_error"
|
|
7362
|
+
notFound3(state, id, "Not found");
|
|
6287
7363
|
},
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
|
|
6296
|
-
}
|
|
6297
|
-
};
|
|
6298
|
-
var RESULT_FIXTURES = {
|
|
6299
|
-
normal: { interpretation: "normal" },
|
|
6300
|
-
abnormal: { interpretation: "abnormal" },
|
|
6301
|
-
critical: { interpretation: "critical" },
|
|
6302
|
-
missing_results: { interpretation: "normal", has_missing_results: true }
|
|
6303
|
-
};
|
|
6304
|
-
var FAULT_PRESETS = {
|
|
6305
|
-
sandbox_user_quota: {
|
|
6306
|
-
operationId: "create_user_v2_user_post",
|
|
6307
|
-
status: 400,
|
|
6308
|
-
body: {
|
|
6309
|
-
detail: {
|
|
6310
|
-
error_type: "INVALID_REQUEST",
|
|
6311
|
-
error_message: "You have reached the maximum of 50 Sandbox users"
|
|
7364
|
+
delete_user_v2_user__user_id__delete: async (context) => {
|
|
7365
|
+
const id = context.params.user_id ?? "";
|
|
7366
|
+
const user = state.users.get(id);
|
|
7367
|
+
if (!user) {
|
|
7368
|
+
if (state.deletedUsers.has(id)) {
|
|
7369
|
+
throw new HttpError(404, {
|
|
7370
|
+
detail: "The user has been scheduled for deletion as per your previous request"
|
|
7371
|
+
});
|
|
6312
7372
|
}
|
|
7373
|
+
notFound3(state, id, "The user does not or no longer exists in this team");
|
|
6313
7374
|
}
|
|
7375
|
+
state.users.delete(id);
|
|
7376
|
+
state.byClientId.delete(user.client_user_id);
|
|
7377
|
+
state.deletedUsers.insert(id, { user_id: id });
|
|
7378
|
+
return jsonRes(200, { success: true });
|
|
6314
7379
|
},
|
|
6315
|
-
|
|
6316
|
-
|
|
6317
|
-
|
|
6318
|
-
|
|
7380
|
+
get_user_by_client_user_id_v2_user_resolve__client_user_id__get: async (context) => {
|
|
7381
|
+
const clientUserId = context.params.client_user_id ?? "";
|
|
7382
|
+
const binding = state.byClientId.get(clientUserId) ?? notFound3(state, `client:${clientUserId}`, "User not found");
|
|
7383
|
+
const user = state.users.get(binding.user_id) ?? notFound3(state, binding.user_id, "User not found");
|
|
7384
|
+
return jsonRes(200, render(user));
|
|
6319
7385
|
},
|
|
6320
|
-
|
|
6321
|
-
|
|
6322
|
-
|
|
6323
|
-
|
|
6324
|
-
|
|
6325
|
-
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
const alias = TRANSITION_ALIASES[method]?.[target];
|
|
6329
|
-
const short = alias ?? (target.split(".").length === 3 ? null : target);
|
|
6330
|
-
if (short !== null && allowed.includes(short)) {
|
|
6331
|
-
const [phase, event] = short.split(".");
|
|
6332
|
-
return { status: `${phase}.${method}.${event}` };
|
|
6333
|
-
}
|
|
6334
|
-
if (target.split(".").length === 3) {
|
|
6335
|
-
const [phase, targetMethod, event] = target.split(".");
|
|
6336
|
-
if (targetMethod === method && allowed.includes(`${phase}.${event}`)) return { status: target };
|
|
6337
|
-
}
|
|
6338
|
-
const names = Object.keys(TRANSITION_ALIASES[method] ?? {});
|
|
6339
|
-
return {
|
|
6340
|
-
error: `${JSON.stringify(target)} is not a ${method} status. Use one of: ${names.join(", ")}, or a full status such as ${allowed.slice(0, 2).map((s) => `${s.split(".")[0]}.${method}.${s.split(".")[1]}`).join(", ")}`
|
|
6341
|
-
};
|
|
6342
|
-
};
|
|
6343
|
-
var adminError2 = (status, message) => jsonRes(status, { error: { type: "mockingbird_admin", message } });
|
|
6344
|
-
var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6345
|
-
var junctionAdminRoutes = (options) => (runtime) => {
|
|
6346
|
-
const api = (request2) => runtime.instance(request2.namespace);
|
|
6347
|
-
return {
|
|
6348
|
-
"GET /orders": (request2) => jsonRes(200, {
|
|
6349
|
-
orders: api(request2).orders().map((order) => ({
|
|
6350
|
-
id: order.id,
|
|
6351
|
-
user_id: order.user_id,
|
|
6352
|
-
method: methodOf(order),
|
|
6353
|
-
status: order.last_event.status,
|
|
6354
|
-
updated_at: order.updated_at
|
|
6355
|
-
}))
|
|
6356
|
-
}),
|
|
6357
|
-
"POST /orders/:id/transition": (request2) => {
|
|
6358
|
-
const body = isRecord4(request2.body) ? request2.body : {};
|
|
6359
|
-
const order = api(request2).order(request2.params.id);
|
|
6360
|
-
if (!order) return adminError2(404, `no order ${request2.params.id} in ${request2.namespace}`);
|
|
6361
|
-
if (typeof body.to !== "string") {
|
|
6362
|
-
return adminError2(400, 'body needs "to": a status like "completed" or "at_lab"');
|
|
7386
|
+
patch_user_v2_user__user_id__patch: async (context) => {
|
|
7387
|
+
const id = context.params.user_id ?? "";
|
|
7388
|
+
const user = state.users.get(id);
|
|
7389
|
+
if (!user) {
|
|
7390
|
+
if (state.deletedUsers.has(id)) {
|
|
7391
|
+
throw new HttpError(404, {
|
|
7392
|
+
detail: "The user has been scheduled for deletion as per your previous request"
|
|
7393
|
+
});
|
|
6363
7394
|
}
|
|
6364
|
-
|
|
6365
|
-
|
|
6366
|
-
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6370
|
-
|
|
6371
|
-
|
|
6372
|
-
|
|
6373
|
-
|
|
6374
|
-
|
|
7395
|
+
notFound3(state, id, "The user does not or no longer exists in this team");
|
|
7396
|
+
}
|
|
7397
|
+
const body = jsonBody(context);
|
|
7398
|
+
const now = state.isoNow(context.now);
|
|
7399
|
+
const patchable = [
|
|
7400
|
+
"client_user_id",
|
|
7401
|
+
"fallback_time_zone",
|
|
7402
|
+
"fallback_birth_date",
|
|
7403
|
+
"ingestion_start",
|
|
7404
|
+
"ingestion_end"
|
|
7405
|
+
];
|
|
7406
|
+
if (!patchable.some((key) => body[key] !== void 0)) {
|
|
7407
|
+
throw new HttpError(400, { detail: "Nothing to patch" });
|
|
7408
|
+
}
|
|
7409
|
+
const validationError = () => {
|
|
7410
|
+
const tz = body.fallback_time_zone;
|
|
7411
|
+
if (tz !== void 0 && tz !== null) {
|
|
7412
|
+
if (typeof tz !== "string") return stringError("fallback_time_zone", tz);
|
|
7413
|
+
if (!isValidIanaTimezone(tz)) {
|
|
7414
|
+
return {
|
|
7415
|
+
type: "value_error",
|
|
7416
|
+
loc: ["body", "fallback_time_zone"],
|
|
7417
|
+
msg: `Value error, Invalid IANA time zone: ${tz}`,
|
|
7418
|
+
input: tz,
|
|
7419
|
+
ctx: { error: {} }
|
|
7420
|
+
};
|
|
6375
7421
|
}
|
|
6376
|
-
flags = { ...named, ...flags };
|
|
6377
|
-
}
|
|
6378
|
-
const updated = api(request2).transitionOrder(order.id, resolved.status, {
|
|
6379
|
-
now: runtime.clock.now,
|
|
6380
|
-
flags
|
|
6381
|
-
});
|
|
6382
|
-
return jsonRes(200, {
|
|
6383
|
-
id: order.id,
|
|
6384
|
-
status: updated?.last_event.status,
|
|
6385
|
-
events: updated?.events
|
|
6386
|
-
});
|
|
6387
|
-
},
|
|
6388
|
-
"GET /result-fixtures": () => jsonRes(200, { fixtures: RESULT_FIXTURES }),
|
|
6389
|
-
"GET /results/:id": (request2) => {
|
|
6390
|
-
const fixture = api(request2).resultFixture(request2.params.id);
|
|
6391
|
-
return fixture ? jsonRes(200, fixture) : adminError2(404, "no result fixture for that order");
|
|
6392
|
-
},
|
|
6393
|
-
"PUT /results/:id": (request2) => {
|
|
6394
|
-
if (!isRecord4(request2.body)) return adminError2(400, "expected a result fixture object");
|
|
6395
|
-
const fixture = { name: "custom", ...request2.body };
|
|
6396
|
-
if (fixture.results !== void 0 && !Array.isArray(fixture.results)) {
|
|
6397
|
-
return adminError2(400, "results must be an array of result lines");
|
|
6398
7422
|
}
|
|
6399
|
-
|
|
6400
|
-
|
|
6401
|
-
|
|
6402
|
-
|
|
6403
|
-
const body = isRecord4(request2.body) ? request2.body : {};
|
|
6404
|
-
if (body.accounts !== null && !Array.isArray(body.accounts)) {
|
|
6405
|
-
return adminError2(400, 'body needs "accounts": an array, or null to restore the default');
|
|
7423
|
+
const birth = body.fallback_birth_date;
|
|
7424
|
+
if (birth !== void 0 && birth !== null) {
|
|
7425
|
+
if (typeof birth !== "string") return stringError("fallback_birth_date", birth);
|
|
7426
|
+
if (!isValidDate(birth)) return dateError("fallback_birth_date", birth);
|
|
6406
7427
|
}
|
|
6407
|
-
|
|
6408
|
-
|
|
6409
|
-
|
|
6410
|
-
|
|
6411
|
-
return adminError2(400, error instanceof Error ? error.message : String(error));
|
|
7428
|
+
const start = body.ingestion_start;
|
|
7429
|
+
if (start !== void 0 && start !== null) {
|
|
7430
|
+
if (typeof start !== "string") return stringError("ingestion_start", start);
|
|
7431
|
+
if (!isValidDate(start)) return dateError("ingestion_start", start);
|
|
6412
7432
|
}
|
|
6413
|
-
|
|
6414
|
-
|
|
6415
|
-
|
|
6416
|
-
|
|
6417
|
-
}),
|
|
6418
|
-
"PUT /geo": (request2) => {
|
|
6419
|
-
const mode = isRecord4(request2.body) ? request2.body.mode : void 0;
|
|
6420
|
-
if (mode !== "corpus" && mode !== "synthetic") {
|
|
6421
|
-
return adminError2(400, 'body needs "mode": "corpus" or "synthetic"');
|
|
7433
|
+
const end = body.ingestion_end;
|
|
7434
|
+
if (end !== void 0 && end !== null) {
|
|
7435
|
+
if (typeof end !== "string") return stringError("ingestion_end", end);
|
|
7436
|
+
if (!isValidDate(end)) return dateError("ingestion_end", end);
|
|
6422
7437
|
}
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
|
|
6429
|
-
|
|
6430
|
-
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6435
|
-
},
|
|
6436
|
-
"POST /webhooks/flush": async () => {
|
|
6437
|
-
if (!options.webhooks) return adminError2(409, "webhook delivery is off");
|
|
6438
|
-
await options.webhooks.flush();
|
|
6439
|
-
return jsonRes(200, { status: "ok" });
|
|
6440
|
-
},
|
|
6441
|
-
"GET /faults/presets": () => jsonRes(200, { presets: FAULT_PRESETS }),
|
|
6442
|
-
"POST /faults/presets/:name": (request2) => {
|
|
6443
|
-
const preset = FAULT_PRESETS[request2.params.name];
|
|
6444
|
-
if (!preset) {
|
|
6445
|
-
return adminError2(
|
|
6446
|
-
404,
|
|
6447
|
-
`no preset ${request2.params.name}; one of ${Object.keys(FAULT_PRESETS).join(", ")}`
|
|
6448
|
-
);
|
|
7438
|
+
const cid = body.client_user_id;
|
|
7439
|
+
if (cid !== void 0) {
|
|
7440
|
+
if (cid === null) {
|
|
7441
|
+
return {
|
|
7442
|
+
type: "value_error",
|
|
7443
|
+
loc: ["body"],
|
|
7444
|
+
msg: "Value error, client_user_id is not a field that can be reset to null.",
|
|
7445
|
+
input: body,
|
|
7446
|
+
ctx: { error: {} }
|
|
7447
|
+
};
|
|
7448
|
+
}
|
|
7449
|
+
if (typeof cid !== "string") return stringError("client_user_id", cid);
|
|
6449
7450
|
}
|
|
6450
|
-
|
|
6451
|
-
|
|
6452
|
-
|
|
6453
|
-
|
|
6454
|
-
|
|
6455
|
-
|
|
7451
|
+
return void 0;
|
|
7452
|
+
};
|
|
7453
|
+
const error = validationError();
|
|
7454
|
+
if (error) throw new HttpError(422, { detail: [error] });
|
|
7455
|
+
if (body.client_user_id !== void 0) {
|
|
7456
|
+
const raw = body.client_user_id;
|
|
7457
|
+
const taken = state.byClientId.get(raw);
|
|
7458
|
+
if (taken && taken.user_id !== id)
|
|
7459
|
+
throw new HttpError(409, { detail: "Client user id already exists" });
|
|
7460
|
+
state.byClientId.delete(user.client_user_id);
|
|
7461
|
+
state.byClientId.insert(raw, { user_id: id });
|
|
7462
|
+
user.client_user_id = raw;
|
|
7463
|
+
}
|
|
7464
|
+
if (body.fallback_time_zone !== void 0) {
|
|
7465
|
+
const raw = body.fallback_time_zone;
|
|
7466
|
+
user.fallback_time_zone = typeof raw === "string" ? { id: raw, source_slug: "manual", updated_at: now } : null;
|
|
7467
|
+
}
|
|
7468
|
+
if (body.fallback_birth_date !== void 0) {
|
|
7469
|
+
const raw = body.fallback_birth_date;
|
|
7470
|
+
user.fallback_birth_date = typeof raw === "string" ? { value: raw, source_slug: "manual", updated_at: now } : null;
|
|
7471
|
+
}
|
|
7472
|
+
const startProvided = body.ingestion_start !== void 0;
|
|
7473
|
+
const endProvided = body.ingestion_end !== void 0;
|
|
7474
|
+
if (startProvided) {
|
|
7475
|
+
const raw = body.ingestion_start;
|
|
7476
|
+
user.ingestion_start = typeof raw === "string" ? raw : null;
|
|
7477
|
+
}
|
|
7478
|
+
if (endProvided) {
|
|
7479
|
+
const rawEnd = body.ingestion_end;
|
|
7480
|
+
user.ingestion_end = typeof rawEnd === "string" ? rawEnd : null;
|
|
7481
|
+
}
|
|
7482
|
+
state.users.update(id, user);
|
|
7483
|
+
return new Response(null, { status: 204 });
|
|
7484
|
+
},
|
|
7485
|
+
get_latest_user_info_user_v2_user__user_id__info_latest_get: async (context) => {
|
|
7486
|
+
const id = context.params.user_id ?? "";
|
|
7487
|
+
if (!isValidUuid(id)) {
|
|
7488
|
+
throw new HttpError(422, {
|
|
7489
|
+
detail: `Invalid format for parameter user_id: error unmarshaling '${id}' text as *uuid.UUID: invalid UUID length: ${id.length}`
|
|
6456
7490
|
});
|
|
6457
|
-
return jsonRes(201, rule);
|
|
6458
7491
|
}
|
|
6459
|
-
|
|
6460
|
-
|
|
7492
|
+
if (!state.users.has(id) && !state.deletedUsers.has(id)) notFound3(state, id, "User not found");
|
|
7493
|
+
const info = state.userInfo.get(id) ?? notFound3(state, id, "User demographics data not found");
|
|
7494
|
+
return jsonRes(200, info);
|
|
7495
|
+
},
|
|
7496
|
+
get_teams_users_v2_user_get: async (context) => {
|
|
7497
|
+
const offset = queryInt2(context, "offset", 0);
|
|
7498
|
+
const limit = queryInt2(context, "limit", 100);
|
|
7499
|
+
if (offset < 0 || limit < 0 || limit > 500) {
|
|
7500
|
+
throw new HttpError(500, "Internal Server Error", { "content-type": "text/plain" });
|
|
7501
|
+
}
|
|
7502
|
+
return jsonRes(200, listUsers(state, offset, limit));
|
|
7503
|
+
}
|
|
7504
|
+
});
|
|
6461
7505
|
|
|
6462
7506
|
// src/coverage-corpus.ts
|
|
6463
7507
|
var PSC_AVAILABILITY_ZIPS = [
|
|
@@ -6899,8 +7943,6 @@ var recordSandboxCorpus = async (args) => {
|
|
|
6899
7943
|
|
|
6900
7944
|
// src/corpus-tools.ts
|
|
6901
7945
|
var DEFAULT_JUNCTION_BASE_URL = "https://api.sandbox.tryvital.io";
|
|
6902
|
-
var SANDBOX_KEY_PREFIXES = ["sk_us_", "sk_eu_"];
|
|
6903
|
-
var isSandboxKey = (key) => SANDBOX_KEY_PREFIXES.some((prefix) => key.startsWith(prefix));
|
|
6904
7946
|
var asRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
6905
7947
|
var listOf = (body) => {
|
|
6906
7948
|
const items = Array.isArray(body) ? body : asRecord2(body)?.data;
|
|
@@ -6917,11 +7959,32 @@ var fingerprintCorpus = async (corpus) => {
|
|
|
6917
7959
|
const content = canonicalJson({
|
|
6918
7960
|
observations: corpus.observations,
|
|
6919
7961
|
catalog: corpus.catalog,
|
|
6920
|
-
labAccounts: corpus.labAccounts
|
|
7962
|
+
labAccounts: corpus.labAccounts,
|
|
7963
|
+
// Absent from version-1 corpora, whose fingerprints stay what they were.
|
|
7964
|
+
...corpus.teamId !== void 0 ? { teamId: corpus.teamId } : {}
|
|
6921
7965
|
});
|
|
6922
7966
|
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(content));
|
|
6923
7967
|
return [...new Uint8Array(digest)].map((byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
6924
7968
|
};
|
|
7969
|
+
var UUID2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
7970
|
+
var readTeamId = async (real, labAccounts) => {
|
|
7971
|
+
const headers = { ...await real.headers(), accept: "application/json" };
|
|
7972
|
+
const response = await real.fetch(new Request(`${real.baseUrl}/v2/user?offset=0&limit=1`, { headers })).catch(() => void 0);
|
|
7973
|
+
if (response?.ok) {
|
|
7974
|
+
const body = asRecord2(await response.json().catch(() => void 0));
|
|
7975
|
+
const first = Array.isArray(body?.users) ? asRecord2(body.users[0]) : void 0;
|
|
7976
|
+
if (typeof first?.team_id === "string" && UUID2.test(first.team_id)) return first.team_id;
|
|
7977
|
+
}
|
|
7978
|
+
const named = /* @__PURE__ */ new Set();
|
|
7979
|
+
for (const account2 of labAccounts) {
|
|
7980
|
+
const allowlist = account2.team_id_allowlist;
|
|
7981
|
+
if (!Array.isArray(allowlist) || allowlist.length === 0) continue;
|
|
7982
|
+
const ids = allowlist.filter((id) => typeof id === "string");
|
|
7983
|
+
if (named.size === 0) for (const id of ids) named.add(id);
|
|
7984
|
+
else for (const id of [...named]) if (!ids.includes(id)) named.delete(id);
|
|
7985
|
+
}
|
|
7986
|
+
return named.size === 1 ? [...named][0] : void 0;
|
|
7987
|
+
};
|
|
6925
7988
|
var pullCorpus = async (options) => {
|
|
6926
7989
|
const baseUrl = (options.baseUrl ?? DEFAULT_JUNCTION_BASE_URL).replace(/\/$/, "");
|
|
6927
7990
|
const send = options.fetch ?? ((request2) => fetch(request2));
|
|
@@ -6973,10 +8036,12 @@ var pullCorpus = async (options) => {
|
|
|
6973
8036
|
([a], [b]) => a < b ? -1 : a > b ? 1 : 0
|
|
6974
8037
|
)
|
|
6975
8038
|
);
|
|
8039
|
+
const teamId = await readTeamId(real, labAccounts) ?? options.base?.teamId;
|
|
6976
8040
|
const corpus = {
|
|
6977
8041
|
version: SEALED_CORPUS_VERSION,
|
|
6978
8042
|
recordedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
6979
8043
|
source: baseUrl,
|
|
8044
|
+
...teamId !== void 0 ? { teamId } : {},
|
|
6980
8045
|
observations,
|
|
6981
8046
|
catalog,
|
|
6982
8047
|
labAccounts
|
|
@@ -7022,8 +8087,10 @@ var diffCorpus = (before, after) => {
|
|
|
7022
8087
|
removed: [...beforeZips].filter((zip) => !afterZips.has(zip)).sort()
|
|
7023
8088
|
};
|
|
7024
8089
|
const empty = (d) => d.added.length + d.removed.length + d.changed.length === 0;
|
|
8090
|
+
const teamChanged = (before.teamId ?? null) !== (after.teamId ?? null);
|
|
7025
8091
|
return {
|
|
7026
|
-
identical: empty(observations) && empty(labTests) && empty(labAccounts),
|
|
8092
|
+
identical: empty(observations) && empty(labTests) && empty(labAccounts) && !teamChanged,
|
|
8093
|
+
...teamChanged ? { teamId: { before: before.teamId ?? null, after: after.teamId ?? null } } : {},
|
|
7027
8094
|
observations,
|
|
7028
8095
|
labTests,
|
|
7029
8096
|
labAccounts,
|
|
@@ -7214,6 +8281,7 @@ var createRuntime2 = (options = {}) => {
|
|
|
7214
8281
|
...options.seed !== void 0 ? { seed: options.seed } : {},
|
|
7215
8282
|
...options.adminKey !== void 0 ? { adminKey: options.adminKey } : {},
|
|
7216
8283
|
...options.onLog ? { onLog: options.onLog } : {},
|
|
8284
|
+
...options.journalSize !== void 0 ? { journalSize: options.journalSize } : {},
|
|
7217
8285
|
create: ({ namespace, publicNamespace, sqlite, clock, rng }) => {
|
|
7218
8286
|
const api = new JunctionAPI({
|
|
7219
8287
|
sqlite,
|
|
@@ -7223,6 +8291,10 @@ var createRuntime2 = (options = {}) => {
|
|
|
7223
8291
|
...options.corpus ? { corpus: options.corpus } : {},
|
|
7224
8292
|
...options.geo ? { geo: options.geo } : {},
|
|
7225
8293
|
...options.labAccounts ? { labAccounts: options.labAccounts } : {},
|
|
8294
|
+
...options.teamId !== void 0 ? { teamId: options.teamId } : {},
|
|
8295
|
+
...options.limits ? { limits: options.limits } : {},
|
|
8296
|
+
...options.identity ? { identity: options.identity } : {},
|
|
8297
|
+
...options.fixtures ? { fixtures: options.fixtures } : {},
|
|
7226
8298
|
onWebhook: (event) => {
|
|
7227
8299
|
options.onWebhook?.(event, publicNamespace);
|
|
7228
8300
|
webhooks?.publish(event, publicNamespace);
|
|
@@ -7240,6 +8312,7 @@ var createRuntime2 = (options = {}) => {
|
|
|
7240
8312
|
return {
|
|
7241
8313
|
corpus: info?.label ?? null,
|
|
7242
8314
|
geo: runtime.instance().geoMode,
|
|
8315
|
+
teamId: runtime.instance().teamId,
|
|
7243
8316
|
webhooks: webhooks ? "on" : "off"
|
|
7244
8317
|
};
|
|
7245
8318
|
},
|
|
@@ -7284,6 +8357,95 @@ var firstDifference = (a, b, path = "$") => {
|
|
|
7284
8357
|
}
|
|
7285
8358
|
return path;
|
|
7286
8359
|
};
|
|
8360
|
+
var orderScopedCalls = (orderId) => {
|
|
8361
|
+
const base = `/v3/order/${orderId}`;
|
|
8362
|
+
const key = { booking_key: "00000000-0000-4000-8000-00000000b00c" };
|
|
8363
|
+
const reason = { cancellation_reason_id: "00000000-0000-4000-8000-00000000c0de" };
|
|
8364
|
+
return {
|
|
8365
|
+
get_order_v3_order__order_id__get: { method: "GET", path: base },
|
|
8366
|
+
cancel_order_v3_order__order_id__cancel_post: { method: "POST", path: `${base}/cancel` },
|
|
8367
|
+
simulate_order_v3_order__order_id__test_post: {
|
|
8368
|
+
method: "POST",
|
|
8369
|
+
path: `${base}/test?final_status=completed.at_home_phlebotomy.completed`,
|
|
8370
|
+
body: {}
|
|
8371
|
+
},
|
|
8372
|
+
get_order_requisition_pdf_v3_order__order_id__requisition_pdf_get: {
|
|
8373
|
+
method: "GET",
|
|
8374
|
+
path: `${base}/requisition/pdf`
|
|
8375
|
+
},
|
|
8376
|
+
get_result_raw_v3_order__order_id__result_get: { method: "GET", path: `${base}/result` },
|
|
8377
|
+
get_result_metadata_v3_order__order_id__result_metadata_get: {
|
|
8378
|
+
method: "GET",
|
|
8379
|
+
path: `${base}/result/metadata`
|
|
8380
|
+
},
|
|
8381
|
+
get_result_pdf_v3_order__order_id__result_pdf_get: {
|
|
8382
|
+
method: "GET",
|
|
8383
|
+
path: `${base}/result/pdf`
|
|
8384
|
+
},
|
|
8385
|
+
get_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_get: {
|
|
8386
|
+
method: "GET",
|
|
8387
|
+
path: `${base}/phlebotomy/appointment`
|
|
8388
|
+
},
|
|
8389
|
+
book_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_book_post: {
|
|
8390
|
+
method: "POST",
|
|
8391
|
+
path: `${base}/phlebotomy/appointment/book`,
|
|
8392
|
+
body: key
|
|
8393
|
+
},
|
|
8394
|
+
reschedule_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_reschedule_patch: {
|
|
8395
|
+
method: "PATCH",
|
|
8396
|
+
path: `${base}/phlebotomy/appointment/reschedule`,
|
|
8397
|
+
body: key
|
|
8398
|
+
},
|
|
8399
|
+
cancel_phlebotomy_appointment_v3_order__order_id__phlebotomy_appointment_cancel_patch: {
|
|
8400
|
+
method: "PATCH",
|
|
8401
|
+
path: `${base}/phlebotomy/appointment/cancel`,
|
|
8402
|
+
body: reason
|
|
8403
|
+
},
|
|
8404
|
+
get_psc_appointment_v3_order__order_id__psc_appointment_get: {
|
|
8405
|
+
method: "GET",
|
|
8406
|
+
path: `${base}/psc/appointment`
|
|
8407
|
+
},
|
|
8408
|
+
book_psc_appointment_v3_order__order_id__psc_appointment_book_post: {
|
|
8409
|
+
method: "POST",
|
|
8410
|
+
path: `${base}/psc/appointment/book`,
|
|
8411
|
+
body: key
|
|
8412
|
+
},
|
|
8413
|
+
reschedule_psc_appointment_v3_order__order_id__psc_appointment_reschedule_patch: {
|
|
8414
|
+
method: "PATCH",
|
|
8415
|
+
path: `${base}/psc/appointment/reschedule`,
|
|
8416
|
+
body: key
|
|
8417
|
+
},
|
|
8418
|
+
cancel_psc_appointment_v3_order__order_id__psc_appointment_cancel_patch: {
|
|
8419
|
+
method: "PATCH",
|
|
8420
|
+
path: `${base}/psc/appointment/cancel`,
|
|
8421
|
+
body: reason
|
|
8422
|
+
}
|
|
8423
|
+
};
|
|
8424
|
+
};
|
|
8425
|
+
var orderBody = (userId, labTestId, extra = {}) => ({
|
|
8426
|
+
user_id: userId,
|
|
8427
|
+
patient_details: {
|
|
8428
|
+
first_name: "Mockingbird",
|
|
8429
|
+
last_name: "Verify",
|
|
8430
|
+
dob: "1990-01-01",
|
|
8431
|
+
gender: "female",
|
|
8432
|
+
phone_number: "+14155551234",
|
|
8433
|
+
email: "verify@example.com"
|
|
8434
|
+
},
|
|
8435
|
+
patient_address: {
|
|
8436
|
+
first_line: "1 Main St",
|
|
8437
|
+
city: "San Diego",
|
|
8438
|
+
state: "CA",
|
|
8439
|
+
zip: "92101",
|
|
8440
|
+
country: "US"
|
|
8441
|
+
},
|
|
8442
|
+
order_set: { lab_test_ids: [labTestId] },
|
|
8443
|
+
...extra
|
|
8444
|
+
});
|
|
8445
|
+
var captureExtraOrder = (reply, ctx) => {
|
|
8446
|
+
const id = reply.body?.order?.id;
|
|
8447
|
+
if (typeof id === "string") ctx.extraOrders.push(id);
|
|
8448
|
+
};
|
|
7287
8449
|
var scenario = (corpus, withOrders) => {
|
|
7288
8450
|
const zip = Object.keys(corpus.observations).map((key) => /\/v3\/order\/area\/info\?.*zip_code=(\d{5})/.exec(key)?.[1]).find((found) => found !== void 0) ?? "92101";
|
|
7289
8451
|
const labTestId = corpus.catalog.labTests[0]?.id;
|
|
@@ -7335,6 +8497,11 @@ var scenario = (corpus, withOrders) => {
|
|
|
7335
8497
|
compare: "shape",
|
|
7336
8498
|
request: () => ({ method: "GET", path: "/v3/lab_tests/labs" })
|
|
7337
8499
|
},
|
|
8500
|
+
{
|
|
8501
|
+
name: "lab tests (bare array)",
|
|
8502
|
+
compare: "shape",
|
|
8503
|
+
request: () => ({ method: "GET", path: "/v3/lab_tests" })
|
|
8504
|
+
},
|
|
7338
8505
|
{
|
|
7339
8506
|
name: `area info ${zip}`,
|
|
7340
8507
|
compare: "exact",
|
|
@@ -7349,25 +8516,7 @@ var scenario = (corpus, withOrders) => {
|
|
|
7349
8516
|
request: (ctx) => ({
|
|
7350
8517
|
method: "POST",
|
|
7351
8518
|
path: "/v3/order",
|
|
7352
|
-
body:
|
|
7353
|
-
user_id: ctx.userId,
|
|
7354
|
-
patient_details: {
|
|
7355
|
-
first_name: "Mockingbird",
|
|
7356
|
-
last_name: "Verify",
|
|
7357
|
-
dob: "1990-01-01",
|
|
7358
|
-
gender: "female",
|
|
7359
|
-
phone_number: "+14155551234",
|
|
7360
|
-
email: "verify@example.com"
|
|
7361
|
-
},
|
|
7362
|
-
patient_address: {
|
|
7363
|
-
first_line: "1 Main St",
|
|
7364
|
-
city: "San Diego",
|
|
7365
|
-
state: "CA",
|
|
7366
|
-
zip: "92101",
|
|
7367
|
-
country: "US"
|
|
7368
|
-
},
|
|
7369
|
-
order_set: { lab_test_ids: [labTestId] }
|
|
7370
|
-
}
|
|
8519
|
+
body: orderBody(ctx.userId, labTestId)
|
|
7371
8520
|
}),
|
|
7372
8521
|
capture: (reply, ctx) => {
|
|
7373
8522
|
const id = reply.body?.order?.id;
|
|
@@ -7388,6 +8537,47 @@ var scenario = (corpus, withOrders) => {
|
|
|
7388
8537
|
}
|
|
7389
8538
|
);
|
|
7390
8539
|
}
|
|
8540
|
+
const unknownOrder = crypto.randomUUID();
|
|
8541
|
+
const calls = orderScopedCalls(unknownOrder);
|
|
8542
|
+
for (const operationId of Object.keys(ORDER_NOT_FOUND)) {
|
|
8543
|
+
const call = calls[operationId];
|
|
8544
|
+
if (!call) continue;
|
|
8545
|
+
steps.push({
|
|
8546
|
+
name: `unknown order 404 ${operationId}`,
|
|
8547
|
+
compare: "exact",
|
|
8548
|
+
request: () => call
|
|
8549
|
+
});
|
|
8550
|
+
}
|
|
8551
|
+
const openAccount = corpus.labAccounts.find(
|
|
8552
|
+
(account2) => Array.isArray(account2.team_id_allowlist) && account2.team_id_allowlist.length === 0 && account2.status === "active"
|
|
8553
|
+
);
|
|
8554
|
+
const openLabTest = openAccount ? corpus.catalog.labTests.find(
|
|
8555
|
+
(test) => String(test.lab?.slug ?? "").toLowerCase() === String(openAccount.lab).toLowerCase()
|
|
8556
|
+
) : void 0;
|
|
8557
|
+
if (withOrders && openAccount && openLabTest) {
|
|
8558
|
+
steps.push(
|
|
8559
|
+
{
|
|
8560
|
+
name: `order.create via empty-allowlist account ${String(openAccount.id)}`,
|
|
8561
|
+
compare: "shape",
|
|
8562
|
+
request: (ctx) => ({
|
|
8563
|
+
method: "POST",
|
|
8564
|
+
path: "/v3/order",
|
|
8565
|
+
body: orderBody(ctx.userId, openLabTest.id, { lab_account_id: openAccount.id })
|
|
8566
|
+
}),
|
|
8567
|
+
capture: captureExtraOrder
|
|
8568
|
+
},
|
|
8569
|
+
{
|
|
8570
|
+
name: `order.create for ${String(openAccount.lab)} with lab_account_id omitted`,
|
|
8571
|
+
compare: "shape",
|
|
8572
|
+
request: (ctx) => ({
|
|
8573
|
+
method: "POST",
|
|
8574
|
+
path: "/v3/order",
|
|
8575
|
+
body: orderBody(ctx.userId, openLabTest.id)
|
|
8576
|
+
}),
|
|
8577
|
+
capture: captureExtraOrder
|
|
8578
|
+
}
|
|
8579
|
+
);
|
|
8580
|
+
}
|
|
7391
8581
|
steps.push({
|
|
7392
8582
|
name: "user.delete",
|
|
7393
8583
|
compare: "shape",
|
|
@@ -7463,7 +8653,10 @@ var verifyAgainstReal = async (options) => {
|
|
|
7463
8653
|
}
|
|
7464
8654
|
}
|
|
7465
8655
|
const clientUserId = `mockingbird-verify-${Date.now().toString(36)}`;
|
|
7466
|
-
const ctx = {
|
|
8656
|
+
const ctx = {
|
|
8657
|
+
real: { clientUserId, extraOrders: [] },
|
|
8658
|
+
mock: { clientUserId, extraOrders: [] }
|
|
8659
|
+
};
|
|
7467
8660
|
const steps = scenario(options.corpus, options.orders === true);
|
|
7468
8661
|
let ran = 0;
|
|
7469
8662
|
let divergent = 0;
|
|
@@ -7506,8 +8699,9 @@ var verifyAgainstReal = async (options) => {
|
|
|
7506
8699
|
await sleep(gap);
|
|
7507
8700
|
}
|
|
7508
8701
|
} finally {
|
|
7509
|
-
|
|
7510
|
-
|
|
8702
|
+
for (const orderId of [ctx.real.orderId, ...ctx.real.extraOrders]) {
|
|
8703
|
+
if (orderId === void 0) continue;
|
|
8704
|
+
await call("real", "POST", `/v3/order/${orderId}/cancel`).catch(() => void 0);
|
|
7511
8705
|
}
|
|
7512
8706
|
if (ctx.real.userId) {
|
|
7513
8707
|
await call("real", "DELETE", `/v2/user/${ctx.real.userId}`).catch(() => void 0);
|
|
@@ -7557,6 +8751,10 @@ var JunctionAPI = class {
|
|
|
7557
8751
|
sqlite;
|
|
7558
8752
|
service;
|
|
7559
8753
|
state;
|
|
8754
|
+
now;
|
|
8755
|
+
explicitTeamId;
|
|
8756
|
+
fixtures;
|
|
8757
|
+
rateWindow = new RateWindow();
|
|
7560
8758
|
corpus;
|
|
7561
8759
|
labAccountConfig;
|
|
7562
8760
|
constructor(options = {}) {
|
|
@@ -7564,6 +8762,13 @@ var JunctionAPI = class {
|
|
|
7564
8762
|
const namespace = options.namespace ?? JUNCTION_NAMESPACE;
|
|
7565
8763
|
const state = new JunctionState(sqlite, namespace, options.onWebhook, options.webhook);
|
|
7566
8764
|
this.state = state;
|
|
8765
|
+
this.now = options.now ?? (() => Date.now());
|
|
8766
|
+
this.explicitTeamId = options.teamId;
|
|
8767
|
+
state.teamId = options.teamId ?? options.corpus?.teamId ?? MOCK_TEAM_ID;
|
|
8768
|
+
state.limits = mergeLimits(DEFAULT_LIMITS, options.limits ?? {});
|
|
8769
|
+
if (options.identity !== void 0 && !IDENTITY_MODES.includes(options.identity))
|
|
8770
|
+
throw new TypeError(`identity must be one of ${IDENTITY_MODES.join(", ")}`);
|
|
8771
|
+
state.identity = options.identity ?? "strict";
|
|
7567
8772
|
const handlers = defineOperations({
|
|
7568
8773
|
...userHandlers(state),
|
|
7569
8774
|
...orderHandlers(state),
|
|
@@ -7585,22 +8790,34 @@ var JunctionAPI = class {
|
|
|
7585
8790
|
});
|
|
7586
8791
|
this.app = this.service.app;
|
|
7587
8792
|
this.sqlite = this.service.sqlite;
|
|
7588
|
-
this.labAccountConfig = options.labAccounts
|
|
8793
|
+
this.labAccountConfig = options.labAccounts ? expandLabAccountLayout(options.labAccounts, state.teamId) : void 0;
|
|
7589
8794
|
if (options.corpus) this.installCorpus(options.corpus);
|
|
7590
8795
|
else this.seedLabAccounts();
|
|
7591
8796
|
state.geoMode = options.geo ?? (options.corpus ? "corpus" : "synthetic");
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
8797
|
+
this.fixtures = options.fixtures;
|
|
8798
|
+
if (this.fixtures) importFixturesInto(state, this.fixtures, this.now);
|
|
8799
|
+
}
|
|
8800
|
+
async fetch(request2) {
|
|
8801
|
+
if (!this.rateWindow.take(this.state.limits.rateLimitPerSecond)) {
|
|
8802
|
+
return jsonRes(429, { detail: "Too Many Requests" }, { "retry-after": "1" });
|
|
8803
|
+
}
|
|
8804
|
+
const normalized = normalizePath(request2);
|
|
8805
|
+
const sent = requestJson(normalized);
|
|
8806
|
+
const response = await this.service.fetch(normalized);
|
|
8807
|
+
const ids = await touchedIds(normalized, await sent, response);
|
|
8808
|
+
return annotateResponse(response, {
|
|
8809
|
+
...Object.keys(ids).length > 0 ? { ids } : {},
|
|
8810
|
+
...this.state.adopted(normalized) ? { adopted: true } : {}
|
|
8811
|
+
});
|
|
7595
8812
|
}
|
|
7596
8813
|
async reset() {
|
|
7597
8814
|
await this.service.reset();
|
|
7598
|
-
if (this.corpus)
|
|
7599
|
-
|
|
7600
|
-
|
|
8815
|
+
if (this.corpus) this.installCorpus(this.corpus);
|
|
8816
|
+
else {
|
|
8817
|
+
this.state.seedDefaultCatalog();
|
|
8818
|
+
this.seedLabAccounts();
|
|
7601
8819
|
}
|
|
7602
|
-
this.state.
|
|
7603
|
-
this.seedLabAccounts();
|
|
8820
|
+
if (this.fixtures) importFixturesInto(this.state, this.fixtures, this.now);
|
|
7604
8821
|
}
|
|
7605
8822
|
/**
|
|
7606
8823
|
* Install a sealed sandbox recording: seed the observation cache and replace the catalog,
|
|
@@ -7612,6 +8829,7 @@ var JunctionAPI = class {
|
|
|
7612
8829
|
this.state.replaceCatalog(corpus.catalog);
|
|
7613
8830
|
}
|
|
7614
8831
|
this.corpus = corpus;
|
|
8832
|
+
this.state.teamId = this.explicitTeamId ?? corpus.teamId ?? MOCK_TEAM_ID;
|
|
7615
8833
|
this.state.coveredZips = coveredZipsOf(corpus);
|
|
7616
8834
|
this.state.corpusLabel = corpusLabel(corpus);
|
|
7617
8835
|
this.seedLabAccounts();
|
|
@@ -7622,20 +8840,93 @@ var JunctionAPI = class {
|
|
|
7622
8840
|
* recorded from a team with no accounts leaves in place).
|
|
7623
8841
|
*/
|
|
7624
8842
|
seedLabAccounts() {
|
|
7625
|
-
const recorded = (this.corpus?.labAccounts ?? []).map(labAccountFromClientFacing).filter((entry) => entry !== void 0);
|
|
8843
|
+
const recorded = (this.corpus?.labAccounts ?? []).map((entry) => labAccountFromClientFacing(entry, this.corpus?.teamId)).filter((entry) => entry !== void 0);
|
|
7626
8844
|
this.state.replaceLabAccounts(
|
|
7627
8845
|
this.labAccountConfig ?? (recorded.length > 0 ? recorded : [...TEAM_LAB_ACCOUNTS])
|
|
7628
8846
|
);
|
|
7629
8847
|
}
|
|
7630
|
-
/**
|
|
8848
|
+
/** The team this namespace answers as. */
|
|
8849
|
+
get teamId() {
|
|
8850
|
+
return this.state.teamId;
|
|
8851
|
+
}
|
|
8852
|
+
/**
|
|
8853
|
+
* Replace the team's lab accounts (a list, or `{ presets, accounts }`); `undefined`
|
|
8854
|
+
* restores the default. Kept across `reset()`, per namespace.
|
|
8855
|
+
*/
|
|
7631
8856
|
configureLabAccounts(accounts) {
|
|
7632
|
-
this.labAccountConfig = accounts
|
|
8857
|
+
this.labAccountConfig = accounts ? expandLabAccountLayout(accounts, this.state.teamId) : void 0;
|
|
7633
8858
|
this.seedLabAccounts();
|
|
7634
8859
|
return this.state.listLabAccounts();
|
|
7635
8860
|
}
|
|
7636
8861
|
labAccounts() {
|
|
7637
8862
|
return this.state.listLabAccounts();
|
|
7638
8863
|
}
|
|
8864
|
+
/** Apply one change to the effective account list and keep it as this namespace's config. */
|
|
8865
|
+
editLabAccounts(edit) {
|
|
8866
|
+
this.labAccountConfig = edit([...this.labAccountConfig ?? this.state.listLabAccounts()]);
|
|
8867
|
+
this.seedLabAccounts();
|
|
8868
|
+
}
|
|
8869
|
+
/** Add one account. Throws `LabAccountConflict` when the id is taken. Kept across resets. */
|
|
8870
|
+
addLabAccount(input) {
|
|
8871
|
+
const record = labAccountFromInput(input, this.state.teamId);
|
|
8872
|
+
if (this.state.labAccounts.has(record.id)) throw new LabAccountConflict(record.id);
|
|
8873
|
+
this.editLabAccounts((accounts) => [...accounts, record]);
|
|
8874
|
+
return record;
|
|
8875
|
+
}
|
|
8876
|
+
/** Add a named preset (see `LAB_ACCOUNT_PRESETS`); `undefined` for an unknown name. */
|
|
8877
|
+
addLabAccountPreset(name, id) {
|
|
8878
|
+
const record = labAccountFromPreset(name, {
|
|
8879
|
+
teamId: this.state.teamId,
|
|
8880
|
+
...id !== void 0 ? { id } : {}
|
|
8881
|
+
});
|
|
8882
|
+
return record ? this.addLabAccount(record) : void 0;
|
|
8883
|
+
}
|
|
8884
|
+
/** Merge fields into an account; `undefined` when there is none with that id. */
|
|
8885
|
+
patchLabAccount(id, patch) {
|
|
8886
|
+
const existing = this.state.labAccounts.get(id);
|
|
8887
|
+
if (!existing) return void 0;
|
|
8888
|
+
const next = patchLabAccount(existing, patch, this.state.teamId);
|
|
8889
|
+
this.editLabAccounts((accounts) => accounts.map((entry) => entry.id === id ? next : entry));
|
|
8890
|
+
return next;
|
|
8891
|
+
}
|
|
8892
|
+
removeLabAccount(id) {
|
|
8893
|
+
if (!this.state.labAccounts.has(id)) return false;
|
|
8894
|
+
this.editLabAccounts((accounts) => accounts.filter((entry) => entry.id !== id));
|
|
8895
|
+
return true;
|
|
8896
|
+
}
|
|
8897
|
+
/** The sandbox limits this namespace enforces. Every one is off unless configured. */
|
|
8898
|
+
get limits() {
|
|
8899
|
+
return { ...this.state.limits };
|
|
8900
|
+
}
|
|
8901
|
+
/** Change some limits (`null` lifts a numeric one); kept across `reset()`. */
|
|
8902
|
+
configureLimits(input) {
|
|
8903
|
+
this.state.limits = mergeLimits(this.state.limits, input);
|
|
8904
|
+
return this.limits;
|
|
8905
|
+
}
|
|
8906
|
+
get identity() {
|
|
8907
|
+
return this.state.identity;
|
|
8908
|
+
}
|
|
8909
|
+
set identity(mode) {
|
|
8910
|
+
if (!IDENTITY_MODES.includes(mode))
|
|
8911
|
+
throw new TypeError(`identity must be one of ${IDENTITY_MODES.join(", ")}`);
|
|
8912
|
+
this.state.identity = mode;
|
|
8913
|
+
}
|
|
8914
|
+
/** Insert users with chosen ids, all or none. Throws `FixtureError` (400/404/409). */
|
|
8915
|
+
insertUsers(users) {
|
|
8916
|
+
return insertUsers(this.state, users, this.now);
|
|
8917
|
+
}
|
|
8918
|
+
/** Remove a user outright, leaving no deletion tombstone. */
|
|
8919
|
+
hardDeleteUser(userId) {
|
|
8920
|
+
return hardDeleteUser(this.state, userId);
|
|
8921
|
+
}
|
|
8922
|
+
/** Insert orders, optionally already in a status. No webhook fires unless asked. */
|
|
8923
|
+
insertOrders(orders, options = {}) {
|
|
8924
|
+
return insertOrders(this.state, orders, this.now, options);
|
|
8925
|
+
}
|
|
8926
|
+
/** Load users, then orders, in one call. */
|
|
8927
|
+
importFixtures(fixtures, options = {}) {
|
|
8928
|
+
return importFixtures(this.state, fixtures, this.now, options);
|
|
8929
|
+
}
|
|
7639
8930
|
corpusInfo() {
|
|
7640
8931
|
const corpus = this.corpus;
|
|
7641
8932
|
if (!corpus) return void 0;
|
|
@@ -7646,7 +8937,8 @@ var JunctionAPI = class {
|
|
|
7646
8937
|
observations: Object.keys(corpus.observations).length,
|
|
7647
8938
|
labTests: corpus.catalog.labTests.length,
|
|
7648
8939
|
labAccounts: corpus.labAccounts.length,
|
|
7649
|
-
zips: this.state.coveredZips.size
|
|
8940
|
+
zips: this.state.coveredZips.size,
|
|
8941
|
+
...corpus.teamId !== void 0 ? { teamId: corpus.teamId } : {}
|
|
7650
8942
|
};
|
|
7651
8943
|
}
|
|
7652
8944
|
get geoMode() {
|
|
@@ -7706,6 +8998,59 @@ var JunctionAPI = class {
|
|
|
7706
8998
|
return this.state.webhookDeliveryAttemptsInOrder();
|
|
7707
8999
|
}
|
|
7708
9000
|
};
|
|
9001
|
+
var LabAccountConflict = class extends Error {
|
|
9002
|
+
constructor(id) {
|
|
9003
|
+
super(`lab account ${id} already exists`);
|
|
9004
|
+
this.id = id;
|
|
9005
|
+
this.name = "LabAccountConflict";
|
|
9006
|
+
}
|
|
9007
|
+
id;
|
|
9008
|
+
};
|
|
9009
|
+
var importFixturesInto = (state, fixtures, now) => {
|
|
9010
|
+
try {
|
|
9011
|
+
importFixtures(state, fixtures, now);
|
|
9012
|
+
} catch (error) {
|
|
9013
|
+
const { message } = fixtureErrorMessage(error);
|
|
9014
|
+
throw new Error(`junction fixtures: ${message}`);
|
|
9015
|
+
}
|
|
9016
|
+
};
|
|
9017
|
+
var asRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
9018
|
+
var isJson = (headers) => (headers.get("content-type") ?? "").toLowerCase().includes("json");
|
|
9019
|
+
var requestJson = async (request2) => {
|
|
9020
|
+
if (request2.method === "GET" || request2.method === "HEAD" || !isJson(request2.headers))
|
|
9021
|
+
return void 0;
|
|
9022
|
+
return asRecord3(
|
|
9023
|
+
await request2.clone().json().catch(() => void 0)
|
|
9024
|
+
);
|
|
9025
|
+
};
|
|
9026
|
+
var UUID_SEGMENT = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}";
|
|
9027
|
+
var USER_PATH = new RegExp(`^/v2/user/(${UUID_SEGMENT})(?:/|$)`);
|
|
9028
|
+
var ORDER_PATH = new RegExp(`^/v3/order/(${UUID_SEGMENT})(?:/|$)`);
|
|
9029
|
+
var touchedIds = async (request2, body, response) => {
|
|
9030
|
+
const ids = {};
|
|
9031
|
+
const put2 = (key, value) => {
|
|
9032
|
+
if (typeof value === "string" && value !== "" && ids[key] === void 0) ids[key] = value;
|
|
9033
|
+
};
|
|
9034
|
+
const url = new URL(request2.url);
|
|
9035
|
+
put2("userId", USER_PATH.exec(url.pathname)?.[1]);
|
|
9036
|
+
put2("orderId", ORDER_PATH.exec(url.pathname)?.[1]);
|
|
9037
|
+
put2("userId", url.searchParams.get("user_id"));
|
|
9038
|
+
put2("orderId", url.searchParams.get("order_id"));
|
|
9039
|
+
put2("labAccountId", url.searchParams.get("lab_account_id"));
|
|
9040
|
+
put2("userId", body?.user_id);
|
|
9041
|
+
put2("labAccountId", body?.lab_account_id);
|
|
9042
|
+
if (request2.method === "POST" && response.ok && isJson(response.headers)) {
|
|
9043
|
+
const created = asRecord3(
|
|
9044
|
+
await response.clone().json().catch(() => void 0)
|
|
9045
|
+
);
|
|
9046
|
+
const order = asRecord3(created?.order);
|
|
9047
|
+
put2("orderId", order?.id);
|
|
9048
|
+
put2("userId", order?.user_id);
|
|
9049
|
+
put2("labAccountId", order?.lab_account_id);
|
|
9050
|
+
put2("userId", created?.user_id);
|
|
9051
|
+
}
|
|
9052
|
+
return ids;
|
|
9053
|
+
};
|
|
7709
9054
|
var normalizePath = (request2) => {
|
|
7710
9055
|
if (!request2.url.includes("/./") && !request2.url.includes("/../") && !/%2f/i.test(request2.url)) {
|
|
7711
9056
|
return request2;
|
|
@@ -7731,20 +9076,36 @@ var normalizePath = (request2) => {
|
|
|
7731
9076
|
};
|
|
7732
9077
|
|
|
7733
9078
|
export {
|
|
7734
|
-
|
|
7735
|
-
|
|
7736
|
-
|
|
9079
|
+
SANDBOX_KEY_PREFIXES,
|
|
9080
|
+
isSandboxKey,
|
|
9081
|
+
DEFAULT_LIMITS,
|
|
9082
|
+
sandboxUserQuotaBody,
|
|
7737
9083
|
observationCacheKey,
|
|
9084
|
+
MOCK_TEAM_ID,
|
|
7738
9085
|
US_STATES,
|
|
9086
|
+
OTHER_TEAM_ID,
|
|
7739
9087
|
TEAM_LAB_ACCOUNTS,
|
|
9088
|
+
isLinkedToTeam,
|
|
7740
9089
|
labAccountFromInput,
|
|
7741
|
-
|
|
7742
|
-
|
|
9090
|
+
PLATFORM_ACCOUNT_STATES,
|
|
9091
|
+
DELEGATED_ACCOUNT_STATES,
|
|
9092
|
+
LAB_ACCOUNT_PRESETS,
|
|
9093
|
+
presetAccountId,
|
|
7743
9094
|
ORDER_STATUSES_BY_METHOD,
|
|
7744
9095
|
TRANSITION_ALIASES,
|
|
7745
9096
|
RESULT_FIXTURES,
|
|
7746
9097
|
FAULT_PRESETS,
|
|
7747
9098
|
resolveTransition,
|
|
9099
|
+
ORDER_NOT_FOUND,
|
|
9100
|
+
MISS_HEADER,
|
|
9101
|
+
KNOWN_HEADER,
|
|
9102
|
+
UNKNOWN_ZIP_STATUS,
|
|
9103
|
+
UNKNOWN_ZIP_ERROR_TYPE,
|
|
9104
|
+
IDENTITY_MODES,
|
|
9105
|
+
FixtureError,
|
|
9106
|
+
document,
|
|
9107
|
+
operationIds,
|
|
9108
|
+
supportedOperationIds,
|
|
7748
9109
|
PSC_AVAILABILITY_ZIPS,
|
|
7749
9110
|
PHLEBOTOMY_AVAILABILITY_ZIPS,
|
|
7750
9111
|
COVERAGE_ZIPS,
|
|
@@ -7754,8 +9115,6 @@ export {
|
|
|
7754
9115
|
reshapeCoverageGeoCommand,
|
|
7755
9116
|
prefetchCoverageObservations,
|
|
7756
9117
|
DEFAULT_JUNCTION_BASE_URL,
|
|
7757
|
-
SANDBOX_KEY_PREFIXES,
|
|
7758
|
-
isSandboxKey,
|
|
7759
9118
|
fingerprintCorpus,
|
|
7760
9119
|
pullCorpus,
|
|
7761
9120
|
diffCorpus,
|
|
@@ -7766,6 +9125,7 @@ export {
|
|
|
7766
9125
|
verifyAgainstReal,
|
|
7767
9126
|
JUNCTION_NAMESPACE,
|
|
7768
9127
|
corpusLabel,
|
|
7769
|
-
JunctionAPI
|
|
9128
|
+
JunctionAPI,
|
|
9129
|
+
LabAccountConflict
|
|
7770
9130
|
};
|
|
7771
|
-
//# sourceMappingURL=chunk-
|
|
9131
|
+
//# sourceMappingURL=chunk-ICINEQ7D.js.map
|