Document the new endpoints and the fixture workflow
Updates the three coverage summaries to 325 methods so they agree, moves the per-resource extras out of the gap list in `COVERAGE.md` now that they are wrapped, and records why the fixtures exist and how to refresh them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+45
-1
@@ -16,6 +16,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
the file twice. This is the only way the API creates a document, so there
|
||||
is no `create_document`. Enables reqwest's `multipart` feature, which adds
|
||||
`mime`, `mime_guess`, and `unicase` and leaves the MSRV at 1.88.
|
||||
- Per-resource extras, nineteen methods: `retry_webhook`, `sort_tasks`,
|
||||
`set_default_design`, `set_default_client_gateway_token`,
|
||||
`subscription_steps`, `check_subscription_steps`, the company-gateway
|
||||
`test`/`clone`/`import_customers` trio, four bank-integration and
|
||||
bank-transaction routes, and six client routes (`update_client_tax_data`,
|
||||
`client_show_settings`, `client_peppol_discovery`, `check_client_vat`,
|
||||
`client_vat_status`, `client_documents`). Each is a `POST` unless noted in
|
||||
`COVERAGE.md`, so none is retried on a 5xx. Adds `MessageResponse` for the
|
||||
`{"message": ...}` replies and `SubscriptionStep`.
|
||||
- Password-protected actions, eight methods: `purge_client`, `merge_clients`,
|
||||
`merge_vendors`, `purge_company`, `purge_company_save_settings`,
|
||||
`invite_user`, `purge_user`, and `new_user_template`. They need
|
||||
`with_api_password`, most cannot be undone, and none is retried on a 5xx.
|
||||
A merge keeps the first id and folds the second into it. Checked against
|
||||
the upstream controllers and mock servers; not yet against a real instance.
|
||||
- Activity log, email history and previews, fourteen methods:
|
||||
`list_activities`, `list_all_activities`, `activity_feed`,
|
||||
`entity_activity`, `add_activity_note`, `download_activity_entity`,
|
||||
`client_email_history`, `entity_email_history`, and the previews `preview`,
|
||||
`preview_html`, `live_preview`, `live_design`, `preview_purchase_order` and
|
||||
`live_preview_purchase_order`. Adds `Activity`, `ActivityFeedEntry` and
|
||||
`ActivityRef`. Email history is returned as raw JSON. See `COVERAGE.md` for
|
||||
which were confirmed against a real server and the easy mistakes (a
|
||||
singular entity name in one route, a plural in the next).
|
||||
- `FileUpload` and `Error::InvalidUpload`. An upload with no files, a nameless
|
||||
file, or an unparseable MIME type is refused locally, because the server
|
||||
answers an empty upload with a success that changed nothing.
|
||||
@@ -27,7 +51,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
release. The advisory job fails visibly rather than silently; it is kept
|
||||
non-blocking through branch protection rather than `continue-on-error`.
|
||||
- Wrappers for every one of the API's 34 `Route::resource` groups, plus
|
||||
`users` — 284 methods (including document uploads). See `COVERAGE.md` for the per-endpoint inventory.
|
||||
`users` — 325 methods (including document uploads, per-resource extras, password-protected actions, and activity, email-history and preview routes). See `COVERAGE.md` for the per-endpoint inventory.
|
||||
- `COVERAGE.md`, a detailed record of what is wrapped, what is not, and why,
|
||||
checked against `routes/api.php` on the `v5-stable` branch.
|
||||
- Retry with full-jitter exponential backoff, configurable through
|
||||
@@ -83,6 +107,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- `list_all_*` with `per_page(0)` never terminated: the check `got < 0` has
|
||||
no solution. It now falls back to the default page size, and a `MAX_PAGES`
|
||||
backstop raises `Error::PaginationLimit` rather than quietly truncating.
|
||||
- Creating a recurring invoice or recurring expense failed after the record
|
||||
had been created: a real server returns `frequency_id` as the string `"5"`
|
||||
and the field was a strict integer. It now accepts a number or a numeric
|
||||
string.
|
||||
- Creating a purchase order, location, or webhook from a default model was
|
||||
rejected by a real server. An unset `PurchaseOrder.status_id`,
|
||||
`Location.country_id`, or `Webhook.format` was sent as `""`, which the
|
||||
server turns into `NULL` and then refuses (a 500 for the purchase order and
|
||||
webhook, a 422 for the location). They are now left out of the request when
|
||||
empty.
|
||||
- `with_api_password` refused a password containing a control character (a
|
||||
newline, say) with a misleading `InvalidToken` error. Such a password is
|
||||
now sent base64-encoded as `X-API-PASSWORD-BASE64`, the form the server's
|
||||
middleware accepts for exactly this.
|
||||
- A payment whose `invoices` entry had a `null` or missing `invoice_id` or
|
||||
`amount` failed to decode the whole payment. Both now default.
|
||||
- `list_system_logs` failed to decode against a real server. `SystemLog`'s
|
||||
`created_at` and `updated_at` were typed as strings, following the OpenAPI
|
||||
spec, but the server sends integer timestamps. They are now `i64`, like
|
||||
every other model.
|
||||
- Requests could hang indefinitely. `reqwest` applies no timeout of its own
|
||||
and none was configured.
|
||||
- Ids were interpolated into request paths unescaped, so a `/`, `?`, or `#`
|
||||
|
||||
@@ -84,10 +84,51 @@ IN_API_TOKEN=... cargo run --example list_clients
|
||||
```
|
||||
|
||||
Tests live in `tests/` (integration-style, no network access required);
|
||||
`src/` has no `#[test]` modules. Two files: `model_roundtrip.rs` covers the
|
||||
serde layer, and `client_requests.rs` covers the request plumbing
|
||||
(pagination, path escaping, headers, timeouts, error mapping) against a
|
||||
`wiremock` server on loopback — no credentials or internet needed.
|
||||
`src/` has no `#[test]` modules. Four files:
|
||||
|
||||
- `model_roundtrip.rs` — the serde layer, from hand-written payloads.
|
||||
- `client_requests.rs` — the request plumbing (pagination, path escaping,
|
||||
headers, timeouts, retry, error mapping, one test per endpoint extra)
|
||||
against a `wiremock` server on loopback.
|
||||
- `decode_tolerance.rs` — for every model, serialises `Default`, then nulls
|
||||
and removes each field in turn and requires the result to still decode.
|
||||
A new model belongs in its list; a new non-`Option` field without
|
||||
`null_to_default` fails it.
|
||||
- `real_shapes.rs` — decodes `tests/fixtures/*.json`, which are the *shapes*
|
||||
(keys and JSON types, no values) of responses captured from a real server,
|
||||
and checks a decode + re-serialise loses no key. It also refuses fixtures
|
||||
that hold any real value, and fixtures with no test.
|
||||
|
||||
No credentials or internet needed for any of them.
|
||||
|
||||
**Why the fixtures matter:** twice, mocks written from the upstream source
|
||||
passed while a real server disagreed (system-log timestamps; the
|
||||
`/subscriptions/steps` shape), and `frequency_id` and several empty-string
|
||||
fields were wrong the same way. When the smoke test finds a divergence,
|
||||
refresh the fixtures so it stays caught:
|
||||
|
||||
```sh
|
||||
# 1. Fill a DISPOSABLE instance with one row of each kind (writes!).
|
||||
IN_SMOKE_WRITE_HOST=localhost IN_BASE_URL=... IN_API_TOKEN=... \
|
||||
cargo run --example seed_sample_data
|
||||
# 2. Capture shapes (GET only). Review `git diff tests/fixtures` before committing.
|
||||
IN_BASE_URL=... IN_API_TOKEN=... cargo run --example capture_shapes
|
||||
```
|
||||
|
||||
A resource with no rows is skipped, so the fixture set only covers what the
|
||||
instance held. Bank integrations, company gateways, client gateway tokens,
|
||||
subscriptions, task schedulers, and recurring quotes are not captured: they
|
||||
need external accounts, or (recurring quotes on 5.13.50) the server's own
|
||||
insert fails.
|
||||
|
||||
**Fields omitted when empty.** The server runs `ConvertEmptyStringsToNull`,
|
||||
so an unset `String` serialised as `""` becomes `NULL`, which a non-null
|
||||
column or an `integer` rule then rejects. `Location.country_id`,
|
||||
`PurchaseOrder.status_id` and `Webhook.format` therefore carry
|
||||
`skip_serializing_if = "String::is_empty"`. If a create from a `Default`
|
||||
model 500s or 422s on an empty field, do the same — and add the path to
|
||||
`OMITTED_WHEN_EMPTY` in `tests/real_shapes.rs` and `examples/smoke_test.rs`,
|
||||
which is deliberately a list of decisions rather than a blanket exemption.
|
||||
|
||||
### Testing against a real server
|
||||
|
||||
@@ -112,10 +153,20 @@ tier leaves a gap in the client number sequence and a soft-deleted row (a
|
||||
real purge needs the account password), and it may fire webhooks. Use a
|
||||
disposable instance — a local VM or container — for it.
|
||||
|
||||
Beyond "does it decode", the read tier fetches the same rows raw and fails
|
||||
if a typed row lost a key (`keys kept: <resource>`, names only, never
|
||||
values), and the write tier creates a purchase order, location, webhook and
|
||||
recurring invoice from bare `Default` models, since those are what broke.
|
||||
With `IN_API_PASSWORD` set as well, a password tier merges two throwaway
|
||||
clients and purges the survivor (a real, permanent delete); it has not yet
|
||||
been run against an instance. `.github/workflows/smoke.yml` runs it on demand (`workflow_dispatch` only,
|
||||
needing `IN_BASE_URL` and `IN_API_TOKEN` secrets); the write tier runs only if
|
||||
the `write_host` input names the instance's host.
|
||||
|
||||
It prints PASS/FAIL per check and never the contents of a record. The upload
|
||||
wire format (`POST` plus `_method=PUT`) was read from the framework source
|
||||
rather than observed, so the first run of the write tier against a real
|
||||
instance is the real verification of it.
|
||||
wire format (`POST` plus `_method=PUT`) was read from the framework source,
|
||||
then confirmed by running the write tier against a disposable Invoice Ninja
|
||||
5.13.50 instance (2026-10-24): single and multi-file uploads both attached.
|
||||
|
||||
## Changelog
|
||||
|
||||
@@ -264,18 +315,18 @@ changes, doc tweaks) does not need an entry.
|
||||
## Coverage
|
||||
|
||||
**All 34 of the API's `Route::resource` groups are wrapped**, plus `users`
|
||||
(which isn't one). 284 methods: 30 resources with CRUD + bulk, 3 with CRUD
|
||||
only, 4 with entity actions, 16 with document uploads, and 28 hand-written.
|
||||
(which isn't one). 325 methods: 30 resources with CRUD + bulk, 3 with CRUD
|
||||
only, 4 with entity actions, 16 with document uploads, and 69 hand-written.
|
||||
|
||||
Plus the non-CRUD surface: documents, entity actions and PDF downloads for
|
||||
the four action-capable resources, `report`, `chart`, `search`, `statics`,
|
||||
`send_email`, `refund_payment`, `ping`, and `health_check`.
|
||||
|
||||
**`COVERAGE.md` is the detailed inventory** — every wrapped endpoint, every
|
||||
gap, and the reason for each gap. The main remaining gaps are a handful of
|
||||
per-resource extras
|
||||
(`clients/{id}/purge`, `company_gateways/{id}/test`, …), and the
|
||||
integration/auth routes.
|
||||
gap, and the reason for each gap. The main remaining gaps are the
|
||||
integration and auth routes, import/export, the public invitation-key
|
||||
downloads (which take no auth token, so they don't fit the client's
|
||||
request path), and a few company and user extras.
|
||||
|
||||
Keep `COVERAGE.md` current when adding or removing an endpoint: it names
|
||||
exact counts and route groups, so it goes stale quietly. The three coverage
|
||||
|
||||
+102
-29
@@ -18,7 +18,7 @@ declares 34 `Route::resource` groups and 274 explicit route declarations
|
||||
> exists or what it returns.
|
||||
|
||||
**All 34 `Route::resource` groups are now wrapped**, plus `users` (which is
|
||||
not a resource group). 284 methods in total.
|
||||
not a resource group). 325 methods in total.
|
||||
|
||||
## Resources with full CRUD + bulk
|
||||
|
||||
@@ -58,6 +58,10 @@ not a resource group). 284 methods in total.
|
||||
| Task Schedulers | `/task_schedulers` | `TaskScheduler` |
|
||||
| Tokens | `/tokens` | `CompanyToken` |
|
||||
|
||||
`create_recurring_quote` currently 500s against Invoice Ninja 5.13.50: the
|
||||
server's own insert names a `design_id` column its `recurring_quotes` table
|
||||
does not have. The request is well-formed; nothing here can work around it.
|
||||
|
||||
`bulk_*` accepts any action the endpoint supports; `archive`, `restore`, and
|
||||
`delete` work on all of them. An `ids` list matching nothing returns an empty
|
||||
`Vec`, not an error.
|
||||
@@ -212,6 +216,103 @@ the whole tail — `chart("project_burnup/<id>", &body)`.
|
||||
Large report exports are queued and emailed rather than returned inline; in
|
||||
that case the body is a short JSON message instead of CSV.
|
||||
|
||||
## Activities, email history and previews
|
||||
|
||||
Each has a response shape of its own. Marked *observed* where confirmed
|
||||
against a 5.13.50 server.
|
||||
|
||||
| Endpoint | Method | Returns |
|
||||
| --- | --- | --- |
|
||||
| `GET /activities` | `list_activities`, `list_all_activities` | `Vec<Activity>` (*observed*) |
|
||||
| `GET /activities?reactv2=1` | `activity_feed(rows)` | `Vec<ActivityFeedEntry>` (*observed*) |
|
||||
| `POST /activities/entity` | `entity_activity(entity, id, rows)` | `Vec<ActivityFeedEntry>` (*observed*) |
|
||||
| `POST /activities/notes` | `add_activity_note(entity, id, notes)` | `Activity` (*observed*) |
|
||||
| `GET /activities/download_entity/{id}` | `download_activity_entity` | PDF bytes |
|
||||
| `POST /emails/clientHistory/{client}` | `client_email_history` | `Vec<serde_json::Value>` |
|
||||
| `POST /emails/entityHistory` | `entity_email_history` | `Vec<serde_json::Value>` |
|
||||
| `POST /preview` | `preview`, `preview_html` | PDF bytes / HTML |
|
||||
| `POST /live_preview` | `live_preview` | PDF bytes (*observed*) |
|
||||
| `POST /live_design` | `live_design` | PDF bytes |
|
||||
| `POST /preview/purchase_order` | `preview_purchase_order` | PDF bytes |
|
||||
| `POST /live_preview/purchase_order` | `live_preview_purchase_order` | PDF bytes |
|
||||
|
||||
Things that are easy to get wrong:
|
||||
|
||||
- **The entity name is singular in one route and plural in the next.**
|
||||
`entity_activity` takes `invoice`; `add_activity_note` takes `invoices`.
|
||||
- **`rows`, not `per_page`, caps the activity log** (default 75). Pass it
|
||||
through `ListParams::param`. `entity_activity` leaves `rows` out when not
|
||||
given rather than sending `null`, because the server tests for the key and
|
||||
a null would count as given and remove the cap.
|
||||
- **The two activity shapes differ.** `Activity` carries hashed ids and a
|
||||
string `activity_type_id`; the feed's `ActivityFeedEntry` carries a numeric
|
||||
one and labelled references (`invoice`, `client`, … each `{label,
|
||||
hashed_id}`).
|
||||
- **Email history is raw JSON.** The objects mirror what the mail provider
|
||||
reported, and the instance available had never sent mail, so there was no
|
||||
shape to take.
|
||||
- **Not yet run against a real server:** `preview`, `preview_html`, and both
|
||||
purchase-order previews (they need a design object or a purchase order and a
|
||||
PDF backend), and `live_design`, which answered a bare minimal request with
|
||||
a 500 from the server on 5.13.50.
|
||||
|
||||
## Password-protected actions
|
||||
|
||||
Behind the `password_protected` middleware, so each needs
|
||||
`with_api_password` (the server also lets a recent password-authenticated
|
||||
session through without the header). Most are **irreversible**, and all are
|
||||
`POST`s except one, so none is retried on a 5xx.
|
||||
|
||||
| Endpoint | Method | Returns |
|
||||
| --- | --- | --- |
|
||||
| `POST /clients/{id}/purge` | `purge_client` | `MessageResponse` |
|
||||
| `POST /clients/{id}/{other}/merge` | `merge_clients(id, other)` | the surviving `Client` |
|
||||
| `POST /vendors/{id}/{other}/merge` | `merge_vendors(id, other)` | the surviving `Vendor` |
|
||||
| `POST /companies/purge/{id}` | `purge_company` | `MessageResponse` |
|
||||
| `POST /companies/purge_save_settings/{id}` | `purge_company_save_settings` | `MessageResponse` |
|
||||
| `POST /users/{id}/invite` | `invite_user` | `MessageResponse` |
|
||||
| `POST /users/{id}/purge` | `purge_user` | `()` (the server answers 204) |
|
||||
| `GET /users/create` | `new_user_template` | a blank `User`, nothing stored |
|
||||
|
||||
A merge keeps the first id and folds the second into it. These are verified
|
||||
against the upstream controllers and mock servers only: running them needs the
|
||||
account password, so the smoke test's optional password tier (merge two
|
||||
throwaway clients, purge the survivor) has not yet been run against a real
|
||||
instance. A password containing a control character cannot be a header value
|
||||
and goes out as `X-API-PASSWORD-BASE64` instead.
|
||||
|
||||
## Per-resource extras
|
||||
|
||||
Routes that sit beside a resource's CRUD set, each with its own response
|
||||
shape (read from the upstream controllers; the ones marked *observed* were
|
||||
also confirmed against a 5.13.50 server). All are `POST` unless noted, so none
|
||||
is retried on a 5xx or a timeout.
|
||||
|
||||
| Endpoint | Method | Returns |
|
||||
| --- | --- | --- |
|
||||
| `POST /webhooks/{id}/retry` | `retry_webhook` | `Webhook` |
|
||||
| `POST /tasks/sort` | `sort_tasks` | `MessageResponse` |
|
||||
| `POST /designs/set/default` | `set_default_design` | `MessageResponse` |
|
||||
| `POST /client_gateway_tokens/{id}/setAsDefault` | `set_default_client_gateway_token` | `ClientGatewayToken` |
|
||||
| `GET /subscriptions/steps` | `subscription_steps` | `Vec<SubscriptionStep>` (*observed*: an object keyed by class name, not the array the source suggests) |
|
||||
| `POST /subscriptions/steps/check` | `check_subscription_steps` | `()` |
|
||||
| `POST /company_gateways/{id}/test` | `test_company_gateway` | `MessageResponse` |
|
||||
| `POST /company_gateways/{id}/clone` | `clone_company_gateway` | `CompanyGateway` |
|
||||
| `POST /company_gateways/{id}/import_customers` | `import_company_gateway_customers` | `MessageResponse` |
|
||||
| `POST /bank_integrations/refresh_accounts` | `refresh_bank_accounts` | `()` (the body is a serialised query builder) |
|
||||
| `POST /bank_integrations/remove_account/{id}` | `remove_bank_account` | `BankIntegration` |
|
||||
| `POST /bank_integrations/get_transactions/{id}` | `fetch_bank_transactions` | `MessageResponse` (the server ignores the id) |
|
||||
| `POST /bank_transactions/match` | `match_bank_transactions` | `Vec<BankTransaction>` |
|
||||
| `POST /clients/{id}/updateTaxData` | `update_client_tax_data` | `Client` |
|
||||
| `POST /clients/{id}/show_settings` | `client_show_settings` | `serde_json::Value` (*observed*) |
|
||||
| `POST /clients/{id}/peppol_discovery` | `client_peppol_discovery` | `serde_json::Value` |
|
||||
| `POST /clients/{id}/check_vat` | `check_client_vat` | `MessageResponse` |
|
||||
| `GET /clients/{id}/vat_status` | `client_vat_status` | `MessageResponse` (*observed*) |
|
||||
| `POST /clients/{id}/documents` | `client_documents` | `Vec<Document>` (*observed*; one page) |
|
||||
|
||||
`updateTaxData` and `check_vat` call out to external tax services, so the
|
||||
smoke test does not exercise them.
|
||||
|
||||
## Not covered
|
||||
|
||||
### Nothing is blocked on a dependency
|
||||
@@ -231,42 +332,14 @@ from `src/models/mod.rs`, and add a `resource_methods!` invocation in
|
||||
`src/client.rs` (or `crud_methods!` if the resource has no `/bulk` route —
|
||||
check `routes/api.php`).
|
||||
|
||||
### Password-protected endpoints still unwrapped
|
||||
|
||||
`with_api_password` makes these reachable, but the methods themselves aren't
|
||||
written yet:
|
||||
|
||||
- `POST /clients/{id}/purge`
|
||||
- `POST /clients/{id}/{other}/merge`
|
||||
- `POST /vendors/{id}/{other}/merge`
|
||||
- `POST /companies/purge/{company}`,
|
||||
`POST /companies/purge_save_settings/{company}`
|
||||
- `POST /users/{id}/invite`, `POST /users/{id}/purge`, `GET /users/create`
|
||||
|
||||
### Individual endpoints not wrapped
|
||||
|
||||
- **Client extras** — `updateTaxData`, `show_settings`, `peppol_discovery`,
|
||||
`check_vat`, `vat_status`, `documents`
|
||||
- **Bank integration extras** — `refresh_accounts`, `remove_account/{id}`,
|
||||
`get_transactions/{id}`, and `POST /bank_transactions/match`
|
||||
- **Company gateway extras** — `test`, `clone`, `import_customers`
|
||||
- **Company extras** — `GET /companies/{id}/logo`,
|
||||
`POST /companies/{id}/default`, `updateOriginTaxData`,
|
||||
`POST /companies/current`
|
||||
- **Design extras** — `POST /designs/set/default`
|
||||
- **Client gateway token extras** — `setAsDefault`
|
||||
- **Task extras** — `POST /tasks/sort`
|
||||
- **Webhook extras** — `POST /webhooks/{id}/retry`
|
||||
- **Subscription extras** — `GET /subscriptions/steps`,
|
||||
`POST /subscriptions/steps/check`
|
||||
- **User extras** (not password-protected, unlike the rest of `/users`) —
|
||||
`POST /users/{id}/disconnect_mailer`, `POST /users/{id}/disconnect_oauth`,
|
||||
`POST /user/{id}/reconfirm`
|
||||
- **Preview** — `POST /preview`, `POST /preview/purchase_order`, live preview
|
||||
- **Activities** — `GET /activities`, `POST /activities/entity`,
|
||||
`POST /activities/notes`, `download_entity/{activity}`
|
||||
- **Email history** — `POST /emails/clientHistory/{client}`,
|
||||
`POST /emails/entityHistory`
|
||||
- **Auth** — `/login`, `/logout`, `/refresh`, `/connected_account`,
|
||||
passkey routes
|
||||
- **Import/export** — `/import`, `/preimport`, `/import_json`, `/export`,
|
||||
|
||||
@@ -149,18 +149,29 @@ way a document is created. There is no `create_document` and there will not
|
||||
be one. `edocument/upload` was left out because it imports an expense from
|
||||
an e-document rather than attaching a file, so it belongs with item 6.
|
||||
|
||||
**Done:** the per-resource extras — webhook retry, task sort, default
|
||||
design, default gateway token, subscription steps, and the client,
|
||||
bank-integration, and company-gateway routes (19 methods). The numbering
|
||||
below keeps its original item numbers. Running the smoke test against a real
|
||||
server overturned one reading of the source here: `/subscriptions/steps`
|
||||
answers with an object keyed by class name, not the array the controller
|
||||
suggests. That is the case for keeping the real-server tier.
|
||||
|
||||
**Done:** the password-protected actions — client purge and merge, vendor
|
||||
merge, company purge, user invite and purge, and the blank-user template (8
|
||||
methods). They are verified against the upstream controllers and mocks only;
|
||||
the smoke test has a password tier for the first real-server run
|
||||
(`IN_API_PASSWORD`), which has not happened yet. The claim here that
|
||||
`with_api_password` already made them reachable was not quite true: a
|
||||
password with a control character was refused locally.
|
||||
|
||||
**Done:** activities, email history and previews (14 methods, two new
|
||||
models). The email history stays raw JSON, since no instance here had sent
|
||||
mail to take a shape from; promoting it to a model needs a sample. Several
|
||||
previews are unverified against a real server (see `COVERAGE.md`).
|
||||
|
||||
Still to do:
|
||||
|
||||
2. **Per-resource extras** — mechanical, and they reuse existing patterns:
|
||||
`webhooks/{id}/retry`, `tasks/sort`, `designs/set/default`,
|
||||
`client_gateway_tokens/{id}/setAsDefault`, `subscriptions/steps`, the
|
||||
client extras (`updateTaxData`, `show_settings`, `check_vat`, …), the
|
||||
bank-integration and company-gateway extras.
|
||||
3. **Password-protected actions** — `with_api_password` already makes these
|
||||
reachable; only the methods are missing. Client purge and merge, vendor
|
||||
merge, company purge, user invite and purge.
|
||||
4. **Preview, activities, and email history** — distinct response shapes, so
|
||||
each needs its own hand-written method and probably its own model.
|
||||
5. **Public invitation-key downloads** — eight routes that take no auth
|
||||
token, so they do not fit the authenticated request path and need a
|
||||
different entry point.
|
||||
|
||||
@@ -100,7 +100,7 @@ IN_API_TOKEN=... cargo run --example list_clients
|
||||
|
||||
## Coverage
|
||||
|
||||
Every one of the API's 34 resource groups, plus users — 284 methods. Each
|
||||
Every one of the API's 34 resource groups, plus users — 325 methods. Each
|
||||
resource has `list`, `list_all`, `get`, `create`, `update`, `delete`, and
|
||||
(where the API offers one) `bulk`:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user