first
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
"""Sklik (Seznam) - JSON-RPC proxy.
|
||||
|
||||
The proxy logs in with X-Sklik-Token per request (client.loginByToken) and then
|
||||
performs the requested call, injecting the session for you. See
|
||||
``app.clients.sklik_client`` and https://api.sklik.cz/drak/ for methods.
|
||||
|
||||
Credentials: X-Sklik-Token (required).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, Header, Path
|
||||
|
||||
from ..clients.sklik_client import SklikClient
|
||||
from ..credentials import get_sklik_token
|
||||
from ..errors import UpstreamError
|
||||
|
||||
router = APIRouter(prefix="/sklik", tags=["sklik"])
|
||||
|
||||
# Entities that expose createReport/readReport for the report helper.
|
||||
_REPORT_ENTITIES = {
|
||||
"campaigns",
|
||||
"groups",
|
||||
"ads",
|
||||
"keywords",
|
||||
"queries",
|
||||
"sitelinks",
|
||||
"productSets",
|
||||
"banners",
|
||||
}
|
||||
|
||||
_REPORT_EXAMPLE = [
|
||||
{"dateFrom": "2026-06-01", "dateTo": "2026-06-18", "statGranularity": "daily"},
|
||||
{"statGranularity": "daily"},
|
||||
]
|
||||
|
||||
|
||||
def _optional_user_id(
|
||||
x_sklik_user_id: str | None = Header(
|
||||
default=None,
|
||||
alias="X-Sklik-User-Id",
|
||||
description="Optional managed account id (userId) to act on behalf of, "
|
||||
"for agency/MCC access.",
|
||||
),
|
||||
) -> int | None:
|
||||
raw = (x_sklik_user_id or "").strip()
|
||||
if not raw:
|
||||
return None
|
||||
try:
|
||||
return int(raw)
|
||||
except ValueError as exc:
|
||||
raise UpstreamError(
|
||||
"X-Sklik-User-Id must be an integer.", status=400
|
||||
) from exc
|
||||
|
||||
|
||||
@router.post("/login", summary="Verify the Sklik token (client.loginByToken)")
|
||||
async def login(
|
||||
token: str = Depends(get_sklik_token),
|
||||
user_id: int | None = Depends(_optional_user_id),
|
||||
) -> dict:
|
||||
"""Check the token works. The session itself is internal and not returned."""
|
||||
async with SklikClient(token, user_id=user_id) as client:
|
||||
payload = await client.login()
|
||||
return {
|
||||
"valid": True,
|
||||
"status": payload.get("status"),
|
||||
"statusMessage": payload.get("statusMessage"),
|
||||
}
|
||||
|
||||
|
||||
@router.get("/limits", summary="API limits and quota (api.limits)")
|
||||
async def limits(
|
||||
token: str = Depends(get_sklik_token),
|
||||
user_id: int | None = Depends(_optional_user_id),
|
||||
) -> Any:
|
||||
async with SklikClient(token, user_id=user_id) as client:
|
||||
return await client.call("api.limits")
|
||||
|
||||
|
||||
@router.post(
|
||||
"/report/{entity}",
|
||||
summary="Create and read a Sklik stats report (createReport + readReport)",
|
||||
)
|
||||
async def report(
|
||||
entity: str = Path(
|
||||
...,
|
||||
description="Entity to report on: "
|
||||
+ ", ".join(sorted(_REPORT_ENTITIES)),
|
||||
),
|
||||
body: list[Any] = Body(
|
||||
...,
|
||||
examples=[_REPORT_EXAMPLE],
|
||||
description="Arguments for {entity}.createReport (restriction filter and "
|
||||
"optional display options). The session is injected automatically.",
|
||||
),
|
||||
token: str = Depends(get_sklik_token),
|
||||
user_id: int | None = Depends(_optional_user_id),
|
||||
) -> Any:
|
||||
if entity not in _REPORT_ENTITIES:
|
||||
raise UpstreamError(
|
||||
f"Unsupported report entity '{entity}'. Allowed: "
|
||||
+ ", ".join(sorted(_REPORT_ENTITIES)),
|
||||
status=400,
|
||||
)
|
||||
async with SklikClient(token, user_id=user_id) as client:
|
||||
return await client.fetch_report(entity, body)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/rpc/{method}",
|
||||
summary="Generic authenticated Sklik call (any method)",
|
||||
)
|
||||
async def rpc(
|
||||
method: str = Path(
|
||||
...,
|
||||
description="Sklik method name, e.g. campaigns.list, groups.list, "
|
||||
"ads.list, api.limits. (client.loginByToken is managed by the proxy.)",
|
||||
),
|
||||
args: list[Any] = Body(
|
||||
default=[],
|
||||
description="Positional arguments AFTER the session struct (which the "
|
||||
"proxy injects as the first argument). Example for campaigns.list: "
|
||||
'[{"statuses": ["active"]}, {"displayColumns": ["id","name"]}]',
|
||||
),
|
||||
token: str = Depends(get_sklik_token),
|
||||
user_id: int | None = Depends(_optional_user_id),
|
||||
) -> Any:
|
||||
async with SklikClient(token, user_id=user_id) as client:
|
||||
return await client.call(method, args)
|
||||
Reference in New Issue
Block a user