Skip to content

Commit bbd72e1

Browse files
committed
Extract template service
The goal is to remove fastapi dependency from service layer. But we need to understand templates require some FastAPI thins, so for templates (views) services to have fastapi dependencies is fine.
1 parent 3546675 commit bbd72e1

File tree

4 files changed

+98
-86
lines changed

4 files changed

+98
-86
lines changed

futuramaapi/routers/services/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
from ._base import (
22
BaseService,
33
BaseSessionService,
4-
BaseTemplateService,
54
BaseUserAuthenticatedService,
65
)
6+
from ._base_template import BaseTemplateService
77

88
__all__ = [
99
"BaseService",

futuramaapi/routers/services/_base.py

Lines changed: 4 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,19 @@
11
from abc import ABC, abstractmethod
22
from collections.abc import Sequence
3-
from typing import Any, ClassVar, TypeVar
3+
from typing import Any, TypeVar
44

55
import jwt
6-
from fastapi import HTTPException, Request, status
6+
from fastapi import HTTPException, status
77
from fastapi_pagination import Page
88
from jwt import ExpiredSignatureError, InvalidSignatureError, InvalidTokenError
9-
from pydantic import Field
10-
from sqlalchemy import Result, Select, select
9+
from sqlalchemy import Select, select
1110
from sqlalchemy.exc import NoResultFound
1211
from sqlalchemy.ext.asyncio import AsyncSession
13-
from sqlalchemy.orm import selectinload
14-
from starlette.templating import _TemplateResponse
1512

16-
from futuramaapi.__version__ import __version__
1713
from futuramaapi.core import settings
1814
from futuramaapi.helpers.pydantic import BaseModel
19-
from futuramaapi.helpers.templates import templates
20-
from futuramaapi.repositories.models import AuthSessionModel, UserModel
15+
from futuramaapi.repositories.models import UserModel
2116
from futuramaapi.repositories.session import session_manager
22-
from futuramaapi.utils import config, metadata
2317

2418
TResponse = TypeVar(
2519
"TResponse",
@@ -58,80 +52,6 @@ async def __call__(self, *args, **kwargs) -> TResponse:
5852
return await self.process(*args, **kwargs)
5953

6054

61-
class _ProjectContext(BaseModel):
62-
version: str = Field(
63-
default=__version__,
64-
)
65-
g_tag: str = Field(
66-
default=settings.g_tag,
67-
alias="G_TAG",
68-
)
69-
author: str | None = Field(
70-
default=metadata.get("author", None),
71-
)
72-
description: str = Field(
73-
default=metadata["summary"],
74-
)
75-
config: dict[str, Any] = config
76-
77-
78-
_project_context: _ProjectContext = _ProjectContext()
79-
80-
81-
class BaseTemplateService(BaseSessionService[_TemplateResponse], ABC):
82-
template_name: ClassVar[str]
83-
84-
_cookie_auth_key: ClassVar[str] = "Authorization"
85-
86-
@abstractmethod
87-
async def get_context(self, *args, **kwargs) -> dict[str, Any]: ...
88-
89-
@property
90-
def request(self) -> Request:
91-
if self.context is None:
92-
raise AttributeError("Request is not defined.")
93-
94-
if "request" not in self.context:
95-
raise AttributeError("Request is not defined.")
96-
97-
return self.context["request"]
98-
99-
@property
100-
def __auth_session_statement(self) -> Select[tuple[AuthSessionModel]]:
101-
statement: Select[tuple[AuthSessionModel]] = select(AuthSessionModel)
102-
statement = statement.where(AuthSessionModel.key == self.request.cookies[self._cookie_auth_key])
103-
statement = statement.options(selectinload(AuthSessionModel.user))
104-
return statement
105-
106-
async def _get_current_user(self) -> UserModel | None:
107-
if self._cookie_auth_key not in self.request.cookies:
108-
return None
109-
110-
result: Result[tuple[AuthSessionModel]] = await self.session.execute(self.__auth_session_statement)
111-
try:
112-
auth_session: AuthSessionModel = result.scalars().one()
113-
except NoResultFound:
114-
return None
115-
116-
if not auth_session.valid:
117-
return None
118-
119-
return auth_session.user
120-
121-
async def _get_context(self) -> dict[str, Any]:
122-
context: dict[str, Any] = await self.get_context()
123-
context["_project"] = _project_context
124-
context["current_user"] = await self._get_current_user()
125-
return context
126-
127-
async def process(self, *args, **kwargs) -> _TemplateResponse:
128-
return templates.TemplateResponse(
129-
self.request,
130-
self.template_name,
131-
context=await self._get_context(),
132-
)
133-
134-
13555
class BaseUserAuthenticatedService[TResponse](BaseSessionService[TResponse], ABC):
13656
token: str
13757

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
from abc import ABC, abstractmethod
2+
from typing import Any, ClassVar
3+
4+
from fastapi import Request
5+
from pydantic import Field
6+
from sqlalchemy import Result, Select, select
7+
from sqlalchemy.exc import NoResultFound
8+
from sqlalchemy.orm import selectinload
9+
from starlette.templating import _TemplateResponse
10+
11+
from futuramaapi.__version__ import __version__
12+
from futuramaapi.core import settings
13+
from futuramaapi.helpers.pydantic import BaseModel
14+
from futuramaapi.helpers.templates import templates
15+
from futuramaapi.repositories.models import AuthSessionModel, UserModel
16+
from futuramaapi.utils import config, metadata
17+
18+
from ._base import BaseSessionService
19+
20+
21+
class _ProjectContext(BaseModel):
22+
version: str = Field(
23+
default=__version__,
24+
)
25+
g_tag: str = Field(
26+
default=settings.g_tag,
27+
alias="G_TAG",
28+
)
29+
author: str | None = Field(
30+
default=metadata.get("author", None),
31+
)
32+
description: str = Field(
33+
default=metadata["summary"],
34+
)
35+
config: dict[str, Any] = config
36+
37+
38+
_project_context: _ProjectContext = _ProjectContext()
39+
40+
41+
class BaseTemplateService(BaseSessionService[_TemplateResponse], ABC):
42+
template_name: ClassVar[str]
43+
44+
_cookie_auth_key: ClassVar[str] = "Authorization"
45+
46+
@abstractmethod
47+
async def get_context(self, *args, **kwargs) -> dict[str, Any]: ...
48+
49+
@property
50+
def request(self) -> Request:
51+
if self.context is None:
52+
raise AttributeError("Request is not defined.")
53+
54+
if "request" not in self.context:
55+
raise AttributeError("Request is not defined.")
56+
57+
return self.context["request"]
58+
59+
@property
60+
def __auth_session_statement(self) -> Select[tuple[AuthSessionModel]]:
61+
statement: Select[tuple[AuthSessionModel]] = select(AuthSessionModel)
62+
statement = statement.where(AuthSessionModel.key == self.request.cookies[self._cookie_auth_key])
63+
statement = statement.options(selectinload(AuthSessionModel.user))
64+
return statement
65+
66+
async def _get_current_user(self) -> UserModel | None:
67+
if self._cookie_auth_key not in self.request.cookies:
68+
return None
69+
70+
result: Result[tuple[AuthSessionModel]] = await self.session.execute(self.__auth_session_statement)
71+
try:
72+
auth_session: AuthSessionModel = result.scalars().one()
73+
except NoResultFound:
74+
return None
75+
76+
if not auth_session.valid:
77+
return None
78+
79+
return auth_session.user
80+
81+
async def _get_context(self) -> dict[str, Any]:
82+
context: dict[str, Any] = await self.get_context()
83+
context["_project"] = _project_context
84+
context["current_user"] = await self._get_current_user()
85+
return context
86+
87+
async def process(self, *args, **kwargs) -> _TemplateResponse:
88+
return templates.TemplateResponse(
89+
self.request,
90+
self.template_name,
91+
context=await self._get_context(),
92+
)

futuramaapi/routers/services/passwords/get_signature_user_password_change_form.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from futuramaapi.helpers.templates import templates
1111
from futuramaapi.repositories.models import UserModel
1212
from futuramaapi.routers.services import BaseSessionService
13-
from futuramaapi.routers.services._base import _project_context
13+
from futuramaapi.routers.services._base_template import _project_context
1414

1515

1616
class TokenDecodeError(Exception):

0 commit comments

Comments
 (0)