Skip to content

Commit

Permalink
Add manga status
Browse files Browse the repository at this point in the history
  • Loading branch information
ThirVondukr committed Feb 27, 2024
1 parent 2b3e4dc commit 444289f
Show file tree
Hide file tree
Showing 17 changed files with 154 additions and 8 deletions.
12 changes: 10 additions & 2 deletions schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ type InvalidCredentialsError implements Error {
}

enum LanguageEnum {
eng
ukr
ENG
UKR
}

type Manga {
Expand All @@ -70,6 +70,7 @@ union MangaCreateError = ValidationErrors

input MangaCreateInput {
title: String!
status: MangaStatus!
}

type MangaCreatePayload {
Expand All @@ -91,6 +92,13 @@ type MangaPagePaginationResult {
pageInfo: PagePaginationInfo!
}

enum MangaStatus {
ONGOING
COMPLETED
CANCELLED
ON_HOLD
}

type MangaTag {
id: ID!
name: String!
Expand Down
7 changes: 7 additions & 0 deletions src/app/adapters/graphql/apps/branches/mutations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import strawberry


@strawberry.type
class MangaBranchMutation:
async def create(self) -> None:
pass
Empty file.
22 changes: 22 additions & 0 deletions src/app/adapters/graphql/apps/branches/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from typing import Self

import strawberry

from app.adapters.graphql.dto import DTOMixin
from app.adapters.graphql.types import LanguageGQL
from app.db.models import MangaBranch


@strawberry.type(name="MangaBranch")
class MangaBranchGQL(DTOMixin[MangaBranch]):
id: strawberry.ID
name: str
language: LanguageGQL

@classmethod
def from_dto(cls, model: MangaBranch) -> Self:
return cls(
id=strawberry.ID(str(model)),
name=model.name,
language=model.language,
)
3 changes: 3 additions & 0 deletions src/app/adapters/graphql/apps/manga/input.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import strawberry

from app.adapters.graphql.types import MangaStatusGQL
from app.core.domain.manga.dto import MangaCreateDTO
from app.core.domain.manga.filters import MangaFilter, TagFilter

Expand Down Expand Up @@ -34,8 +35,10 @@ def to_dto(self) -> MangaFilter:
@strawberry.federation.input(name="MangaCreateInput")
class MangaCreateInput:
title: str
status: MangaStatusGQL

def to_dto(self) -> MangaCreateDTO:
return MangaCreateDTO(
title=self.title,
status=self.status,
)
13 changes: 13 additions & 0 deletions src/app/adapters/graphql/schema.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import strawberry
from aioinject.ext.strawberry import AioInjectExtension
from strawberry import Schema
from strawberry.enum import EnumDefinition, EnumValue
from strawberry.extensions import ParserCache, ValidationCache
from strawberry.schema.config import StrawberryConfig
from strawberry.schema.name_converter import NameConverter
from strawberry.tools import merge_types

from app.adapters.graphql.apps.auth.mutation import AuthMutationsGQL
Expand Down Expand Up @@ -32,6 +35,15 @@ async def groups(self) -> GroupMutationsGQL:
return GroupMutationsGQL()


class CustomNameConverter(NameConverter):
def from_enum_value(
self,
enum: EnumDefinition, # noqa: ARG002
enum_value: EnumValue,
) -> str:
return enum_value.name.upper()


schema = Schema(
query=Query,
mutation=Mutation,
Expand All @@ -40,4 +52,5 @@ async def groups(self) -> GroupMutationsGQL:
ValidationCache(maxsize=128),
AioInjectExtension(container=create_container()),
],
config=StrawberryConfig(name_converter=CustomNameConverter()),
)
3 changes: 2 additions & 1 deletion src/app/adapters/graphql/types.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import strawberry

from lib.types import Language
from lib.types import Language, MangaStatus

LanguageGQL = strawberry.enum(Language, name="LanguageEnum")
MangaStatusGQL = strawberry.enum(MangaStatus, name="MangaStatus")
2 changes: 2 additions & 0 deletions src/app/core/domain/manga/dto.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
from pydantic import Field

from lib.dto import BaseDTO
from lib.types import MangaStatus
from lib.validators import StripWhitespace


class MangaCreateDTO(BaseDTO):
title: Annotated[str, Field(max_length=250), StripWhitespace]
status: MangaStatus
1 change: 1 addition & 0 deletions src/app/core/domain/manga/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def __init__(
async def create(self, dto: MangaCreateDTO) -> Manga:
model = Manga(
title=dto.title,
status=dto.status,
title_slug=slugify(dto.title),
)
self._db_context.add(model)
Expand Down
3 changes: 3 additions & 0 deletions src/app/db/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
from uuid_utils.compat import uuid7

from app.core.domain.const import GENERIC_NAME_LENGTH
from app.db._types import IntEnumType
from lib.time import utc_now
from lib.types import MangaStatus

meta = MetaData(
naming_convention={
Expand Down Expand Up @@ -71,5 +73,6 @@ class Base(DeclarativeBase):
type_annotation_map={
datetime: DateTime(timezone=True),
# enum.Enum: Enum(native_enum=False),
MangaStatus: IntEnumType(MangaStatus),
},
)
32 changes: 32 additions & 0 deletions src/app/db/_types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from enum import IntEnum
from typing import TypeVar

from sqlalchemy import Dialect, Integer, TypeDecorator

TEnum = TypeVar("TEnum", bound=IntEnum)


class IntEnumType(TypeDecorator[TEnum]):
impl = Integer

def __init__(self, enum: type[TEnum]) -> None:
super().__init__()
self.enum = enum

def process_bind_param(
self,
value: TEnum | None,
dialect: Dialect, # noqa: ARG002
) -> int | None:
if value is None:
return None
return value.value

def process_result_value(
self,
value: int | None,
dialect: Dialect, # noqa: ARG002
) -> TEnum | None:
if value is None:
return value
return self.enum(value)
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""
Add manga status
Revision ID: 01aa09cf16f4
Revises: d8d2a40af9fb
Create Date: 2024-02-27 23:51:58.269706
"""

import sqlalchemy as sa
from alembic import op
from sqlalchemy import Integer

# revision identifiers, used by Alembic.
revision = "01aa09cf16f4"
down_revision: str | None = "d8d2a40af9fb"
branch_labels: str | None = None
depends_on: str | None = None


def upgrade() -> None:
op.add_column(
"manga",
sa.Column("status", Integer, nullable=True),
)
op.execute("update manga set status = 0;")
op.alter_column("manga", "status", nullable=False)


def downgrade() -> None:
op.drop_column("manga", "status")
3 changes: 2 additions & 1 deletion src/app/db/models/_manga.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
PkUUID,
str_title,
)
from lib.types import Language
from lib.types import Language, MangaStatus

manga_manga_tag_secondary_table = Table(
"manga__manga_tag__secondary",
Expand Down Expand Up @@ -67,6 +67,7 @@ class Manga(

title: Mapped[str_title] = mapped_column(unique=True)
title_slug: Mapped[str_title] = mapped_column(unique=True)
status: Mapped[MangaStatus]

branches: Mapped[list[MangaBranch]] = relationship(
back_populates="manga",
Expand Down
7 changes: 7 additions & 0 deletions src/lib/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,10 @@
class Language(enum.Enum):
eng = "eng"
ukr = "ukr"


class MangaStatus(enum.IntEnum):
ongoing = 0
completed = enum.auto()
cancelled = enum.auto()
on_hold = enum.auto()
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ async def test_ok(
{
"__typename": "AltTitle",
"id": str(alt_title.id),
"language": alt_title.language.name,
"language": alt_title.language.name.upper(),
"title": alt_title.title,
}
for alt_title in manga.alt_titles
Expand Down
18 changes: 16 additions & 2 deletions tests/adapters/graphql/manga/test_create.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import random

import pytest
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.db.models import Manga
from lib.types import MangaStatus
from tests.adapters.graphql.client import GraphQLClient

pytestmark = [pytest.mark.usefixtures("session")]
Expand Down Expand Up @@ -37,13 +40,21 @@ def _tpl(manga: object, error: object) -> object:
}


@pytest.fixture
def manga_status() -> MangaStatus:
return random.choice(list(MangaStatus.__members__.values()))


async def test_validation(
graphql_client: GraphQLClient,
manga_status: MangaStatus,
) -> None:
title = "a" * 251
response = await graphql_client.query(
query=QUERY,
variables={"input": {"title": title}},
variables={
"input": {"title": title, "status": manga_status.name.upper()},
},
)
assert response == _tpl(
manga=None,
Expand All @@ -54,11 +65,14 @@ async def test_validation(
async def test_create(
session: AsyncSession,
graphql_client: GraphQLClient,
manga_status: MangaStatus,
) -> None:
title = "Test Manga Title"
response = await graphql_client.query(
query=QUERY,
variables={"input": {"title": title}},
variables={
"input": {"title": title, "status": manga_status.name.upper()},
},
)

manga = await session.scalar(select(Manga))
Expand Down
3 changes: 2 additions & 1 deletion tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from faker import Faker

from app.db.models import AltTitle, Manga, MangaBranch, MangaChapter, MangaTag
from lib.types import Language
from lib.types import Language, MangaStatus

T = TypeVar("T")

Expand Down Expand Up @@ -38,6 +38,7 @@ class Meta:
class MangaFactory(GenericFactory[Manga]):
title = factory.Faker("sentence")
title_slug = factory.Faker("sentence")
status = factory.Faker("enum", enum_cls=MangaStatus)
created_at = factory.Faker("date_time", tzinfo=UTC)


Expand Down

0 comments on commit 444289f

Please sign in to comment.