Skip to content

Commit eb824da

Browse files
committed
Improve /events filters and add documentation
Signed-off-by: Denys Fedoryshchenko <denys.f@collabora.com>
1 parent 8ecb176 commit eb824da

2 files changed

Lines changed: 208 additions & 12 deletions

File tree

api/main.py

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -778,15 +778,29 @@ def _build_events_query(query_params: dict) -> tuple:
778778
Returns (recursive, processed_query_params).
779779
Raises HTTPException on validation errors.
780780
"""
781+
# Simple filters: param_name -> query_field
782+
simple_filters = {
783+
'kind': 'data.kind',
784+
'state': 'data.state',
785+
'result': 'data.result',
786+
'op': 'data.op',
787+
'name': 'data.name',
788+
'group': 'data.group',
789+
'owner': 'data.owner',
790+
'channel': 'channel',
791+
}
792+
781793
recursive = query_params.pop('recursive', None)
782794
limit = query_params.pop('limit', None)
783-
kind = query_params.pop('kind', None)
784-
state = query_params.pop('state', None)
785-
result = query_params.pop('result', None)
786795
from_ts = query_params.pop('from', None)
787796
node_id = query_params.pop('node_id', None)
788-
event_id = query_params.pop('id', None)
789-
event_ids = query_params.pop('ids', None)
797+
path = query_params.pop('path', None)
798+
799+
# Apply simple filters
800+
for param, field in simple_filters.items():
801+
value = query_params.pop(param, None)
802+
if value:
803+
query_params[field] = value
790804

791805
if node_id:
792806
if 'data.id' in query_params:
@@ -796,18 +810,16 @@ def _build_events_query(query_params: dict) -> tuple:
796810
)
797811
query_params['data.id'] = node_id
798812

813+
event_id = query_params.pop('id', None)
814+
event_ids = query_params.pop('ids', None)
799815
_parse_event_id_filter(query_params, event_id, event_ids)
800816

801817
if from_ts:
802818
if isinstance(from_ts, str):
803819
from_ts = datetime.fromisoformat(from_ts)
804820
query_params['timestamp'] = {'$gt': from_ts}
805-
if kind:
806-
query_params['data.kind'] = kind
807-
if state:
808-
query_params['data.state'] = state
809-
if result:
810-
query_params['data.result'] = result
821+
if path:
822+
query_params['data.path'] = {'$regex': path}
811823
if limit:
812824
query_params['limit'] = int(limit)
813825

@@ -829,10 +841,16 @@ async def get_events(request: Request):
829841
Get all the matching events otherwise.
830842
Query parameters can be used to filter the events:
831843
- limit: Number of events to return
832-
- from: Start timestamp (unix epoch) to filter events
844+
- from: Start timestamp (unix epoch or ISO format) to filter events
833845
- kind: Event kind to filter events
834846
- state: Event state to filter events
835847
- result: Event result to filter events
848+
- op: Operation type ('created', 'updated')
849+
- name: Node name to filter events
850+
- path: Node path to filter events (regex pattern)
851+
- group: Node group to filter events
852+
- owner: Node owner to filter events
853+
- channel: Pub/sub channel to filter events
836854
- id / ids: Event document id(s) to filter events
837855
- node_id: Node id to filter events (alias for data.id)
838856
- recursive: Retrieve node together with event

doc/api-details.md

Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,3 +941,181 @@ kernelci-api | INFO: 127.0.0.1:35810 - "POST /publish/abc HTTP/1.1" 200 OK
941941
kernelci-api | INFO: 127.0.0.1:35754 - "GET /listen/abc HTTP/1.1" 200 OK
942942
kernelci-api | INFO: 127.0.0.1:36744 - "POST /unsubscribe/abc HTTP/1.1" 200 OK
943943
```
944+
945+
946+
## Events
947+
948+
The `/events` endpoint provides access to the event history stored in MongoDB.
949+
Events are generated when nodes are created or updated, and are stored for a
950+
configurable retention period (default 7 days).
951+
952+
### Event Structure
953+
954+
Each event contains the following fields:
955+
956+
| Field | Type | Description |
957+
|-------|------|-------------|
958+
| `id` | string | Unique event document ID |
959+
| `timestamp` | datetime | When the event was created |
960+
| `sequence_id` | integer | Sequential ID for ordering (used by pub/sub) |
961+
| `channel` | string | Pub/sub channel name (typically "node") |
962+
| `owner` | string | Username of the event publisher |
963+
| `data` | object | Event payload (see below) |
964+
965+
The `data` object contains node information:
966+
967+
| Field | Type | Description |
968+
|-------|------|-------------|
969+
| `op` | string | Operation type: `created` or `updated` |
970+
| `id` | string | Node ID |
971+
| `kind` | string | Node kind (e.g., `checkout`, `kbuild`, `job`, `test`) |
972+
| `name` | string | Node name |
973+
| `path` | array | Node path hierarchy |
974+
| `group` | string | Node group |
975+
| `state` | string | Node state: `running`, `available`, `closing`, `done` |
976+
| `result` | string | Node result: `pass`, `fail`, `skip`, `incomplete`, or `null` |
977+
| `owner` | string | Node owner (username) |
978+
| `data` | object | Node-specific data |
979+
| `is_hierarchy` | boolean | Whether this is a hierarchy update |
980+
981+
### Query Parameters
982+
983+
The `/events` endpoint supports the following query parameters:
984+
985+
| Parameter | Type | Description | Example |
986+
|-----------|------|-------------|---------|
987+
| `limit` | integer | Maximum number of events to return | `limit=100` |
988+
| `from` | datetime | Return events after this timestamp (ISO format or Unix epoch) | `from=2025-01-01T00:00:00` |
989+
| `kind` | string | Filter by node kind | `kind=job` |
990+
| `state` | string | Filter by node state | `state=done` |
991+
| `result` | string | Filter by node result | `result=pass` |
992+
| `op` | string | Filter by operation type | `op=created` |
993+
| `name` | string | Filter by node name (exact match) | `name=baseline-x86` |
994+
| `path` | string | Filter by node path (regex pattern) | `path=.*mainline.*` |
995+
| `group` | string | Filter by node group (exact match) | `group=kunit-x86_64` |
996+
| `owner` | string | Filter by node owner (exact match) | `owner=admin` |
997+
| `channel` | string | Filter by pub/sub channel | `channel=node` |
998+
| `id` | string | Filter by event document ID | `id=507f1f77bcf86cd799439011` |
999+
| `ids` | string | Filter by multiple event IDs (comma-separated) | `ids=id1,id2,id3` |
1000+
| `node_id` | string | Filter by node ID (alias for `data.id`) | `node_id=507f1f77bcf86cd799439011` |
1001+
| `recursive` | boolean | Include full node data with each event | `recursive=true` |
1002+
1003+
> **Note**: When using `recursive=true`, the `limit` parameter is required and must be <= 1000.
1004+
1005+
### Examples
1006+
1007+
**Get all events (limited to default pagination):**
1008+
```
1009+
$ curl http://localhost:8001/latest/events
1010+
```
1011+
1012+
**Get events for completed jobs with passing results:**
1013+
```
1014+
$ curl 'http://localhost:8001/latest/events?kind=job&state=done&result=pass'
1015+
```
1016+
1017+
**Get recently created events (last hour):**
1018+
```
1019+
$ curl 'http://localhost:8001/latest/events?op=created&from=2025-01-10T12:00:00'
1020+
```
1021+
1022+
**Get events for a specific node path pattern (regex):**
1023+
```
1024+
$ curl 'http://localhost:8001/latest/events?path=.*linux-next.*&limit=50'
1025+
```
1026+
1027+
**Get events for a specific group:**
1028+
```
1029+
$ curl 'http://localhost:8001/latest/events?group=kunit-x86_64&state=done'
1030+
```
1031+
1032+
**Get events by owner:**
1033+
```
1034+
$ curl 'http://localhost:8001/latest/events?owner=admin&kind=checkout'
1035+
```
1036+
1037+
**Get events with full node data:**
1038+
```
1039+
$ curl 'http://localhost:8001/latest/events?state=done&result=fail&recursive=true&limit=10'
1040+
```
1041+
1042+
**Get events for a specific node ID:**
1043+
```
1044+
$ curl 'http://localhost:8001/latest/events?node_id=65a1355ee98651d0fe81e412'
1045+
```
1046+
1047+
**Combine multiple filters:**
1048+
```
1049+
$ curl 'http://localhost:8001/latest/events?kind=test&state=done&result=fail&group=kselftest&from=2025-01-01&limit=100'
1050+
```
1051+
1052+
### Sample Response
1053+
1054+
```json
1055+
[
1056+
{
1057+
"id": "65a1355ee98651d0fe81e500",
1058+
"timestamp": "2025-01-12T08:30:00.000000",
1059+
"sequence_id": 12345,
1060+
"channel": "node",
1061+
"owner": "admin",
1062+
"data": {
1063+
"op": "updated",
1064+
"id": "65a1355ee98651d0fe81e412",
1065+
"kind": "test",
1066+
"name": "kselftest-cpufreq",
1067+
"path": ["checkout", "kbuild", "test", "kselftest-cpufreq"],
1068+
"group": "kselftest",
1069+
"state": "done",
1070+
"result": "pass",
1071+
"owner": "admin",
1072+
"data": {
1073+
"kernel_revision": {
1074+
"tree": "mainline",
1075+
"branch": "master",
1076+
"commit": "abc123..."
1077+
}
1078+
},
1079+
"is_hierarchy": false
1080+
}
1081+
}
1082+
]
1083+
```
1084+
1085+
### Response with recursive=true
1086+
1087+
When `recursive=true` is specified, each event includes a `node` field with the
1088+
full node object:
1089+
1090+
```json
1091+
[
1092+
{
1093+
"id": "65a1355ee98651d0fe81e500",
1094+
"timestamp": "2025-01-12T08:30:00.000000",
1095+
"data": {
1096+
"op": "updated",
1097+
"id": "65a1355ee98651d0fe81e412",
1098+
"kind": "test",
1099+
"name": "kselftest-cpufreq",
1100+
"state": "done",
1101+
"result": "pass"
1102+
},
1103+
"node": {
1104+
"id": "65a1355ee98651d0fe81e412",
1105+
"kind": "test",
1106+
"name": "kselftest-cpufreq",
1107+
"path": ["checkout", "kbuild", "test", "kselftest-cpufreq"],
1108+
"group": "kselftest",
1109+
"parent": "65a1355ee98651d0fe81e400",
1110+
"state": "done",
1111+
"result": "pass",
1112+
"artifacts": {...},
1113+
"data": {...},
1114+
"created": "2025-01-12T08:00:00.000000",
1115+
"updated": "2025-01-12T08:30:00.000000",
1116+
"owner": "admin",
1117+
"user_groups": []
1118+
}
1119+
}
1120+
]
1121+
```

0 commit comments

Comments
 (0)