Working version

This commit is contained in:
Eden Kirin
2023-08-27 00:03:13 +02:00
parent 7f1acec1af
commit 9faca36ce1
5 changed files with 86 additions and 72 deletions

View File

@ -1,8 +1,10 @@
from __future__ import annotations from __future__ import annotations
from typing import TYPE_CHECKING, Optional, cast from dataclasses import dataclass
from typing import TYPE_CHECKING, Optional, cast, TypeVar, Type, Generic
from litestar import Controller, delete, get, post, put from litestar import Controller, delete, get, post, put
from litestar.contrib.repository.filters import LimitOffset, SearchFilter, FilterTypes
from litestar.di import Provide from litestar.di import Provide
from litestar.pagination import ( from litestar.pagination import (
AbstractAsyncOffsetPaginator, AbstractAsyncOffsetPaginator,
@ -12,16 +14,19 @@ from litestar.status_codes import HTTP_200_OK
from sqlalchemy import ScalarResult, func, select from sqlalchemy import ScalarResult, func, select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from app.domain.machine import MachineReadDTO, MachineWriteDTO, Repository, Service from app.domain.machine import (
from app.domain.machine import Machine Machine,
MachineReadDTO,
from litestar.contrib.repository.filters import SearchFilter, LimitOffset MachineWriteDTO,
Repository,
Service,
)
from app.lib.responses import ObjectListResponse, ObjectResponse
if TYPE_CHECKING: if TYPE_CHECKING:
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
DETAIL_ROUTE = "/{machine_id:int}" DETAIL_ROUTE = "/{machine_id:int}"
@ -59,20 +64,10 @@ class MachineController(Controller):
@get() @get()
async def get_machines( async def get_machines(
self, service: Service, self, service: Service, search: Optional[str] = None
search: Optional[str] = None, ) -> ObjectListResponse[Machine]:
) -> list[Machine]:
"""Get a list of authors."""
print("#" * 100)
print(search)
print("#" * 100)
filters = [ filters = [
LimitOffset( LimitOffset(limit=20, offset=0),
limit=20,
offset=0
),
] ]
if search: if search:
@ -83,22 +78,18 @@ class MachineController(Controller):
), ),
) )
return await service.list(*filters) content = await service.list(*filters)
return ObjectListResponse(content=content)
# @get()
# async def get_machines(
# self, service: Service, filters: list[FilterTypes]
# ) -> list[Machine]:
# """Get a list of authors."""
# return await service.list(*filters)
# @post() # @post()
# async def create_author(self, data: Machine, service: Service) -> Machine: # async def create_author(self, data: Machine, service: Service) -> Machine:
# return await service.create(data) # return await service.create(data)
# #
@get(DETAIL_ROUTE) @get(DETAIL_ROUTE)
async def get_machine(self, service: Service, machine_id: int) -> Machine: async def get_machine(self, service: Service, machine_id: int) -> ObjectResponse[Machine]:
return await service.get(machine_id) content = await service.get(machine_id)
return ObjectResponse(content=content)
# #
# @put(DETAIL_ROUTE) # @put(DETAIL_ROUTE)
# async def update_author( # async def update_author(

View File

@ -1,6 +1,6 @@
from typing import AsyncGenerator from typing import AsyncGenerator
from litestar.contrib.sqlalchemy.plugins import SQLAlchemyAsyncConfig from litestar.contrib.sqlalchemy.plugins import EngineConfig, SQLAlchemyAsyncConfig
from litestar.exceptions import ClientException from litestar.exceptions import ClientException
from litestar.status_codes import HTTP_409_CONFLICT from litestar.status_codes import HTTP_409_CONFLICT
from sqlalchemy import URL from sqlalchemy import URL
@ -32,5 +32,8 @@ db_connection_string = URL.create(
database="televend", database="televend",
) )
db_config = SQLAlchemyAsyncConfig( db_config = SQLAlchemyAsyncConfig(
connection_string=db_connection_string.render_as_string(hide_password=False) connection_string=db_connection_string.render_as_string(hide_password=False),
engine_config=EngineConfig(
echo=True,
),
) )

View File

@ -1,9 +1,14 @@
from __future__ import annotations
from typing import Annotated from typing import Annotated
from litestar.contrib.repository import FilterTypes
from litestar.contrib.sqlalchemy.base import BigIntBase from litestar.contrib.sqlalchemy.base import BigIntBase
from litestar.contrib.sqlalchemy.dto import SQLAlchemyDTO from litestar.contrib.sqlalchemy.dto import SQLAlchemyDTO
from litestar.contrib.sqlalchemy.repository import SQLAlchemyAsyncRepository from litestar.contrib.sqlalchemy.repository import SQLAlchemyAsyncRepository
from litestar.contrib.sqlalchemy.repository.types import SelectT
from litestar.dto import DTOConfig from litestar.dto import DTOConfig
from sqlalchemy import true
from sqlalchemy.orm import Mapped from sqlalchemy.orm import Mapped
from app.lib import service from app.lib import service
@ -11,12 +16,27 @@ from app.lib import service
class Machine(BigIntBase): class Machine(BigIntBase):
__tablename__ = "machines" __tablename__ = "machines"
caption: Mapped[str] caption: Mapped[str]
enabled: Mapped[bool]
alive: Mapped[bool]
deleted: Mapped[bool]
external_id: Mapped[str]
class Repository(SQLAlchemyAsyncRepository[Machine]): class Repository(SQLAlchemyAsyncRepository[Machine]):
model_type = Machine model_type = Machine
def _apply_filters(
self, *filters: FilterTypes, apply_pagination: bool = True, statement: SelectT
) -> SelectT:
statement = super()._apply_filters(
*filters, apply_pagination=apply_pagination, statement=statement
)
statement = statement.where(Machine.alive == true())
return statement
class Service(service.Service[Machine]): class Service(service.Service[Machine]):
repository_type = Repository repository_type = Repository

26
app/lib/responses.py Normal file
View File

@ -0,0 +1,26 @@
from dataclasses import dataclass
from typing import Generic, TypeVar
T = TypeVar("T")
@dataclass
class ObjectResponse(Generic[T]):
content: T
@dataclass
class ObjectListResponse(Generic[T]):
content: list[T]
@dataclass
class PaginationMeta:
page: int
page_count: int
@dataclass
class PaginatedObjectListResponse(Generic[T]):
content: list[T]
meta: PaginationMeta

56
main.py
View File

@ -1,56 +1,30 @@
from uuid import UUID from typing import Any
from litestar import Litestar, get from litestar import Litestar, get
from litestar.contrib.repository import FilterTypes
from litestar.contrib.repository.exceptions import ( from litestar.contrib.repository.exceptions import (
RepositoryError as RepositoryException, RepositoryError as RepositoryException,
) )
from litestar.contrib.repository.filters import (
BeforeAfter,
CollectionFilter,
LimitOffset,
NotInCollectionFilter,
NotInSearchFilter,
OnBeforeAfter,
OrderBy,
SearchFilter,
)
from litestar.contrib.sqlalchemy.plugins import SQLAlchemyPlugin from litestar.contrib.sqlalchemy.plugins import SQLAlchemyPlugin
from litestar.openapi import OpenAPIConfig from litestar.openapi import OpenAPIConfig
from sqlalchemy.ext.asyncio import AsyncSession
from app.controllers import create_router from app.controllers import create_router
from app.database import db_config, provide_transaction from app.database import db_config, provide_transaction
from app.lib import exceptions from app.lib import exceptions
from app.lib.service import ServiceError from app.lib.service import ServiceError
def create_app(**kwargs: Any) -> Litestar:
@get("/") return Litestar(
async def hello_world() -> str: route_handlers=[create_router()],
return "Hello, world!" openapi_config=OpenAPIConfig(title="My API", version="1.0.0"),
dependencies={"session": provide_transaction},
plugins=[SQLAlchemyPlugin(db_config)],
exception_handlers={
RepositoryException: exceptions.repository_exception_to_http_response, # type: ignore[dict-item]
ServiceError: exceptions.service_exception_to_http_response, # type: ignore[dict-item]
},
debug=True,
**kwargs,
)
app = Litestar( app = create_app()
route_handlers=[hello_world, create_router()],
openapi_config=OpenAPIConfig(title="My API", version="1.0.0"),
dependencies={"session": provide_transaction},
plugins=[SQLAlchemyPlugin(db_config)],
exception_handlers={
RepositoryException: exceptions.repository_exception_to_http_response, # type: ignore[dict-item]
ServiceError: exceptions.service_exception_to_http_response, # type: ignore[dict-item]
},
signature_namespace={
"AsyncSession": AsyncSession,
"FilterTypes": FilterTypes,
"BeforeAfter": BeforeAfter,
"CollectionFilter": CollectionFilter,
"LimitOffset": LimitOffset,
"UUID": UUID,
"OrderBy": OrderBy,
"SearchFilter": SearchFilter,
"OnBeforeAfter": OnBeforeAfter,
"NotInSearchFilter": NotInSearchFilter,
"NotInCollectionFilter": NotInCollectionFilter,
},
debug=True,
)