distill: 48 cross-project best-practices from 2026-07 reflection sweep
Promotions from reflecting 21 projects' session logs (incl. agent-runtimes 122-log drain). Adds coverage across networking (eBPF VIP/VPN SNAT/VLAN bridge/forward-auth preflight/ingress TLS), kubernetes (CSI hotplug/PodSecurity debug/self-managed GitOps/runtime annotations), CI (dispatch tokens/runner death/base image), git (CI-rebase/shallow reset/PR governance), python (async session pool/httpx redirects/logging), TDD (AsyncMock/xfail lifecycle), api-integration (SDK parse/token-scope 404/schema probing), plus docker, scripting, debugging, security-architecture, secrets, react, octopus. State: .distill-state.json refreshed with current HEADs + 5 newly-tracked projects.
This commit is contained in:
@@ -129,6 +129,52 @@ Cross-references: [API Design](api-design.md) covers server-side API design (the
|
||||
|
||||
---
|
||||
|
||||
## 6. Discover API Endpoints via Swagger/OpenAPI Before Re-Reading Prose Docs
|
||||
|
||||
**Principle:** When an API call returns 404 or "endpoint not found", fetch the actual paths from the live spec (`/swagger.v1.json`, `/openapi.json`, `/api-docs`, `/.well-known/openapi`) with `curl + jq` before consulting documentation.
|
||||
|
||||
**Why it matters:** Documentation prose drifts from the live API faster than the OpenAPI spec does. The spec is generated from the running code; the prose is hand-maintained. Five seconds with curl beats fifteen minutes of doc spelunking and beats minutes of guessing at path variants.
|
||||
|
||||
**How to implement:**
|
||||
- Grep the spec for the resource/verb: `curl -s https://api.example.com/swagger.v1.json | jq '.paths | keys[]' | grep -i <resource>`
|
||||
- For verb-specific lookups: `jq '.paths | to_entries[] | select(.value.post) | .key'`
|
||||
- For required body fields: `jq '.components.schemas.<TypeName>.required'`
|
||||
- If the API requires auth even for the spec endpoint, fetch via your existing credential — the spec is not sensitive.
|
||||
|
||||
**Anti-patterns:**
|
||||
- Reading the docs page-by-page when a 30-character `jq` query finds the answer.
|
||||
- Trying URL variants (`/foo`, `/foos`, `/foo/v1`, `/v1/foo`) without checking the spec first.
|
||||
- Trusting a path from training data that returns 404 instead of consulting the live spec.
|
||||
|
||||
Generalises across any REST API consumer — Gitea, GitHub, Octopus, Kubernetes apiserver, cloud provider APIs. The 4xx response is a strong signal that you have the wrong path; treat it as a prompt to fetch the spec, not as a prompt to guess again.
|
||||
|
||||
## 7. Generated OpenAPI SDKs: Bypass `_parse_response` for Untyped/Error Bodies
|
||||
|
||||
Clients generated by `openapi-python-client` (and similar generators) only handle the status codes and response shapes that were in the spec at generation time. Two silent failure modes result:
|
||||
- A valid RFC-9457 (422) problem body raises `ValueError: Unexpected status code` inside the generated `_parse_response` — the caller sees it as a 502, not a structured 422.
|
||||
- An endpoint whose 200 body wasn't captured in the schema (untyped/streaming/`Any`) returns `.parsed is None` on success — no error, the value is just absent.
|
||||
|
||||
Fix: for any call where you need to handle 4xx/5xx bodies as data, or where the 200 body isn't in the generated model, bypass parsing. Use the generated `_get_kwargs` to build the request, then call the raw httpx client and inspect `.status_code`/`.content` yourself: `sdk_fn._get_kwargs(**kwargs)` → `sdk_client.get_httpx_client().request(**req_kwargs)`. Do NOT use `*_detailed()` / `sync_detailed()` for those endpoints — they route through `_parse_response` and will raise or silently return `None`.
|
||||
|
||||
## 8. A 404 on a Known-Good Endpoint May Be a Token-Scope Miss, Not a Missing Resource
|
||||
|
||||
Some APIs (Gitea, and others that avoid confirming resource existence to unauthorized callers) return **404 rather than 403** when the token lacks the required scope for an operation. A `workflow_dispatch` (or any write) call to an endpoint you *know* exists, returning 404, is a strong signal that the token is missing a scope (e.g. `write:actions`), not that the path is wrong.
|
||||
|
||||
- Before re-checking the path or the API version, check the token's scopes against what the operation needs.
|
||||
- Keep a scoped-token map: know which token has which scopes, and switch to the correctly-scoped credential rather than debugging the URL.
|
||||
|
||||
This is a distinct 404 root cause from section 6 (wrong path) — when the path is known-good, suspect scope before re-fetching the spec.
|
||||
|
||||
## 9. Discover Undocumented Schemas by Probing With Minimal Writes
|
||||
|
||||
When a resource's required fields and accepted enum values aren't documented, send deliberately minimal `POST`/`PUT` requests and read the validation errors — they enumerate required fields and reveal which enum values are accepted vs rejected. Create-then-delete a throwaway resource to confirm the full response shape, then delete it so no residue remains. Always confirm the running version first (e.g. `GET /api` for version) rather than trusting a schema from memory or training data, since schemas drift between versions. This complements OpenAPI discovery (section 6): use the spec when one exists; fall back to error-driven probing when the resource is under-specified or the spec omits body requirements.
|
||||
|
||||
## 10. Check the Resource/Link Map + Feature-Toggles Before Assuming a Feature Exists
|
||||
|
||||
Before concluding an API offers a given primitive (a "policies" endpoint, a "required steps" feature, a webhook family), inspect the service's root/link map or feature-toggle list rather than guessing at paths. Absence of the link means the primitive isn't exposed. When a documented feature seems missing from the live API, check the feature-toggle endpoint (e.g. `/api/configuration/feature-toggles`) before concluding it's absent — toggles frequently gate visibility of an entire endpoint family.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
Integrating with a third-party API is an exercise in compensating for its limitations. Verify capabilities before designing, build application-layer compensation for missing features, use git + SOPS as a state store when you have no persistent compute, and always declare a system of record for bidirectional syncs. Idempotency and observability are non-negotiable.
|
||||
|
||||
Reference in New Issue
Block a user