Stage 239: per-delivery retry (infra/api/app.py)
Browse files- infra/api/app.py +50 -0
infra/api/app.py
CHANGED
|
@@ -3533,6 +3533,56 @@ def create_app(db_path: Optional[str] = None,
|
|
| 3533 |
# Stage 168 — delivery log for a single webhook. Read-only,
|
| 3534 |
# any tenant role can see it (debugging "why didn't I get the
|
| 3535 |
# Slack ping" needs analyst-tier access, not operator).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3536 |
@app.get("/webhooks/{webhook_id}/deliveries", tags=["webhooks"])
|
| 3537 |
async def list_webhook_deliveries_route(
|
| 3538 |
webhook_id: str,
|
|
|
|
| 3533 |
# Stage 168 — delivery log for a single webhook. Read-only,
|
| 3534 |
# any tenant role can see it (debugging "why didn't I get the
|
| 3535 |
# Slack ping" needs analyst-tier access, not operator).
|
| 3536 |
+
# Stage 239 — tenant-wide delivery list (cross-webhook). Used
|
| 3537 |
+
# by the Webhooks page "failed deliveries" section.
|
| 3538 |
+
@app.get("/tenants/{tenant_id}/webhook_deliveries",
|
| 3539 |
+
tags=["webhooks"])
|
| 3540 |
+
async def list_tenant_webhook_deliveries_route(
|
| 3541 |
+
tenant_id: str,
|
| 3542 |
+
status: Optional[str] = Query(default=None),
|
| 3543 |
+
limit: int = Query(100, ge=1, le=500),
|
| 3544 |
+
key: ApiKey = Depends(auth_dep),
|
| 3545 |
+
):
|
| 3546 |
+
require_tenant_access(key, tenant_id)
|
| 3547 |
+
require_role(key, ROLE_READONLY)
|
| 3548 |
+
try:
|
| 3549 |
+
return {
|
| 3550 |
+
"deliveries": svc.list_tenant_webhook_deliveries(
|
| 3551 |
+
tenant_id, status=status, limit=limit,
|
| 3552 |
+
),
|
| 3553 |
+
}
|
| 3554 |
+
except ValueError as e:
|
| 3555 |
+
raise ApiError("bad_request", str(e), status=400) from e
|
| 3556 |
+
|
| 3557 |
+
# Stage 239 — manual retry of one specific delivery.
|
| 3558 |
+
@app.post("/webhook_deliveries/{delivery_id}/retry",
|
| 3559 |
+
tags=["webhooks"])
|
| 3560 |
+
async def retry_one_delivery_route(
|
| 3561 |
+
delivery_id: str,
|
| 3562 |
+
authorization: Optional[str] = Header(default=None),
|
| 3563 |
+
):
|
| 3564 |
+
# Resolve the delivery's tenant before auth so a
|
| 3565 |
+
# forged delivery_id doesn't sneak through.
|
| 3566 |
+
delivery = svc.webhook_deliveries.get(delivery_id)
|
| 3567 |
+
if delivery is None:
|
| 3568 |
+
raise ApiError(
|
| 3569 |
+
"not_found",
|
| 3570 |
+
f"unknown delivery {delivery_id!r}", status=404,
|
| 3571 |
+
)
|
| 3572 |
+
key = require_tenant_or_admin(
|
| 3573 |
+
svc, authorization, delivery["tenant_id"],
|
| 3574 |
+
)
|
| 3575 |
+
if key is not None:
|
| 3576 |
+
require_role(key, ROLE_OPERATOR)
|
| 3577 |
+
actor = (key.key_id if key is not None
|
| 3578 |
+
else _admin_actor(authorization))
|
| 3579 |
+
try:
|
| 3580 |
+
return svc.retry_one_delivery(delivery_id, actor=actor)
|
| 3581 |
+
except KeyError as e:
|
| 3582 |
+
raise ApiError("not_found", str(e), status=404) from e
|
| 3583 |
+
except ValueError as e:
|
| 3584 |
+
raise ApiError("bad_request", str(e), status=400) from e
|
| 3585 |
+
|
| 3586 |
@app.get("/webhooks/{webhook_id}/deliveries", tags=["webhooks"])
|
| 3587 |
async def list_webhook_deliveries_route(
|
| 3588 |
webhook_id: str,
|