"""SQL implementation of PageConfig service methods. Extracted from blog/bp/data/routes.py and blog/bp/actions/routes.py to enable sx defquery/defaction conversion. """ from __future__ import annotations from typing import Any from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm.attributes import flag_modified from shared.models.page_config import PageConfig def _to_dict(pc: PageConfig) -> dict[str, Any]: return { "id": pc.id, "container_type": pc.container_type, "container_id": pc.container_id, "features": pc.features or {}, "sumup_merchant_code": pc.sumup_merchant_code, "sumup_api_key": pc.sumup_api_key, "sumup_checkout_prefix": pc.sumup_checkout_prefix, } class SqlPageConfigService: async def ensure( self, session: AsyncSession, *, container_type: str = "page", container_id: int, ) -> dict[str, Any]: """Get or create a PageConfig. Returns minimal dict with id.""" row = (await session.execute( select(PageConfig).where( PageConfig.container_type == container_type, PageConfig.container_id == container_id, ) )).scalar_one_or_none() if row is None: row = PageConfig( container_type=container_type, container_id=container_id, features={}, ) session.add(row) await session.flush() return { "id": row.id, "container_type": row.container_type, "container_id": row.container_id, } async def get_by_container( self, session: AsyncSession, *, container_type: str = "page", container_id: int, ) -> dict[str, Any] | None: pc = (await session.execute( select(PageConfig).where( PageConfig.container_type == container_type, PageConfig.container_id == container_id, ) )).scalar_one_or_none() return _to_dict(pc) if pc else None async def get_by_id( self, session: AsyncSession, *, id: int, ) -> dict[str, Any] | None: pc = await session.get(PageConfig, id) return _to_dict(pc) if pc else None async def get_batch( self, session: AsyncSession, *, container_type: str = "page", ids: list[int], ) -> list[dict[str, Any]]: if not ids: return [] result = await session.execute( select(PageConfig).where( PageConfig.container_type == container_type, PageConfig.container_id.in_(ids), ) ) return [_to_dict(pc) for pc in result.scalars().all()] async def update( self, session: AsyncSession, *, container_type: str = "page", container_id: int, features: dict | None = None, sumup_merchant_code: str | None = None, sumup_checkout_prefix: str | None = None, sumup_api_key: str | None = None, ) -> dict[str, Any]: pc = (await session.execute( select(PageConfig).where( PageConfig.container_type == container_type, PageConfig.container_id == container_id, ) )).scalar_one_or_none() if pc is None: pc = PageConfig( container_type=container_type, container_id=container_id, features=features or {}, ) session.add(pc) await session.flush() if features is not None: merged = dict(pc.features or {}) for key, val in features.items(): if isinstance(val, bool): merged[key] = val elif val in ("true", "1", "on"): merged[key] = True elif val in ("false", "0", "off", None): merged[key] = False pc.features = merged flag_modified(pc, "features") if sumup_merchant_code is not None: pc.sumup_merchant_code = sumup_merchant_code or None if sumup_checkout_prefix is not None: pc.sumup_checkout_prefix = sumup_checkout_prefix or None if sumup_api_key is not None: pc.sumup_api_key = sumup_api_key or None await session.flush() result = _to_dict(pc) result["sumup_configured"] = bool(pc.sumup_api_key) return result