Files
wehub-resource-sync fed8b2eed7
Build and push multi-arch DocsGPT Docker image / build (linux/amd64, ubuntu-latest, amd64) (push) Has been cancelled
Backend release / release (push) Has been cancelled
Bandit Security Scan / bandit_scan (push) Has been cancelled
Build and push multi-arch DocsGPT Docker image / build (linux/arm64, ubuntu-24.04-arm, arm64) (push) Has been cancelled
Build and push multi-arch DocsGPT Docker image / manifest (push) Has been cancelled
Build and push DocsGPT FE Docker image for development / build (linux/amd64, ubuntu-latest, amd64) (push) Has been cancelled
Build and push DocsGPT FE Docker image for development / build (linux/arm64, ubuntu-24.04-arm, arm64) (push) Has been cancelled
Build and push DocsGPT FE Docker image for development / manifest (push) Has been cancelled
Python linting / ruff (push) Has been cancelled
Run python tests with pytest / Run tests and count coverage (3.12) (push) Has been cancelled
React Widget Build / build (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:28:29 +08:00

68 lines
2.4 KiB
Python

"""Common helpers shared by all repositories.
Repositories are thin wrappers around SQLAlchemy Core query construction.
They take a ``Connection`` on call and return plain ``dict`` rows during the
Mongo→Postgres cutover so that call sites don't have to change shape. Once
cutover is complete, a follow-up phase may migrate repo return types to
Pydantic DTOs (tracked in the migration plan as a post-migration item).
"""
import re
from typing import Any, Mapping
from uuid import UUID
from application.storage.db.serialization import coerce_pg_native
_UUID_RE = re.compile(
r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
re.IGNORECASE,
)
def looks_like_uuid(value: Any) -> bool:
"""Return True if ``value`` is a canonical UUID (string or ``UUID`` instance).
Used by ``get_any`` accessors to pick the UUID lookup path vs. the
``legacy_mongo_id`` fallback during the Mongo→PG cutover window.
Accepting ``uuid.UUID`` directly matters for callers that receive an
id straight from a PG column (SQLAlchemy maps ``UUID`` columns to the
Python ``UUID`` type) — without this, the call falls through to the
legacy-text lookup and crashes on ``operator does not exist: text = uuid``.
"""
if isinstance(value, UUID):
return True
return isinstance(value, str) and bool(_UUID_RE.match(value))
def row_to_dict(row: Any) -> dict:
"""Convert a SQLAlchemy ``Row`` to a plain JSON-safe dict.
Normalises PG-native types at the SELECT boundary: UUID, datetime,
date, Decimal, and bytes are coerced to JSON-safe forms via
:func:`coerce_pg_native`. Downstream serialisation (SSE events,
JSONB writes, API responses) becomes safe by default — repository
consumers no longer need to know that PG returns a different type
set than Mongo did.
Also emits ``_id`` alongside ``id`` for the duration of the Mongo→PG
cutover so legacy serializers expecting Mongo's shape keep working.
Args:
row: A SQLAlchemy ``Row`` object, or ``None``.
Returns:
A plain dict, or an empty dict if ``row`` is ``None``.
"""
if row is None:
return {}
# Row has a ``._mapping`` attribute exposing a MappingProxy view.
mapping: Mapping[str, Any] = row._mapping # type: ignore[attr-defined]
out = coerce_pg_native(dict(mapping))
if "id" in out and out["id"] is not None:
out["_id"] = out["id"]
return out