@crvouga/mockingbird-service-rxvortex 0.1.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 +5 -0
- package/README.md +124 -0
- package/dist/chunk-DNELVPK4.js +3191 -0
- package/dist/chunk-DNELVPK4.js.map +7 -0
- package/dist/chunk-ZAM35JQO.js +384 -0
- package/dist/chunk-ZAM35JQO.js.map +7 -0
- package/dist/cli.js +19 -0
- package/dist/cli.js.map +7 -0
- package/dist/index.d.ts +966 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +7 -0
- package/dist/server.d.ts +1294 -0
- package/dist/server.js +12 -0
- package/dist/server.js.map +7 -0
- package/package.json +88 -0
package/CHANGELOG.md
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# @crvouga/mockingbird-service-rxvortex
|
|
2
|
+
|
|
3
|
+
Stateful mock of the **RxVortex (Strive)** compounding-pharmacy API for test suites: the
|
|
4
|
+
client-credentials token, order submit, status, cancel, the recovery lookup by sender order id,
|
|
5
|
+
the preset catalog, and the signed status webhooks the pharmacy posts back. Orders move only
|
|
6
|
+
when a test says so (an admin transition or an auto-advance path on the mock clock), so an eRx
|
|
7
|
+
suite that waited up to 60 s on the real sandbox resolves in milliseconds.
|
|
8
|
+
|
|
9
|
+
- Operation coverage: [SUPPORT.md](https://github.com/crvouga/mockingbird/blob/main/packages/service/rxvortex/SUPPORT.md)
|
|
10
|
+
- The vendor publishes no spec: the contract (`openapi.yaml`) is hand-authored from the wire
|
|
11
|
+
shapes our consumer reads and writes, and every field fallback it relies on is served.
|
|
12
|
+
|
|
13
|
+
## Install
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -D @crvouga/mockingbird-service-rxvortex
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
ESM only. Node >= 22 or Bun >= 1.2. No native dependencies. Serve it with
|
|
20
|
+
`npx mockingbird-rxvortex serve`, `createServer` from `./server` (Node), or `createRuntime` with
|
|
21
|
+
any Fetch server.
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
Point `RXVORTEX_API_URL` at the mock. Set `RXVORTEX_WEBHOOK_SECRET` in the app and pass the same
|
|
26
|
+
value as `--webhook-secret`.
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npx mockingbird-rxvortex serve --port 8791 \
|
|
30
|
+
--webhook-url http://127.0.0.1:3000/prescriptions/webhooks/rxvortex \
|
|
31
|
+
--webhook-secret "$RXVORTEX_WEBHOOK_SECRET" \
|
|
32
|
+
--auto-advance "2000:Fill,Shipping,Delivered"
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { createRuntime } from "@crvouga/mockingbird-service-rxvortex"
|
|
37
|
+
|
|
38
|
+
const rx = createRuntime({
|
|
39
|
+
webhooks: { url: "http://127.0.0.1:3000/prescriptions/webhooks/rxvortex", secret: "whsec-test" },
|
|
40
|
+
})
|
|
41
|
+
const post = (path: string, body: unknown, headers: Record<string, string> = {}) =>
|
|
42
|
+
rx.fetch(
|
|
43
|
+
new Request(`http://rxvortex.test${path}`, {
|
|
44
|
+
method: "POST",
|
|
45
|
+
headers: { "content-type": "application/json", ...headers },
|
|
46
|
+
body: JSON.stringify(body),
|
|
47
|
+
}),
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
const { access_token } = (await (
|
|
51
|
+
await post("/api/v1/generate-access-token", { client_id: "geviti", client_secret: "s" })
|
|
52
|
+
).json()) as { access_token: string }
|
|
53
|
+
// …the app submits POST /api/v1/orders with Authorization: Bearer <access_token>…
|
|
54
|
+
|
|
55
|
+
// Move an order the way the pharmacy would; each step emits the signed status webhook.
|
|
56
|
+
await post("/__admin/orders/pay_123/transition", { to: "Shipping", trackingnumber: "1Z999" })
|
|
57
|
+
await post("/__admin/orders/pay_123/transition", { to: "Delivered" })
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Routes
|
|
61
|
+
|
|
62
|
+
| Route | Behaviour |
|
|
63
|
+
| --- | --- |
|
|
64
|
+
| `POST /api/v1/generate-access-token` | JSON `{client_id, client_secret}` → `{access_token, token_type: "Bearer", expires_in: 86400}`. Any pair works unless `clients` is set (`PUT /__admin/settings`). Tokens stay valid 24 h on the mock clock; our client caches for 24 h and never refreshes on 401. |
|
|
65
|
+
| `POST /api/v1/orders` | Validates the submit payload against the contract; a violation is 422 `{message, errors: {"patient.phone": ["…"]}}`. An inactive or unknown `preset_catalog_id` is 422. A repeated `order.sender_order_id` is 409. Success: `{success: true, order_tracking_id: "RXV-…", sender_order_id, status: "Created"}` (the tracking id is always a string). |
|
|
66
|
+
| `GET /api/v1/orders/{id}` | `id` is the tracking id **or** the sender order id (our payment id, the recovery lookup). Returns `rxstatus`, `orderstatus`, `shipping_status`, `delivered_date`, `trackingnumber`, `shippingservice`, `shippingcarrier`, `shipmenttrackingurl`, `cancellable`, and the id under `order_tracking_id`, `tracking_id` and `orderReferenceID`. |
|
|
67
|
+
| `DELETE /api/v1/orders/{id}` | Cancels while `cancellable` (until shipped), emitting the webhook; otherwise 409. |
|
|
68
|
+
| `GET /api/v1/preset-catalog-items` | `{data: [...]}` rows with `catalog_id`, `medication_name`, `medication_strength`, `package_size`, `quantity`, `quantity_units`, `medication_form`, `route`, `states`, `status`. Includes the custom-cream anchor preset `e404ad76-0f82-4b04-8f25-841650e2e819` and one inactive row. |
|
|
69
|
+
|
|
70
|
+
### Webhooks
|
|
71
|
+
|
|
72
|
+
Every status change posts `{event: "order.status_updated", orderReferenceID, order_tracking_id,
|
|
73
|
+
tracking_id, sender_order_id, rxstatus, orderstatus, shipping_status, delivered_date,
|
|
74
|
+
trackingnumber, shippingcarrier, shippingservice, shipmenttrackingurl, updated_at}` with header
|
|
75
|
+
`x-rxvortex-webhook-secret: <secret>` (plain equality, as our receiver checks). Non-2xx answers
|
|
76
|
+
are retried (immediately, 5 s, 5 min, 30 min, 2 h). `GET /__admin/webhooks` lists deliveries,
|
|
77
|
+
`GET /__admin/webhooks/events` the payloads, `POST /__admin/webhooks/flush` runs pending retries
|
|
78
|
+
now, and `PUT /__admin/webhook-endpoints` sets per-namespace receivers.
|
|
79
|
+
|
|
80
|
+
### Admin (beyond the standard contract)
|
|
81
|
+
|
|
82
|
+
| Route | Effect |
|
|
83
|
+
| --- | --- |
|
|
84
|
+
| `POST /__admin/orders/:id/transition` | `{to, trackingnumber?, shippingcarrier?, shippingservice?, delivered_date?}`. `to` is a vendor status: `Fill`, `PV1 Complete`, `Compound`, `Out of Stock`, `On Hold`, `Shipping`, `Delivered`, `Cancelled`, `Rejected`, `Error`, or any string (used verbatim). The three status fields move together; shipping generates tracking when none is given. |
|
|
85
|
+
| `PUT /__admin/settings` | `{tokenTtlSeconds?, staticTokens?, clients?, autoAdvance?: {afterMs, path} \| null}` for the calling namespace. `staticTokens` admits `RXVORTEX_API_TOKEN` (the catalog client's static bearer). |
|
|
86
|
+
| `POST /__admin/tick` | Apply every auto-advance step that is due on the mock clock (the served mock also ticks every 100 ms). |
|
|
87
|
+
| `GET /__admin/orders` | The namespace's orders. |
|
|
88
|
+
|
|
89
|
+
Fault presets (`POST /__admin/faults {"preset": "<name>", "count"?: n}`; `GET /__admin/faults/presets`):
|
|
90
|
+
`duplicate_sender_order_id`, `created_but_500` (creates, then 500; recovery succeeds),
|
|
91
|
+
`numeric_tracking_id`, `token_expired`, `stale_error_with_delivered_date`,
|
|
92
|
+
`validation_errors_array`, `validation_errors_object`, `validation_errors_empty`, `server_error`,
|
|
93
|
+
`webhook_duplicate`, `webhook_reorder`, `webhook_drop`.
|
|
94
|
+
|
|
95
|
+
### Namespaces
|
|
96
|
+
|
|
97
|
+
Our backend's `fetch` cannot add headers, so a namespace can be chosen three ways:
|
|
98
|
+
`x-mockingbird-namespace`, a `/ns/<name>` prefix on `RXVORTEX_API_URL`, or by client id:
|
|
99
|
+
`PUT /__admin/credentials {"credentials": {"<RXVORTEX_CLIENT_ID>": "<namespace>"}}` (tokens carry
|
|
100
|
+
the client id they were issued to).
|
|
101
|
+
|
|
102
|
+
### Deliberately not modelled
|
|
103
|
+
|
|
104
|
+
- Real fulfilment timing: nothing moves on its own unless `autoAdvance` is set.
|
|
105
|
+
- Patient and prescriber details are validated, never stored or echoed back.
|
|
106
|
+
- The live catalog: the default rows are synthesised in the live client's field names (no
|
|
107
|
+
sandbox recording exists); pass `catalog` to load recorded rows.
|
|
108
|
+
- Refills and multi-order shipments.
|
|
109
|
+
|
|
110
|
+
## API
|
|
111
|
+
|
|
112
|
+
| Export | Kind | Description |
|
|
113
|
+
| --- | --- | --- |
|
|
114
|
+
| `RxVortexAPI` | class | The in-process mock: `fetch(request)`, `reset()`, `transition(id, {to, …})`, `tick()`, `orders()`. Options: `sqlite`, `now`, `namespace`, `catalog`, `settings`, `onWebhook`. |
|
|
115
|
+
| `createRuntime` | function | The mock with the full service contract (health, admin, namespaces, credentials, presets, webhooks). Options: `webhooks: {url, secret, retryDelaysMs?, fetch?}`, `settings`, `catalog`, `tickMs`, `clock`, `seed`, `adminKey`, `onLog`. |
|
|
116
|
+
| `RXVORTEX_PRESETS` | object | Every named fault preset. |
|
|
117
|
+
| `RXVORTEX_NAMESPACE` | string | The service name, `"rxvortex"`. |
|
|
118
|
+
| `tokenCredential` | function | The client id a bearer token was issued to (how credentials map to namespaces). |
|
|
119
|
+
| `CUSTOM_CREAM_ANCHOR_PRESET_ID` | string | The sandbox custom-cream anchor preset id. |
|
|
120
|
+
| `DEFAULT_CATALOG` | array | The default preset catalog rows. |
|
|
121
|
+
| `document`, `operationIds`, `supportedOperationIds` | values | The vendored OpenAPI contract and its operation ids. |
|
|
122
|
+
| `createServer`, `serveTarget`, `DEFAULT_PORT` (`./server`) | Node | Serve over `node:http` (auto-advance ticks every 100 ms); the `serve` CLI target; port 8791. |
|
|
123
|
+
|
|
124
|
+
Part of [mockingbird](https://github.com/crvouga/mockingbird).
|