interloper-api 0.84.0__tar.gz → 0.86.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {interloper_api-0.84.0 → interloper_api-0.86.0}/PKG-INFO +1 -1
- {interloper_api-0.84.0 → interloper_api-0.86.0}/pyproject.toml +1 -1
- {interloper_api-0.84.0 → interloper_api-0.86.0}/pyproject.toml.orig +1 -1
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/routes/runs.py +32 -5
- {interloper_api-0.84.0 → interloper_api-0.86.0}/README.md +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/__init__.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/app.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/dependencies/__init__.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/dependencies/auth.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/dependencies/rbac.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/dependencies/state.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/notifications/__init__.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/notifications/invitations.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/routes/__init__.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/routes/admin.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/routes/agent.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/routes/auth.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/routes/backfills.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/routes/catalog.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/routes/components.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/routes/health.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/routes/oauth.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/routes/organisations.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/routes/tokens.py +0 -0
- {interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/routes/websocket.py +0 -0
|
@@ -10,7 +10,7 @@ from fastapi import APIRouter, HTTPException, Query, Response
|
|
|
10
10
|
from interloper.errors import NotFoundError
|
|
11
11
|
from interloper_db import Profile, Store
|
|
12
12
|
from interloper_db.models import Event, Execution, Run
|
|
13
|
-
from pydantic import BaseModel
|
|
13
|
+
from pydantic import BaseModel, Field
|
|
14
14
|
|
|
15
15
|
from interloper_api.dependencies import (
|
|
16
16
|
CurrentUserDep,
|
|
@@ -40,6 +40,15 @@ class RunResponse(BaseModel):
|
|
|
40
40
|
target kinds they do not know about. All three ``component_*`` identity
|
|
41
41
|
fields are ``None`` exactly when the target was deleted
|
|
42
42
|
(``component_id`` nulls on deletion).
|
|
43
|
+
|
|
44
|
+
A response is one attempt. In a stack-native listing it is the stack's
|
|
45
|
+
latest, so ``attempt`` is also how many attempts the stack took; fetching
|
|
46
|
+
an older attempt by id gives that attempt's own number. ``root_run_id``
|
|
47
|
+
is what groups them, and lists them through ``?root_run_id=``.
|
|
48
|
+
|
|
49
|
+
``execution_counts`` is the attempt's own operation executions per status
|
|
50
|
+
(a failed-scope retry only re-executes what had not succeeded), so a
|
|
51
|
+
listing can draw each attempt's progress without fetching its executions.
|
|
43
52
|
"""
|
|
44
53
|
|
|
45
54
|
id: UUID
|
|
@@ -52,18 +61,23 @@ class RunResponse(BaseModel):
|
|
|
52
61
|
partition_key: str | None
|
|
53
62
|
status: str
|
|
54
63
|
retry_of: UUID | None = None
|
|
64
|
+
root_run_id: UUID | None = None
|
|
55
65
|
attempt: int = 1
|
|
56
66
|
retry_scope: str | None = None
|
|
67
|
+
scheduled_for: str | None = None
|
|
57
68
|
started_at: str | None = None
|
|
58
69
|
completed_at: str | None = None
|
|
59
70
|
created_at: str | None = None
|
|
71
|
+
execution_counts: dict[str, int] = Field(default_factory=dict)
|
|
60
72
|
|
|
61
73
|
@classmethod
|
|
62
|
-
def from_run(cls, run: Run) -> RunResponse:
|
|
74
|
+
def from_run(cls, run: Run, execution_counts: dict[str, int] | None = None) -> RunResponse:
|
|
63
75
|
"""Convert a DB Run to a RunResponse.
|
|
64
76
|
|
|
65
77
|
Args:
|
|
66
78
|
run: The DB Run row, with its ``target`` relationship loaded.
|
|
79
|
+
execution_counts: The run's execution count per status; None (a
|
|
80
|
+
run just queued) reads as no executions yet.
|
|
67
81
|
|
|
68
82
|
Returns:
|
|
69
83
|
The response model.
|
|
@@ -79,11 +93,14 @@ class RunResponse(BaseModel):
|
|
|
79
93
|
partition_key=run.partition_key,
|
|
80
94
|
status=run.status,
|
|
81
95
|
retry_of=run.retry_of,
|
|
96
|
+
root_run_id=run.root_run_id,
|
|
82
97
|
attempt=run.attempt,
|
|
83
98
|
retry_scope=run.retry_scope,
|
|
99
|
+
scheduled_for=str(run.scheduled_for) if run.scheduled_for else None,
|
|
84
100
|
started_at=str(run.started_at) if run.started_at else None,
|
|
85
101
|
completed_at=str(run.completed_at) if run.completed_at else None,
|
|
86
102
|
created_at=str(run.created_at) if run.created_at else None,
|
|
103
|
+
execution_counts=execution_counts or {},
|
|
87
104
|
)
|
|
88
105
|
|
|
89
106
|
|
|
@@ -222,10 +239,16 @@ def list_runs(
|
|
|
222
239
|
q: str | None = None,
|
|
223
240
|
component_kind: str | None = None,
|
|
224
241
|
component_key: str | None = None,
|
|
242
|
+
root_run_id: UUID | None = None,
|
|
225
243
|
limit: int = 50,
|
|
226
244
|
offset: int = 0,
|
|
227
245
|
) -> list[RunResponse]:
|
|
228
|
-
"""List
|
|
246
|
+
"""List one row per run stack, or one stack's attempts.
|
|
247
|
+
|
|
248
|
+
A stack is one piece of work, so a listing carries its latest attempt and
|
|
249
|
+
every filter reads that attempt: a stack whose first attempt failed and
|
|
250
|
+
whose second succeeded is a success. Passing ``root_run_id`` asks for one
|
|
251
|
+
stack's attempts instead, newest first.
|
|
229
252
|
|
|
230
253
|
``after``/``before`` bound the runs to those whose execution overlaps that
|
|
231
254
|
window — a run occupies ``started_at`` → ``completed_at`` (open-ended while
|
|
@@ -247,6 +270,7 @@ def list_runs(
|
|
|
247
270
|
component_kind: Keep only runs targeting a component of this kind; None applies no filter.
|
|
248
271
|
component_key: Keep only runs targeting a component of this type (catalog key); None
|
|
249
272
|
applies no filter.
|
|
273
|
+
root_run_id: List this stack's attempts rather than one row per stack.
|
|
250
274
|
limit: Maximum number of runs on the page.
|
|
251
275
|
offset: Number of matching runs to skip before the page starts.
|
|
252
276
|
user: The authenticated user, required to hold at least the ``viewer`` role.
|
|
@@ -266,6 +290,7 @@ def list_runs(
|
|
|
266
290
|
q=q,
|
|
267
291
|
component_kind=component_kind,
|
|
268
292
|
component_key=component_key,
|
|
293
|
+
root_run_id=root_run_id,
|
|
269
294
|
)
|
|
270
295
|
response.headers["X-Total-Count"] = str(total)
|
|
271
296
|
runs = store.runs.list_all(
|
|
@@ -278,10 +303,12 @@ def list_runs(
|
|
|
278
303
|
q=q,
|
|
279
304
|
component_kind=component_kind,
|
|
280
305
|
component_key=component_key,
|
|
306
|
+
root_run_id=root_run_id,
|
|
281
307
|
limit=limit,
|
|
282
308
|
offset=offset,
|
|
283
309
|
)
|
|
284
|
-
|
|
310
|
+
counts = store.events.count_executions([run.id for run in runs])
|
|
311
|
+
return [RunResponse.from_run(run, counts.get(run.id)) for run in runs]
|
|
285
312
|
|
|
286
313
|
|
|
287
314
|
@router.post("/", status_code=201)
|
|
@@ -348,7 +375,7 @@ def get_run(
|
|
|
348
375
|
The run, as a response model.
|
|
349
376
|
"""
|
|
350
377
|
run = _load_authorized_run(run_id, user, store)
|
|
351
|
-
return RunResponse.from_run(run)
|
|
378
|
+
return RunResponse.from_run(run, store.events.count_executions([run_id]).get(run_id))
|
|
352
379
|
|
|
353
380
|
|
|
354
381
|
@router.get("/{run_id}/executions")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/notifications/__init__.py
RENAMED
|
File without changes
|
{interloper_api-0.84.0 → interloper_api-0.86.0}/src/interloper_api/notifications/invitations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|