diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ebe6764..619c955 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,9 +1,7 @@ name: ci on: - push: - branches: ["**"] - pull_request: + workflow_dispatch: jobs: test: diff --git a/src/sentinel_api/appeals.py b/src/sentinel_api/appeals.py index 5290302..d9ff9fa 100644 --- a/src/sentinel_api/appeals.py +++ b/src/sentinel_api/appeals.py @@ -10,6 +10,7 @@ from typing import Any, Literal, cast, get_args from uuid import uuid4 +from psycopg import sql from pydantic import BaseModel, ConfigDict, Field from sentinel_api.logging import get_logger @@ -461,22 +462,22 @@ def list_appeals( request_id: str | None, limit: int, ) -> AdminAppealListResponse: - where_conditions: list[str] = [] + where_conditions: list[sql.Composable] = [] where_params: list[object] = [] if status is not None: - where_conditions.append("status = %s") + where_conditions.append(sql.SQL("status = %s")) where_params.append(status) if request_id is not None: - where_conditions.append("request_id = %s") + where_conditions.append(sql.SQL("request_id = %s")) where_params.append(request_id) - where_clause = "" + where_clause = sql.SQL("") if where_conditions: - where_clause = "WHERE " + " AND ".join(where_conditions) + where_clause = sql.SQL(" WHERE ") + sql.SQL(" AND ").join(where_conditions) with self._connection() as conn: with conn.cursor() as cur: cur.execute( - f"SELECT COUNT(1) FROM appeals {where_clause}", + sql.SQL("SELECT COUNT(1) FROM appeals") + where_clause, tuple(where_params), ) total_row = cur.fetchone() @@ -484,30 +485,36 @@ def list_appeals( query_params = list(where_params) query_params.append(limit) cur.execute( - f""" - SELECT - id, - status, - request_id, - original_decision_id, - original_action, - original_reason_codes, - original_model_version, - original_lexicon_version, - original_policy_version, - original_pack_versions, - submitted_by, - reviewer_actor, - resolution_code, - resolution_reason_codes, - created_at, - updated_at, - resolved_at - FROM appeals - {where_clause} - ORDER BY created_at DESC, id DESC - LIMIT %s - """, + sql.SQL( + """ + SELECT + id, + status, + request_id, + original_decision_id, + original_action, + original_reason_codes, + original_model_version, + original_lexicon_version, + original_policy_version, + original_pack_versions, + submitted_by, + reviewer_actor, + resolution_code, + resolution_reason_codes, + created_at, + updated_at, + resolved_at + FROM appeals + """ + ) + + where_clause + + sql.SQL( + """ + ORDER BY created_at DESC, id DESC + LIMIT %s + """ + ), tuple(query_params), ) items = [_appeal_from_row(row) for row in cur.fetchall()] diff --git a/src/sentinel_api/partner_connectors.py b/src/sentinel_api/partner_connectors.py index dba14e3..baebdf8 100644 --- a/src/sentinel_api/partner_connectors.py +++ b/src/sentinel_api/partner_connectors.py @@ -196,6 +196,7 @@ def fetch_signals( retry_delays: list[int] = [] attempts = 0 + last_error = "unknown error" def _sleep(seconds: float) -> None: self._sleep_fn(int(seconds)) diff --git a/src/sentinel_api/result_cache.py b/src/sentinel_api/result_cache.py index 78e5929..101f907 100644 --- a/src/sentinel_api/result_cache.py +++ b/src/sentinel_api/result_cache.py @@ -48,7 +48,9 @@ def get_cached_result(cache_key: str, redis_url: str) -> ModerationResponse | No client = redis.Redis.from_url(redis_url, decode_responses=True) cached = client.get(cache_key) - if not cached: + if cached is None: + return None + if not isinstance(cached, str): return None return ModerationResponse.model_validate_json(cached) except Exception as exc: