Skip to content

Commit 6875b85

Browse files
committed
[API] Design and implement the v5 API
Assisted-by: Claude Code
1 parent a86976f commit 6875b85

74 files changed

Lines changed: 16950 additions & 4 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/api.rst

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,27 @@ Accessing Data outside of LNT: REST API
55

66
LNT provides comprehensive REST APIs to access and manage data stored in the LNT database.
77

8+
v5 API (Recommended)
9+
--------------------
10+
11+
The v5 API is the latest REST API for LNT. It provides interactive documentation via
12+
Swagger UI, available at::
13+
14+
http://<server>/api/v5/openapi/swagger-ui
15+
16+
The OpenAPI 3.0 specification is also available in JSON format at::
17+
18+
http://<server>/api/v5/openapi/openapi.json
19+
20+
The v5 API uses Bearer token authentication, natural keys (machine names, test names)
21+
instead of internal IDs, cursor-based pagination, and a standardized error format.
22+
Refer to the Swagger UI for the complete list of endpoints and their parameters.
23+
24+
v4 API (Legacy)
25+
---------------
26+
27+
The v4 API is documented below for reference. New integrations should use the v5 API.
28+
829
Quick Reference
930
---------------
1031

docs/design/v5-api.md

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
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

Comments
 (0)