Skip to content

Commit

Permalink
Rewrite graphql core to use pydantic
Browse files Browse the repository at this point in the history
  • Loading branch information
koldakov committed Jan 10, 2024
1 parent e646df5 commit d5f7067
Showing 1 changed file with 56 additions and 70 deletions.
126 changes: 56 additions & 70 deletions app/graphql/schemas.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
from datetime import date, datetime
from typing import List, Optional
from uuid import UUID
from typing import List

from fastapi_storages.base import StorageImage
import strawberry

from app.repositories.models import (
Expand All @@ -17,75 +14,66 @@
SeasonDoesNotExist,
)
from app.repositories.sessions import get_async_session_ctx
from app.services.characters import build_url


_Status = strawberry.enum(CharacterStatus)
_Gender = strawberry.enum(CharacterGender)
_Species = strawberry.enum(CharacterSpecies)


@strawberry.type
class CharacterResponse:
id: int
name: str
status: _Status
created_at: datetime
gender: _Gender
species: _Species
uuid: UUID
image: Optional[str]

from app.services.base import EpisodeBase as EpisodeBaseSchema
from app.services.characters import Character as CharacterSchema
from app.services.episodes import (
Episode as EpisodeSchema,
SeasonEpisode as SeasonEpisodeSchema,
)
from app.services.seasons import (
Season as SeasonSchema,
EpisodeSeason as EpisodeSeasonSchema,
)

@strawberry.type
class SeasonBase:
id: int
created_at: datetime
uuid: UUID

@strawberry.experimental.pydantic.type(model=CharacterSchema)
class Character:
id: strawberry.auto
name: strawberry.auto
gender: strawberry.enum(CharacterGender)
status: strawberry.enum(CharacterStatus)
species: strawberry.enum(CharacterSpecies)
created_at: strawberry.auto
image: strawberry.auto

@strawberry.type
class EpisodeSeason(SeasonBase):
"""Season model for episode response.

We don't need to return episodes in season on episode request.
"""
@strawberry.experimental.pydantic.type(model=SeasonEpisodeSchema, all_fields=True)
class SeasonEpisode:
...


@strawberry.type
@strawberry.experimental.pydantic.type(model=EpisodeBaseSchema, all_fields=True)
class EpisodeBase:
id: int
name: str
created_at: datetime
uuid: UUID
air_date: Optional[date]
duration: Optional[int]
production_code: str
broadcast_number: int
...


@strawberry.type
class EpisodeResponse(EpisodeBase):
season: EpisodeSeason
@strawberry.experimental.pydantic.type(model=EpisodeSchema)
class Episode(EpisodeBase):
air_date: strawberry.auto
duration: strawberry.auto
created_at: strawberry.auto
season: SeasonEpisode
broadcast_code: str


@strawberry.type
class SeasonEpisode(EpisodeBase):
"""Episode model for season response.
@strawberry.experimental.pydantic.type(model=EpisodeSeasonSchema)
class EpisodeSeason(EpisodeBase):
id: strawberry.auto
name: strawberry.auto
broadcast_number: strawberry.auto
production_code: strawberry.auto

We don't need to return season in episode on season request.
"""


@strawberry.type
class SeasonResponse(SeasonBase):
episodes: List[SeasonEpisode]
@strawberry.experimental.pydantic.type(model=SeasonSchema)
class Season:
id: strawberry.auto
episodes: List[EpisodeSeason]


@strawberry.type
class Query:
@strawberry.field
async def character(self, character_id: int) -> Optional[CharacterResponse]:
@strawberry.field()
async def character(self, character_id: int) -> Character | None:
async with get_async_session_ctx() as session:
try:
character: CharacterModel = await CharacterModel.get(
Expand All @@ -94,27 +82,25 @@ async def character(self, character_id: int) -> Optional[CharacterResponse]:
)
except CharacterDoesNotExist:
return None
data: dict = character.to_dict()
image: StorageImage = data.pop("image")
return CharacterResponse(
**data,
image=build_url(path=image._name), # noqa
)

@strawberry.field
async def episode(self, episode_id: int) -> Optional[EpisodeResponse]:
return Character.from_pydantic(CharacterSchema.model_validate(character))

@strawberry.field()
async def episode(self, episode_id: int) -> Episode | None:
async with get_async_session_ctx() as session:
try:
episode: EpisodeModel = await EpisodeModel.get(session, episode_id)
episode: EpisodeModel = await EpisodeModel.get(
session,
episode_id,
)
except EpisodeDoesNotExist:
return None
return EpisodeResponse(**episode.to_dict(exclude=["season_id"]))
return Episode.from_pydantic(EpisodeSchema.model_validate(episode))

@strawberry.field
async def season(self, season_id: int) -> Optional[SeasonResponse]:
@strawberry.field()
async def season(self, season_id: int) -> Season | None:
async with get_async_session_ctx() as session:
try:
season: SeasonModel = await SeasonModel.get(session, season_id)
except SeasonDoesNotExist:
return None
return SeasonResponse(**season.to_dict())
return Season.from_pydantic(SeasonSchema.model_validate(season))

0 comments on commit d5f7067

Please sign in to comment.