Skip to content

Commit 74395ab

Browse files
Acruve15fe51
andauthored
feat: add limit/offset pagination to /alerts/unlabeled/latest (#588)
* feat: add limit/offset pagination to /alerts/unlabeled/latest Replaces the hardcoded .limit(15) on GET /api/v1/alerts/unlabeled/latest with optional limit and offset query parameters, matching the convention already used by /alerts/all/fromdate. Default limit=15 preserves prior behavior; clients can now paginate or request more alerts. Closes #587 * Update src/app/api/api_v1/endpoints/alerts.py Co-authored-by: fe51 <55736935+fe51@users.noreply.github.com> --------- Co-authored-by: fe51 <55736935+fe51@users.noreply.github.com>
1 parent 077eeda commit 74395ab

2 files changed

Lines changed: 32 additions & 1 deletion

File tree

src/app/api/api_v1/endpoints/alerts.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ async def fetch_alert_sequences(
208208
summary="Fetch all the alerts with unlabeled sequences from the last 24 hours",
209209
)
210210
async def fetch_latest_unlabeled_alerts(
211+
limit: Union[int, None] = Query(15, ge=1, description="Maximum number of alerts to fetch"),
212+
offset: Union[int, None] = Query(0, description="Number of alerts to skip before starting to fetch"),
211213
risk_score: Union[FwiClass, None] = Query(
212214
None, description="Override FWI class applied to every sequence; bypasses risk-api lookup."
213215
),
@@ -236,7 +238,8 @@ async def fetch_latest_unlabeled_alerts(
236238
.where(Alert.organization_id == token_payload.organization_id)
237239
.where(cast(Any, Alert.id).in_(seq_match))
238240
.order_by(Alert.started_at.desc()) # type: ignore[attr-defined]
239-
.limit(15)
241+
.limit(limit)
242+
.offset(offset)
240243
)
241244
alerts = list((await session.exec(alerts_stmt)).all())
242245
alert_ids = [alert.id for alert in alerts]

src/tests/endpoints/test_alerts.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,34 @@ async def test_alerts_unlabeled_latest(async_client: AsyncClient, detection_sess
154154
assert any(seq["detections_count"] == 0 for seq in returned["sequences"])
155155

156156

157+
@pytest.mark.asyncio
158+
async def test_alerts_unlabeled_latest_pagination(async_client: AsyncClient, detection_session: AsyncSession):
159+
alert_a, _, _ = await _create_alert_with_sequences(detection_session, org_id=1, camera_id=1, lat=48.0, lon=2.0)
160+
alert_b, _, _ = await _create_alert_with_sequences(detection_session, org_id=1, camera_id=1, lat=48.1, lon=2.1)
161+
162+
auth = pytest.get_token(
163+
pytest.user_table[0]["id"], pytest.user_table[0]["role"].split(), pytest.user_table[0]["organization_id"]
164+
)
165+
166+
resp = await async_client.get("/alerts/unlabeled/latest?limit=10&offset=0", headers=auth)
167+
assert resp.status_code == 200, resp.text
168+
full = resp.json()
169+
full_ids = [item["id"] for item in full]
170+
assert {alert_a.id, alert_b.id}.issubset(full_ids)
171+
172+
resp = await async_client.get("/alerts/unlabeled/latest?limit=1&offset=0", headers=auth)
173+
assert resp.status_code == 200, resp.text
174+
page_one = resp.json()
175+
assert len(page_one) == 1
176+
assert page_one[0]["id"] == full_ids[0]
177+
178+
resp = await async_client.get("/alerts/unlabeled/latest?limit=1&offset=1", headers=auth)
179+
assert resp.status_code == 200, resp.text
180+
page_two = resp.json()
181+
assert len(page_two) == 1
182+
assert page_two[0]["id"] == full_ids[1]
183+
184+
157185
@pytest.mark.asyncio
158186
async def test_alerts_from_date(async_client: AsyncClient, detection_session: AsyncSession):
159187
alert, seq_ids, detections_count_by_sequence = await _create_alert_with_sequences(

0 commit comments

Comments
 (0)