mirror of
https://github.com/GSA/notifications-api.git
synced 2026-09-06 20:28:25 -04:00
Added light polling endpoint (#2006)
* Added light polling endpoint * Import linting * CICD checks updated
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
import json
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
import dateutil
|
||||
from flask import Blueprint, current_app, jsonify, request
|
||||
|
||||
from app import db, redis_store
|
||||
from app import db
|
||||
from app.aws.s3 import (
|
||||
extract_personalisation,
|
||||
extract_phones,
|
||||
@@ -32,7 +31,7 @@ from app.dao.notifications_dao import (
|
||||
)
|
||||
from app.dao.services_dao import dao_fetch_service_by_id
|
||||
from app.dao.templates_dao import dao_get_template_by_id
|
||||
from app.enums import JobStatus
|
||||
from app.enums import JobStatus, NotificationStatus
|
||||
from app.errors import InvalidRequest, register_errors
|
||||
from app.schemas import (
|
||||
JobSchema,
|
||||
@@ -65,78 +64,35 @@ def get_job_by_service_and_job_id(service_id, job_id):
|
||||
|
||||
@job_blueprint.route("/<job_id>/status", methods=["GET"])
|
||||
def get_job_status(service_id, job_id):
|
||||
"""Lightweight endpoint for polling job status with aggressive caching."""
|
||||
current_app.logger.info(
|
||||
f"Getting job status for service_id={service_id}, job_id={job_id}"
|
||||
)
|
||||
"""Fast job status endpoint for real-time polling. No S3 calls, no caching."""
|
||||
check_suspicious_id(service_id, job_id)
|
||||
|
||||
# Check cache first (10 second TTL)
|
||||
cache_key = f"job_status:{service_id}:{job_id}"
|
||||
cached_status = redis_store.get(cache_key)
|
||||
|
||||
if cached_status:
|
||||
current_app.logger.info(f"Returning cached status for job {job_id}")
|
||||
return jsonify(json.loads(cached_status))
|
||||
|
||||
job = dao_get_job_by_service_id_and_job_id(service_id, job_id)
|
||||
|
||||
statistics = dao_get_notification_outcomes_for_job(service_id, job_id)
|
||||
|
||||
sent_count = 0
|
||||
failed_count = 0
|
||||
pending_count = 0
|
||||
|
||||
status_mapping = {
|
||||
"sent": ["sent"],
|
||||
"delivered": ["sent"],
|
||||
"failed": [
|
||||
"failed",
|
||||
"permanent-failure",
|
||||
"temporary-failure",
|
||||
"technical-failure",
|
||||
"virus-scan-failed",
|
||||
],
|
||||
"pending": ["created", "sending", "pending", "pending-virus-check"],
|
||||
}
|
||||
delivered_statuses = (NotificationStatus.DELIVERED, NotificationStatus.SENT)
|
||||
failed_statuses = (NotificationStatus.FAILED,) + NotificationStatus.failed_types()
|
||||
|
||||
delivered_count = failed_count = 0
|
||||
for stat in statistics:
|
||||
count = stat[0] if isinstance(stat, tuple) else stat.count
|
||||
status = stat[1] if isinstance(stat, tuple) else stat.status
|
||||
if stat.status in delivered_statuses:
|
||||
delivered_count += stat.count
|
||||
elif stat.status in failed_statuses:
|
||||
failed_count += stat.count
|
||||
|
||||
if status in status_mapping.get(
|
||||
"delivered", []
|
||||
) or status in status_mapping.get("sent", []):
|
||||
sent_count += count
|
||||
elif status in status_mapping.get("failed", []):
|
||||
failed_count += count
|
||||
elif status in status_mapping.get("pending", []):
|
||||
pending_count += count
|
||||
else:
|
||||
if job.processing_finished:
|
||||
sent_count += count
|
||||
else:
|
||||
pending_count += count
|
||||
total_count = job.notification_count or 0
|
||||
pending_calculated = max(0, total_count - delivered_count - failed_count)
|
||||
|
||||
is_finished = job.processing_finished is not None and pending_calculated == 0
|
||||
|
||||
response_data = {
|
||||
"sent_count": sent_count,
|
||||
"failed_count": failed_count,
|
||||
"pending_count": pending_count,
|
||||
"total_count": job.notification_count,
|
||||
"job_status": job.job_status,
|
||||
"processing_finished": job.processing_finished is not None,
|
||||
"total": total_count,
|
||||
"delivered": delivered_count,
|
||||
"failed": failed_count,
|
||||
"pending": pending_calculated,
|
||||
"finished": is_finished,
|
||||
}
|
||||
|
||||
if job.processing_finished:
|
||||
# Finished jobs can be cached for 5 minutes since they won't change
|
||||
cache_ttl = 300
|
||||
else:
|
||||
# Active jobs cached for 10 seconds
|
||||
cache_ttl = 10
|
||||
|
||||
redis_store.set(cache_key, json.dumps(response_data), ex=cache_ttl)
|
||||
current_app.logger.info(f"Cached status for job {job_id} with TTL={cache_ttl}s")
|
||||
|
||||
return jsonify(response_data)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user