interloper-api 0.87.0__tar.gz → 0.88.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.
Files changed (25) hide show
  1. {interloper_api-0.87.0 → interloper_api-0.88.0}/PKG-INFO +1 -1
  2. {interloper_api-0.87.0 → interloper_api-0.88.0}/pyproject.toml +1 -1
  3. {interloper_api-0.87.0 → interloper_api-0.88.0}/pyproject.toml.orig +1 -1
  4. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/routes/backfills.py +14 -5
  5. {interloper_api-0.87.0 → interloper_api-0.88.0}/README.md +0 -0
  6. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/__init__.py +0 -0
  7. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/app.py +0 -0
  8. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/dependencies/__init__.py +0 -0
  9. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/dependencies/auth.py +0 -0
  10. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/dependencies/rbac.py +0 -0
  11. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/dependencies/state.py +0 -0
  12. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/notifications/__init__.py +0 -0
  13. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/notifications/invitations.py +0 -0
  14. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/routes/__init__.py +0 -0
  15. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/routes/admin.py +0 -0
  16. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/routes/agent.py +0 -0
  17. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/routes/auth.py +0 -0
  18. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/routes/catalog.py +0 -0
  19. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/routes/components.py +0 -0
  20. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/routes/health.py +0 -0
  21. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/routes/oauth.py +0 -0
  22. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/routes/organisations.py +0 -0
  23. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/routes/runs.py +0 -0
  24. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/routes/tokens.py +0 -0
  25. {interloper_api-0.87.0 → interloper_api-0.88.0}/src/interloper_api/routes/websocket.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: interloper-api
3
- Version: 0.87.0
3
+ Version: 0.88.0
4
4
  Summary: Interloper FastAPI routes
5
5
  Author: Guillaume Onfroy
6
6
  Author-email: Guillaume Onfroy <guillaume@digitlcloud.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "interloper-api"
3
- version = "0.87.0"
3
+ version = "0.88.0"
4
4
  description = "Interloper FastAPI routes"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -3,7 +3,7 @@
3
3
  # ###############
4
4
  [project]
5
5
  name = "interloper-api"
6
- version = "0.87.0"
6
+ version = "0.88.0"
7
7
  description = "Interloper FastAPI routes"
8
8
  readme = "README.md"
9
9
  authors = [{ name = "Guillaume Onfroy", email = "guillaume@digitlcloud.com" }]
@@ -7,7 +7,7 @@ from uuid import UUID
7
7
  from fastapi import APIRouter, HTTPException
8
8
  from interloper.errors import NotFoundError
9
9
  from interloper_db.models import Backfill
10
- from pydantic import BaseModel
10
+ from pydantic import BaseModel, Field
11
11
 
12
12
  from interloper_api.dependencies import (
13
13
  CurrentUserDep,
@@ -40,6 +40,10 @@ class BackfillResponse(BaseModel):
40
40
  The target component's identity is resolved server-side, same contract
41
41
  as ``RunResponse``: the ``component_*`` identity fields are ``None``
42
42
  exactly when the target was deleted.
43
+
44
+ ``run_counts`` is the backfill's partitions per status, each read as its
45
+ stack's latest attempt, so a listing can draw the backfill's progress
46
+ without fetching its runs.
43
47
  """
44
48
 
45
49
  id: UUID
@@ -57,13 +61,16 @@ class BackfillResponse(BaseModel):
57
61
  started_at: str | None = None
58
62
  completed_at: str | None = None
59
63
  created_at: str | None = None
64
+ run_counts: dict[str, int] = Field(default_factory=dict)
60
65
 
61
66
  @classmethod
62
- def from_backfill(cls, backfill: Backfill) -> BackfillResponse:
67
+ def from_backfill(cls, backfill: Backfill, run_counts: dict[str, int] | None = None) -> BackfillResponse:
63
68
  """Convert a DB Backfill to a BackfillResponse.
64
69
 
65
70
  Args:
66
71
  backfill: The DB Backfill row, with its ``target`` relationship loaded.
72
+ run_counts: The backfill's partition count per status; None reads
73
+ as no runs yet.
67
74
 
68
75
  Returns:
69
76
  The response model.
@@ -84,6 +91,7 @@ class BackfillResponse(BaseModel):
84
91
  started_at=str(backfill.started_at) if backfill.started_at else None,
85
92
  completed_at=str(backfill.completed_at) if backfill.completed_at else None,
86
93
  created_at=str(backfill.created_at) if backfill.created_at else None,
94
+ run_counts=run_counts or {},
87
95
  )
88
96
 
89
97
 
@@ -116,7 +124,8 @@ def list_backfills(
116
124
  backfills = store.runs.list_active_backfills(org_id)
117
125
  else:
118
126
  backfills = store.runs.list_backfills(org_id)
119
- return [BackfillResponse.from_backfill(b) for b in backfills]
127
+ counts = store.runs.count_backfill_runs([backfill.id for backfill in backfills])
128
+ return [BackfillResponse.from_backfill(backfill, counts.get(backfill.id)) for backfill in backfills]
120
129
 
121
130
 
122
131
  @router.post("/", status_code=201)
@@ -153,7 +162,7 @@ def create_backfill(
153
162
  )
154
163
  except ValueError as e:
155
164
  raise HTTPException(status_code=400, detail=str(e))
156
- return BackfillResponse.from_backfill(backfill)
165
+ return BackfillResponse.from_backfill(backfill, store.runs.count_backfill_runs([backfill.id]).get(backfill.id))
157
166
 
158
167
 
159
168
  @router.get("/{backfill_id}")
@@ -180,7 +189,7 @@ def get_backfill(
180
189
  except NotFoundError:
181
190
  raise HTTPException(status_code=404, detail=f"Backfill {backfill_id} not found")
182
191
  authorize_org_member(user, backfill.org_id, store, detail=f"Backfill {backfill_id} not found")
183
- return BackfillResponse.from_backfill(backfill)
192
+ return BackfillResponse.from_backfill(backfill, store.runs.count_backfill_runs([backfill_id]).get(backfill_id))
184
193
 
185
194
 
186
195
  @router.post("/{backfill_id}/cancel")