scryfall-py/scryfall/models/api.py

72 lines
2.2 KiB
Python

from typing import Any, Literal
from pydantic import BaseModel, HttpUrl, ValidationError, model_validator
from scryfall.models.base import BaseAPIModel
from scryfall.models.cards import Card
from scryfall.models.rulings import Ruling
from scryfall.models.sets import Set
from scryfall.models.symbols import CardSymbol
CLASS_LOOKUP = {"card": Card, "card_symbol": CardSymbol, "ruling": Ruling, "set": Set}
class APIError(BaseModel):
"""Scryfall API error model."""
status: int
"""HTTP status code"""
code: str
"""HTTP status code text (i.e. not_found)"""
details: str
"""More details"""
type: str | None = None
"""The error type"""
warnings: list[str] | None = None
"""Any associated warnings that are not quite errors"""
class APIList(BaseAPIModel):
"""Scryfall API list model for paginated requests"""
object: Literal["list"]
"""The object type, `list` in this case"""
data: list[Card | CardSymbol | Ruling | Set]
"""The page of data, max 175 items unless it is an entire set of cards"""
has_more: bool
"""If there are more pages"""
next_page: HttpUrl | None = None
"""The URL to the next page"""
total_cards: int | None = None
"""The total number of cards found by the original query"""
warnings: list[str] | None = None
"""Warnings generated by the query that are not quite errors"""
@model_validator(mode="before")
@classmethod
def validate_data(cls, data: Any) -> Any:
if data.get("object") == "list":
for item in data.get("data"):
item["_client"] = data["_client"]
obj = item.get("object")
if obj not in CLASS_LOOKUP:
raise ValidationError("Item missing object field")
item = CLASS_LOOKUP[obj](**item)
return data
async def get_next_page(self) -> "APIList | None":
"""Get the next page of the query"""
if self.has_more and self.next_page is not None:
params = dict(self.next_page.query_params())
params.pop("format", None)
return await self._client.search_cards(**params) # type: ignore
return None