|
| 1 | +R1: Framework and Standards |
| 2 | + |
| 3 | +- Implement using flask-smorest within the existing Flask application |
| 4 | +- OpenAPI 3.x specification auto-generated from code annotations and marshmallow schemas |
| 5 | +- Target Python 3.10+ |
| 6 | +- New code lives in lnt/server/api/v5/ package, completely separate from existing API code |
| 7 | +- Existing v4 API remains unchanged — no modifications to api.py or its behavior |
| 8 | +- Reuse existing database models (SQLAlchemy schemas in testsuitedb.py, testsuite.py, etc.) but do not treat reuse of existing API implementation code as a requirement — write clean new implementations where |
| 9 | +appropriate |
| 10 | +- CORS headers enabled on all v5 endpoints (Access-Control-Allow-Origin: *) |
| 11 | + |
| 12 | +R2: URL Structure and Identifiers |
| 13 | + |
| 14 | +- Base path: /api/v5/{testsuite}/ |
| 15 | +- Always uses the default database (no db_<database> prefix) |
| 16 | +- Entities addressed by natural keys (machine name, test name) or server-generated UUIDs (runs, regressions, field changes) — never by internal auto-increment database IDs |
| 17 | +- A discovery endpoint at GET /api/v5/ lists available test suites with links to their resources |
| 18 | + |
| 19 | +R3: Entity Endpoints |
| 20 | + |
| 21 | +Machines |
| 22 | + |
| 23 | +GET /machines — List (filterable, simple pagination) |
| 24 | +POST /machines — Create machine independently |
| 25 | +GET /machines/{machine_name} — Detail |
| 26 | +PATCH /machines/{machine_name} — Update metadata/parameters (including rename) |
| 27 | +DELETE /machines/{machine_name} — Delete machine and its runs |
| 28 | +GET /machines/{machine_name}/runs — List runs for this machine (cursor-paginated) |
| 29 | +Machines are also created implicitly if a run is submitted for a nonexistent machine. |
| 30 | + |
| 31 | +Orders |
| 32 | + |
| 33 | +GET /orders — List (cursor-paginated, filterable) |
| 34 | +POST /orders — Create with metadata (git commit info, etc.) |
| 35 | +GET /orders/{order_id} — Detail (includes previous/next order references) |
| 36 | +PATCH /orders/{order_id} — Update metadata |
| 37 | +Orders are read/create/update only — no delete. The order_id in the path is the primary order field value (e.g. the revision hash). If order fields are multi-valued and ambiguous, query parameters |
| 38 | +disambiguate. Orders are also created implicitly during run submission. |
| 39 | + |
| 40 | +Runs |
| 41 | + |
| 42 | +GET /runs — List (cursor-paginated, filterable by machine=, after=, before=) |
| 43 | +POST /runs — Submit run (server generates UUID, returns it) |
| 44 | +GET /runs/{uuid} — Detail |
| 45 | +DELETE /runs/{uuid} — Delete run |
| 46 | +The UUID is a new field, generated server-side on submission. This requires a database schema migration to add the column. The submission endpoint requires JSON format with format_version '2'. Legacy formats (v0, v1) and non-JSON payloads are rejected. |
| 47 | + |
| 48 | +Tests |
| 49 | + |
| 50 | +GET /tests — List (cursor-paginated, filterable) |
| 51 | +GET /tests/{test_name} — Detail |
| 52 | +Read-only. Tests are created implicitly via run submission. |
| 53 | + |
| 54 | +Samples |
| 55 | + |
| 56 | +Samples are always accessed through their parent run — they have no external identifier of their own. |
| 57 | +GET /runs/{uuid}/samples — All samples for a run (cursor-paginated) |
| 58 | +GET /runs/{uuid}/samples?has_profile=true — Filter to samples with profiles |
| 59 | +GET /runs/{uuid}/tests/{test_name}/samples — Samples for a specific test in a run |
| 60 | +Read-only. Samples are created as part of run submission. |
| 61 | + |
| 62 | +Profiles |
| 63 | + |
| 64 | +Profiles are accessed through run + test name. Under the hood, the API finds the sample for that run+test that has a profile attached. |
| 65 | +GET /runs/{uuid}/tests/{test_name}/profile — Profile metadata + top-level counters |
| 66 | +GET /runs/{uuid}/tests/{test_name}/profile/functions — List functions with counters |
| 67 | +GET /runs/{uuid}/tests/{test_name}/profile/functions/{fn_name} — Disassembly + per-instruction counters |
| 68 | +Profiles are submitted as base64-encoded data within the run submission payload (existing format). No separate upload endpoint. |
| 69 | + |
| 70 | +Regressions |
| 71 | + |
| 72 | +GET /regressions — List (cursor-paginated, filterable by state=, machine=, test=) |
| 73 | +POST /regressions — Create from field changes |
| 74 | +GET /regressions/{uuid} — Detail (see response contents below) |
| 75 | +PATCH /regressions/{uuid} — Update title, bug URL, state |
| 76 | +DELETE /regressions/{uuid} — Delete |
| 77 | +POST /regressions/{uuid}/merge — Merge source regressions into this one |
| 78 | +POST /regressions/{uuid}/split — Split field changes into a new regression |
| 79 | +GET /regressions/{uuid}/indicators — List field changes (cursor-paginated) |
| 80 | +POST /regressions/{uuid}/indicators — Add field change |
| 81 | +DELETE /regressions/{uuid}/indicators/{fc_uuid} — Remove field change |
| 82 | +Regressions are identified by server-generated UUID (schema migration required). |
| 83 | + |
| 84 | +Regression states (string enum): |
| 85 | +detected, staged, active, not_to_be_fixed, ignored, detected_fixed, fixed |
| 86 | + |
| 87 | +State transitions are unconstrained — any state can be set to any other state via PATCH. |
| 88 | + |
| 89 | +Regression detail response (GET /regressions/{uuid}) includes: |
| 90 | +- uuid, title, bug, state |
| 91 | +- Embedded list of indicators, each containing: |
| 92 | + - field_change_uuid |
| 93 | + - test_name, machine_name, field_name |
| 94 | + - old_value, new_value |
| 95 | + - start_order and end_order (the order field values, not internal IDs) |
| 96 | + - run_uuid (the run where the change was detected) |
| 97 | + |
| 98 | +Field Changes (triage) |
| 99 | + |
| 100 | +GET /field-changes — List unassigned field changes (cursor-paginated, filterable by machine=, test=, field=) |
| 101 | +POST /field-changes — Create a field change programmatically (references machine, test, metric, and orders by name) |
| 102 | +POST /field-changes/{uuid}/ignore — Ignore a field change |
| 103 | +DELETE /field-changes/{uuid}/ignore — Un-ignore a field change |
| 104 | +Field changes are identified by server-generated UUID (schema migration required). |
| 105 | +Creating a field change requires: machine (name), test (name), metric (name), old_value, new_value, start_order, end_order, and optionally run_uuid. All references are resolved by name/value, not internal ID. |
| 106 | + |
| 107 | +Time Series |
| 108 | + |
| 109 | +GET /query |
| 110 | + Query params: machine={name}&test={name}&metric={name}&after_order={order}&before_order={order} |
| 111 | + &after_time={iso8601}&before_time={iso8601}&sort={fields}&limit={n}&cursor={c} |
| 112 | +The metric parameter is required; all other query parameters are optional. |
| 113 | +Returns cursor-paginated time-series data for graphing. Uses field names (not indices) to be self-documenting. |
| 114 | + |
| 115 | +Schema and Fields |
| 116 | + |
| 117 | +Schema definitions and metric field metadata are returned as part of the test suite |
| 118 | +detail response (GET /api/v5/test-suites/{name}) rather than as standalone endpoints. |
| 119 | +The response includes a "schema" object containing machine_fields, run_fields, and |
| 120 | +metrics (with name, type, display_name, unit, unit_abbrev, bigger_is_better for each). |
| 121 | +There are no separate /fields or /schema endpoints. |
| 122 | + |
| 123 | +R4: Pagination |
| 124 | + |
| 125 | +- Cursor-based pagination for unbounded lists: runs, tests, orders, samples, field changes, regressions, regression indicators, time series |
| 126 | +- Simple offset-based or unpaginated for bounded/small lists: machines, API keys |
| 127 | +- Cursor-paginated response envelope: {"items": [...], "cursor": {"next": "...", "previous": "..."}} |
| 128 | +- Default page size with configurable limit parameter |
| 129 | +- Cursors are opaque strings (clients must not parse them) |
| 130 | + |
| 131 | +R5: Filtering and Sorting |
| 132 | + |
| 133 | +- Named query parameters per endpoint, documented in OpenAPI spec |
| 134 | +- Supported filter types per endpoint (examples): |
| 135 | + - machine=, test=, field=, name_contains=, name_prefix= |
| 136 | + - after=, before= (for timestamps and order values) |
| 137 | + - state= (for regressions, supports multiple values: ?state=active&state=detected) |
| 138 | + - has_profile=true (for samples) |
| 139 | + - sort=field_name (prefix with - for descending: sort=-start_time) |
| 140 | +- Exact filters and available sort fields defined per endpoint in the OpenAPI spec |
| 141 | + |
| 142 | +R6: Response Format |
| 143 | + |
| 144 | +- All responses in JSON |
| 145 | +- Standardized error format: |
| 146 | +{"error": {"code": "not_found", "message": "Machine 'foo' not found in test suite 'nts'"}} |
| 147 | +- Standard HTTP status codes: 200, 201, 204, 304, 400, 401, 403, 404, 409, 422 |
| 148 | +- ETag headers on GET responses; support If-None-Match for conditional requests returning 304 Not Modified when data hasn't changed |
| 149 | + |
| 150 | +R7: Authentication and Authorization |
| 151 | + |
| 152 | +- Authorization: Bearer <token> header on all requests |
| 153 | +- API keys with scopes (each scope includes all scopes above it): |
| 154 | + - read — all GET endpoints |
| 155 | + - submit — submit runs (POST /runs), create orders (POST /orders) |
| 156 | + - triage — modify regression state/title/bug, ignore/un-ignore field changes, create/merge/split regressions, manage regression indicators |
| 157 | + - manage — create/update/delete machines; update orders; delete runs |
| 158 | + - admin — create/revoke API keys |
| 159 | +- Keys stored hashed in the database |
| 160 | +- Admin endpoints (outside any test suite): |
| 161 | +GET /api/v5/admin/api-keys — List keys (admin) |
| 162 | +POST /api/v5/admin/api-keys — Create key (admin), returns the raw token once |
| 163 | +DELETE /api/v5/admin/api-keys/{prefix} — Revoke key (admin) |
| 164 | + |
| 165 | +R8: Testing |
| 166 | + |
| 167 | +- All API endpoints must have automated tests |
| 168 | +- Use current best practices for Flask API testing (e.g. pytest with Flask test client, or similar) |
| 169 | +- Tests should cover: happy paths, error cases, authentication/authorization, pagination, filtering |
| 170 | + |
| 171 | +R9: Not in Scope (Deferred) |
| 172 | + |
| 173 | +- Webhooks / change notifications |
| 174 | +- Bulk/batch query endpoints |
| 175 | +- Multi-database support |
| 176 | +- Rate limiting |
| 177 | +- Run comparison / derived analytics endpoints |
| 178 | +- Report endpoints (daily, summary, latest runs) |
| 179 | +- Machine merge |
0 commit comments