@crvouga/ mockingbird-service-aha (英语)
本地模拟器。 真正的API合同. 部分 嘲笑鸟。 。 。 .
国家模拟器 AHA (高级卫生学院) 在家理发 用于测试的 API 套接字:HMAC署名的创建顺序并取消,以及——其主要工作——订单状态webhooks AHA 邮递员 销售商没有拉API,所以每个下游效应(EMR预约, storefront status, "blood draw") 起始为 webhook; 仿真器按需发出一个, 每一个 字段,我们的处理器读取, 所以ZIP路线的血液工作路径 终于可以测试.
- 行动范围: 支助
- 供应商不公布具体内容:合同
openapi.yaml由我们手写 消费者的zod计划 和电线形状.
安装
npm install -D @crvouga/mockingbird-service-aha
仅限无害环境管理。 节点 22或 Bun 1.2. 无土生属地. 给它服务
npx mockingbird-aha serve, (中文(简体) ), createServer 从 ./server (节点),或 createRuntime 带有任意
获取服务器 .
使用量
将应用程序指向模拟器:
| 信封 | 数值 |
|---|---|
AHA_API_URL | http://127.0.0.1:8799 (实验室提供者路径已经允许回回回回;); AhaService 需要 http 例外,参见 G-A1 / S10.2) |
AHA_API_KEY 编号 : AHA_API_SECRET | 什么都行,否则一对就成了 --api-key 编号 : --api-secret 准确验证签名 |
AHA_USE_LEGACY_AUTH | true 开关到 X-<Partner>-Auth-Key (例如:. X-Acme-Auth-Key); 两种模式都被接受 |
AHA_WEBHOOK_SECRET | 与 --webhook-secret |
npx mockingbird-aha serve --port 8799 \
--webhook-url http://127.0.0.1:3000/bloodwork/aha-webhook \
--webhook-secret "$AHA_WEBHOOK_SECRET" \
--api-key "$AHA_API_KEY" --api-secret "$AHA_API_SECRET" \
--envelope raw --auto-schedule 2000
import { createHmac } from "node:crypto"
import { createServer } from "@crvouga/mockingbird-service-aha/server"
// Test setup: the emulator, posting order-status webhooks to the app's handler.
const server = await createServer({
webhooks: { url: "http://127.0.0.1:3000/bloodwork/aha-webhook", secret: "aha-webhook-secret" },
})
// Application code: its usual signed request, with AHA_API_URL set to server.url.
const [apiUrl, apiKey, apiSecret] = [server.url, "aha-key", "aha-secret"]
const path = "/v1/acme/create-order"
const timestamp = Date.now().toString()
const signature = createHmac("sha256", apiSecret)
.update(`${apiKey}:${path}:${timestamp}`)
.digest("base64")
const created = await fetch(`${apiUrl}${path}`, {
method: "POST",
headers: {
"content-type": "application/json",
"X-API-KEY": apiKey,
"X-TIMESTAMP": timestamp,
"X-SIGNATURE": signature,
},
body: JSON.stringify({
partner_order_id: "AC-101", patient_id: "pat-1", biological_sex: "Female",
patient_first_name: "Ada", patient_last_name: "Lovelace", patient_dob: "1990-12-10",
patient_phone_number: "5555550100", patient_address_line1: "501 N 5th St",
patient_city: "Phoenix", patient_state: "AZ", patient_zipcode: "85004",
service_type: "Full Service", npi: "1234567893", ordering_physician: "Dr. Grace Hopper",
test_codes: [{ test_code: "CBC", test_description: "Complete blood count" }],
}),
})
// → {content: {partner_order_id: "AC-101", order_number: "AHA-…"}, message, status: "SUCCESS"}
// Test control: move the order as AHA would. Each transition posts a webhook to the app.
const transition = (body: unknown) =>
fetch(`${server.url}/__admin/orders/AC-101/transition`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(body),
})
// AHA books the draw: our handler books the EMR appointment for 10:30 Denver time.
await transition({
status: "Scheduled",
scheduledAt: "2026-10-01T16:30:00Z",
timeZone: "America/Denver",
})
// The phlebotomist checks out with a sample: our handler sets vitalBloodDrawn.
await transition({ status: "Check Out", drawStatus: "Sample Collected" })
await server.close()
路线
这个 {partner} 路径段是您的账户的 sult( 例如) . acme);模拟器接受任何.
| 路线 | 行为 |
|---|---|
POST /v1/{partner}/create-order | 鉴定尸体(partner_order_id病人的田园, biological_sex, (中文(简体) ), service_type, (中文(简体) ), npi, (中文(简体) ), ordering_physician, (中文(简体) ), test_codes选项 preferred_schedule_date/time, (中文(简体) ), patient_timezone。创建 或更新: 重复 partner_order_id 保留它 order_number答复 {content: {partner_order_id, order_number}, message, status: "SUCCESS"}。 。 。 . |
POST /v1/{partner}/cancel | {partner_order_id, notes: [{note_type: "CANCELLATION", notes}]} → {message, status}显示 a Cancelled webhook( 关闭为 cancelWebhook: false) (中文(简体) ). 未知的id → 404;采集样本的命令 → 200 status: "ERROR"AHA协会 order_number 已接受为 partner_order_id 因为我们的实验室提供客户端 把它送到那里(G -A1). |
奥特. HMAC 模式 : X-API-KEY, (中文(简体) ), X-TIMESTAMP (划时代,在相隔5分钟后),
X-SIGNATURE = 基数64 HMAC-SHA256(机密, "<apiKey>:<path>:<timestamp>"),在其中 path 这是
请求没有主机、正机或路径 /__admin/ns/<name> 前缀。 带有已知密钥 + 密钥
(单位:千美元)--api-key/--api-secret 或者说 credentials (b) 准确核实签名;
未配置任何密钥的签名会被接受,签名只检查形状。 遗产
模式 : X-<Partner>-Auth-Key (+ 数) X-API-Version: 1.0),例如. X-Acme-Auth-Key; 任何伙伴名称为
已接受。 失败者为401人 {status: "ERROR", message}。 。 。 .
信封 (G- A1). AhaService 期待原始数据 {content, message, status}· ;
AhaLabProvider 预期 {success: true, data: {…}}。 Raw 是默认值;选择使用
--envelope raw|wrapped 或每个命名空间 PUT /__admin/settings {"envelope": "wrapped"}。 。 。 .
引证无能. X-Idempotency-Key (实验室提供路径):同一键和身体重放
存储的响应( e)idempotent-replayed: true; 不同身体的同一键为409.
网络呼号
POST <webhook-url> (我们的路线 : POST /bloodwork/aha-webhook与
Authorization: Token <AHA_WEBHOOK_SECRET>每一个身体都有 status, (中文(简体) ), partnerOrderId, (中文(简体) ), ahaOrderId, (中文(简体) ),
加上,按地位(顺序的IANA区所有当地时间):
status | 额外字段 |
|---|---|
Scheduled, (中文(简体) ), Rescheduled | scheduleServiceTime (单位:千美元)YYYY-MM-DDTHH:mm:ss,瞬间可解析)和 scheduleServiceTimeZone (IANA)——由我们的处理者要求,尽管没有参加DTO——加 scheduledServiceDate/Time/TimeZone 财务报告和审定财务报表 scheduleConfirmationDate/Time/TimeZone |
Check In | checkInDate, (中文(简体) ), checkInTime, (中文(简体) ), checkInTimeZone |
Check Out | drawStatus (默认) Sample Collected, (中文(简体) ), drawStatusDate/Time/TimeZone |
Lab Testing In Progress | dropOffDate, (中文(简体) ), dropOffTime, (中文(简体) ), dropOffTimeZone |
Cancelled, (中文(简体) ), Non Scheduled Update | —— 说 |
drawStatus 值 : Sample Collected, (中文(简体) ), Completed (拖动), Patient Refused, (中文(简体) ), UTO, (中文(简体) ),
Patient Not Home, (中文(简体) ), Patient Rescheduled, (中文(简体) ), Order Cancelled, (中文(简体) ), Others, (中文(简体) ),
Patient Asked to Reschedule (拖曳失败). 非-2xx 的答案被重审(立即,5 s,
5分30分2小时); GET /__admin/webhooks, (中文(简体) ), …/events, (中文(简体) ), …/replay, (中文(简体) ), …/flush 和往常一样.
管理员(超过标准合同)
| 路线 | 效果 |
|---|---|
POST /__admin/orders/:partnerOrderId/transition | {status, drawStatus?, scheduledAt?, timeZone?} 发出网钩. status 以上为任何值(例和 _ 原谅;未知值被逐字发送). scheduledAt (ISO 或 ocho ms) 默认命令的首选位置, 否则在接下来的 24 h出局 ; Rescheduled 默认为一天后. timeZone 默认命令 patient_timezone,则 America/New_York。 。 。 . :partnerOrderId 也可能是 order_number。 。 。 . |
PUT /__admin/settings | {envelope?, credentials?: [{apiKey, apiSecret?}], allowLegacy?, timestampToleranceMs?, defaultTimeZone?, cancelWebhook?, autoSchedule?: {afterMs, leadMs?} | ms | null} 呼叫的命名空间. GET 让他们看到隐藏的秘密. |
POST /__admin/tick | 释放所有 autoSchedule 在模拟器时钟上到期的 webhook( 已安装的模拟器每100 mss) . |
GET /__admin/orders | 取名空间的命令(编号,状态,预约,区-没有病人数据). |
错误预设( E)POST /__admin/faults {"preset": "<name>", "count"?: n}: bad_signature (401个),
rate_limited (429个) rate_limit, (中文(简体) ), server_error (500个), order_error (200个带内
status: "ERROR", (中文(简体) ), invalid_response (200个身体,zod计划不接受),
webhook_duplicate, (中文(简体) ), webhook_reorder, (中文(简体) ), webhook_drop。 。 。 .
命名空间
x-mockingbird-namespace页:1 /__admin/ns/<name> 前缀在 AHA_API_URL (签名仍涵盖
或 API 键:
PUT /__admin/credentials {"credentials": {"<AHA_API_KEY>": "<namespace>"}}。 。 。 .
SFTP 结果交付
createAhaSftpServer 从 ./sftp 在麻黄端口启动真实的SSH/SFTP服务器并共享命令状态 createRuntime它支持密码或公用钥匙认证,主机钥匙验证, list编号 :stat, 二进制上下载, 原子临时文件重命名, 删除, 嵌入目录, 稳定的 POSIX 权限和仿真器时钟时间戳 。 决定性的 Ed25519 主机密钥在运行间稳定.
import { Client } from "ssh2"
import { createRuntime } from "@crvouga/mockingbird-service-aha"
import { createAhaSftpServer } from "@crvouga/mockingbird-service-aha/sftp"
// Test setup: the SFTP endpoint, sharing order state with the HTTP emulator, and one result.
const runtime = createRuntime()
const sftp = await createAhaSftpServer({
runtime,
accounts: [{ username: "aha", password: "local-test-password" }],
})
sftp.seed("/outbox/AHA-1001_result.pdf", new TextEncoder().encode("%PDF-1.4"))
// Application code: the ssh2 client it already uses. Only host, port and host key change.
const client = new Client()
await new Promise<void>((resolve, reject) =>
client.once("ready", resolve).once("error", reject).connect({
host: sftp.host,
port: sftp.port,
username: "aha",
password: "local-test-password",
hostVerifier: (key: Buffer) => key.equals(sftp.hostPublicKey),
}),
)
const pdf = await new Promise<Buffer>((resolve, reject) =>
client.sftp((error, session) => {
if (error) return reject(error)
session.readFile("/outbox/AHA-1001_result.pdf", (error, bytes) =>
error ? reject(error) : resolve(bytes),
)
}),
)
client.end()
await sftp.close()
使用 seed() 安装任意的二进制固定件或 publishResult(orderId, bytes) 摆放一个稳定 AHA-…_result.pdf 内 /outbox。第一个完整的下载将链接的 HTTP 命令移动到 Lab Testing In Progress 并发布其网页;重复投票/倒数不重复这一过渡. fault() 强制下一场行动断开、拒绝许可、报告磁盘完整或只接受部分写入. journal() 曝光路径、 操作名称、 字节计数和结果—— 绝不是证书或文件内容 . reset(namespace?) 清除决定性文件系统状态。 复制目的地名称失败, 所以 .tmp ——最终更名为原子.
故意不模拟
- 下游S3摄入和
aha_results_queuesFTP 交接后处理. - 真正的调度: AHA 联系病人; 除非测试转换顺序, 任何动作都不要动
或组合
autoSchedule。 。 。 . - 可用- ZIP 列表(我们的应用程序在呼叫 AHA 之前,先决定资格).
- 患者细节经过验证,从未被存储或回放.
- 真正销售商使用的哪个信封(G-A1):两者都得到服务,每个命名空间一个.
API 密码
| 导出 | 仁爱 | 说明 |
|---|---|---|
AhaAPI | 类 | 进程模拟器 : fetch(request), (中文(简体) ), reset(), (中文(简体) ), transition(id, {status, drawStatus?, scheduledAt?, timeZone?}), (中文(简体) ), tick(), (中文(简体) ), orders()选项: sqlite, (中文(简体) ), now, (中文(简体) ), namespace, (中文(简体) ), settings, (中文(简体) ), onWebhook, (中文(简体) ), wallClock。 。 。 . |
createRuntime | 函数 | 服务合同的模拟者。 选项 : webhooks: {url, secret, retryDelaysMs?, fetch?}, (中文(简体) ), settings, (中文(简体) ), tickMs, (中文(简体) ), wallClock, (中文(简体) ), clock, (中文(简体) ), seed, (中文(简体) ), adminKey, (中文(简体) ), onLog。 。 。 . |
AHA_PRESETS | 对象 | 每个命名的断层预设. |
AHA_NAMESPACE | 字符串 | 服务名称, "aha"。 。 。 . |
WEBHOOK_PATH | 字符串 | "/bloodwork/aha-webhook"接收器的路线. |
ORDER_STATUSES, (中文(简体) ), DRAW_STATUSES | 数组 | 销售商状况拼写了webhooks使用. |
verifyAuth | 函数 | HMAC / 仿真器的遗留验证( 错误消息, 或 undefined) (中文(简体) ). |
apiKeyCredential | 函数 | 请求所携带的 API 密钥( 如何使用证书映射来命名空间 ) . |
isTimeZone, (中文(简体) ), zonedParts, (中文(简体) ), zonedToEpoch | 函数 | IANA-zone帮助器曾填补了本地日期/时间字段. |
document, (中文(简体) ), operationIds, (中文(简体) ), supportedOperationIds | 数值 | 销售商为OpenAPI合同及其业务代号. |
createServer, (中文(简体) ), serveTarget, (中文(简体) ), DEFAULT_PORT (单位:千美元)./server(中文(简体) ) | 节点 | 服务过来 node:http (表单每100毫秒勾出) serve CLI目标; 第8799口. |
createAhaSftpServer (单位:千美元)./sftp(中文(简体) ) | 节点 | Real SSH/ SFTP 端点带有决定主机密钥/文件系统,共享顺序状态,传输控制,以及一个等离子端口. |
部分 嘲笑鸟。 。 。 .