You've already forked RekomenciBackend
add lints
This commit is contained in:
@@ -6,7 +6,6 @@ from cryptography.fernet import Fernet
|
|||||||
from template_project.application.access_token.cryptographer import AccessTokenCryptographer
|
from template_project.application.access_token.cryptographer import AccessTokenCryptographer
|
||||||
from template_project.application.access_token.entity import AccessTokenId
|
from template_project.application.access_token.entity import AccessTokenId
|
||||||
|
|
||||||
|
|
||||||
type RawAccessToken = str
|
type RawAccessToken = str
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from typing import override
|
from typing import override
|
||||||
|
|
||||||
from template_project.application.access_token.entity import AccessToken
|
from template_project.application.access_token.entity import AccessToken
|
||||||
from template_project.application.access_token.entity_factory import AccessTokenFactory
|
from template_project.application.access_token.entity_factory import AccessTokenFactory
|
||||||
from template_project.application.user.entity import UserId
|
from template_project.application.user.entity import UserId
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ from typing import override
|
|||||||
|
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from template_project.adapters.data_gateways.tables import access_token_table
|
from template_project.adapters.data_gateways.tables import access_token_table
|
||||||
from template_project.application.access_token.data_gateway import AccessTokenDataGateway
|
from template_project.application.access_token.data_gateway import AccessTokenDataGateway
|
||||||
from template_project.application.access_token.entity import AccessToken, AccessTokenId
|
from template_project.application.access_token.entity import AccessToken, AccessTokenId
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ from sqlalchemy.orm import registry
|
|||||||
from template_project.application.access_token.entity import AccessToken
|
from template_project.application.access_token.entity import AccessToken
|
||||||
from template_project.application.user.entity import User
|
from template_project.application.user.entity import User
|
||||||
|
|
||||||
|
|
||||||
meta_data = MetaData()
|
meta_data = MetaData()
|
||||||
|
|
||||||
user_table = Table(
|
user_table = Table(
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from typing import override
|
from typing import cast, override
|
||||||
|
|
||||||
from sqlalchemy import exists, select
|
from sqlalchemy import exists, select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
@@ -24,4 +25,4 @@ class DefaultUserDataGateway(UserDataGateway):
|
|||||||
result_fetchone = result.fetchone()
|
result_fetchone = result.fetchone()
|
||||||
if result_fetchone is None:
|
if result_fetchone is None:
|
||||||
return False
|
return False
|
||||||
return result_fetchone[0]
|
return cast(bool, result_fetchone[0])
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
from abc import abstractmethod
|
|
||||||
from typing import override
|
from typing import override
|
||||||
|
|
||||||
import argon2
|
import argon2
|
||||||
@@ -27,7 +26,7 @@ class ArgonPasswordVerifying(PasswordVerifying):
|
|||||||
) -> None:
|
) -> None:
|
||||||
self._password_hasher = password_hasher
|
self._password_hasher = password_hasher
|
||||||
|
|
||||||
@abstractmethod
|
@override
|
||||||
def verify(
|
def verify(
|
||||||
self,
|
self,
|
||||||
verifiable_password: SecretString,
|
verifiable_password: SecretString,
|
||||||
@@ -35,6 +34,5 @@ class ArgonPasswordVerifying(PasswordVerifying):
|
|||||||
) -> bool:
|
) -> bool:
|
||||||
try:
|
try:
|
||||||
return self._password_hasher.verify(hashed_password, verifiable_password.get_value())
|
return self._password_hasher.verify(hashed_password, verifiable_password.get_value())
|
||||||
except Argon2Error as e:
|
except Argon2Error:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ from typing import Protocol
|
|||||||
|
|
||||||
from template_project.application.access_token.entity import AccessTokenId
|
from template_project.application.access_token.entity import AccessTokenId
|
||||||
|
|
||||||
|
|
||||||
type RawAccessToken = str
|
type RawAccessToken = str
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from template_project.application.user.entity import UserId
|
|||||||
|
|
||||||
AccessTokenId = NewType("AccessTokenId", UUID)
|
AccessTokenId = NewType("AccessTokenId", UUID)
|
||||||
|
|
||||||
|
|
||||||
@to_entity
|
@to_entity
|
||||||
class AccessToken(Entity[AccessTokenId]):
|
class AccessToken(Entity[AccessTokenId]):
|
||||||
user_id: UserId
|
user_id: UserId
|
||||||
@@ -32,17 +33,12 @@ class AccessToken(Entity[AccessTokenId]):
|
|||||||
revoked=False,
|
revoked=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def ensure_expired(self) -> None:
|
def ensure_expired(self) -> None:
|
||||||
if self.expired_predicate():
|
if self.expired_predicate():
|
||||||
raise AccessTokenExpiredError(id_=self.id)
|
raise AccessTokenExpiredError(id_=self.id)
|
||||||
|
|
||||||
def expired_predicate(self) -> bool:
|
def expired_predicate(self) -> bool:
|
||||||
return (
|
return (self.expires_in < datetime.now(tz=UTC)) or self.revoked or self.deleted_at is not None
|
||||||
(self.expires_in < datetime.now(tz=UTC))
|
|
||||||
or self.revoked
|
|
||||||
or self.deleted_at is not None
|
|
||||||
)
|
|
||||||
|
|
||||||
def revoke(self) -> None:
|
def revoke(self) -> None:
|
||||||
self.revoked = True
|
self.revoked = True
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from typing import dataclass_transform
|
|||||||
eq_default=False,
|
eq_default=False,
|
||||||
kw_only_default=True,
|
kw_only_default=True,
|
||||||
)
|
)
|
||||||
def to_data_structure[_InteractorClsT](interactor_cls: type[_InteractorClsT]) -> type[_InteractorClsT]:
|
def to_data_structure[InteractorClsT](interactor_cls: type[InteractorClsT]) -> type[InteractorClsT]:
|
||||||
return dataclass(
|
return dataclass(
|
||||||
kw_only=True,
|
kw_only=True,
|
||||||
eq=False,
|
eq=False,
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
from collections.abc import Hashable
|
from collections.abc import Hashable
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import dataclass_transform, override
|
from typing import cast, dataclass_transform, override
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from template_project.application.common.errors import EntityAlreadyDeletedError
|
from template_project.application.common.errors import EntityAlreadyDeletedError
|
||||||
|
|
||||||
|
|
||||||
@dataclass_transform(kw_only_default=True)
|
@dataclass_transform(kw_only_default=True)
|
||||||
def to_entity[_EntityCLsT](entity_cls: type[_EntityCLsT]) -> type[_EntityCLsT]:
|
def to_entity[EntityCLsT](entity_cls: type[EntityCLsT]) -> type[EntityCLsT]:
|
||||||
return dataclass(kw_only=True)(entity_cls)
|
return dataclass(kw_only=True)(entity_cls)
|
||||||
|
|
||||||
|
|
||||||
@to_entity
|
@to_entity
|
||||||
class Entity[_EntityId: UUID](Hashable):
|
class Entity[EntityId: UUID](Hashable):
|
||||||
id: _EntityId
|
id: EntityId
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
deleted_at: datetime | None = None
|
deleted_at: datetime | None = None
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ class Entity[_EntityId: UUID](Hashable):
|
|||||||
@override
|
@override
|
||||||
def __eq__(self, other: object) -> bool:
|
def __eq__(self, other: object) -> bool:
|
||||||
if isinstance(other, Entity):
|
if isinstance(other, Entity):
|
||||||
return self.id == other.id
|
return cast(bool, self.id == other.id)
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ from typing import dataclass_transform
|
|||||||
eq_default=False,
|
eq_default=False,
|
||||||
kw_only_default=True,
|
kw_only_default=True,
|
||||||
)
|
)
|
||||||
def to_interactor[_InteractorClsT](interactor_cls: type[_InteractorClsT]) -> type[_InteractorClsT]:
|
def to_interactor[InteractorClsT](interactor_cls: type[InteractorClsT]) -> type[InteractorClsT]:
|
||||||
return dataclass(
|
return dataclass(
|
||||||
kw_only=True,
|
kw_only=True,
|
||||||
eq=False,
|
eq=False,
|
||||||
|
|||||||
@@ -10,8 +10,5 @@ class UserDataGateway(Protocol):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def exists_by_email(
|
async def exists_by_email(self, email: str) -> bool:
|
||||||
self,
|
|
||||||
email: str
|
|
||||||
) -> bool:
|
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@@ -8,6 +8,7 @@ from template_project.application.common.entity import Entity, to_entity
|
|||||||
|
|
||||||
UserId = NewType("UserId", UUID)
|
UserId = NewType("UserId", UUID)
|
||||||
|
|
||||||
|
|
||||||
@to_entity
|
@to_entity
|
||||||
class User(Entity[UserId]):
|
class User(Entity[UserId]):
|
||||||
email: str
|
email: str
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from typing import override
|
from typing import override
|
||||||
|
|
||||||
from template_project.application.common.errors import ApplicationError, to_error
|
from template_project.application.common.errors import ApplicationError, to_error
|
||||||
|
|
||||||
|
|
||||||
@@ -10,6 +11,7 @@ class UserWithEmailAlreadyExistsError(ApplicationError):
|
|||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
return f"User with the email={self.email!r} already exists"
|
return f"User with the email={self.email!r} already exists"
|
||||||
|
|
||||||
|
|
||||||
@to_error
|
@to_error
|
||||||
class UserUnauthorizedError(ApplicationError):
|
class UserUnauthorizedError(ApplicationError):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class GetMeResponse:
|
|||||||
id: UserId
|
id: UserId
|
||||||
email: str
|
email: str
|
||||||
|
|
||||||
|
|
||||||
response_converter = get_converter(User, GetMeResponse)
|
response_converter = get_converter(User, GetMeResponse)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from datetime import timedelta
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from tomllib import loads
|
from tomllib import loads
|
||||||
from typing import dataclass_transform
|
from typing import dataclass_transform
|
||||||
|
|
||||||
from adaptix import P, Retort, loader
|
from adaptix import P, Retort, loader
|
||||||
|
|
||||||
from template_project.application.common.containers import SecretString
|
from template_project.application.common.containers import SecretString
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import sys
|
||||||
from collections.abc import AsyncIterator
|
from collections.abc import AsyncIterator
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import sys
|
|
||||||
from typing import Final
|
from typing import Final
|
||||||
|
|
||||||
|
import uvicorn
|
||||||
from dishka import AsyncContainer
|
from dishka import AsyncContainer
|
||||||
from dishka.integrations.fastapi import setup_dishka
|
from dishka.integrations.fastapi import setup_dishka
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.middleware.cors import CORSMiddleware
|
from fastapi.middleware.cors import CORSMiddleware
|
||||||
import uvicorn
|
|
||||||
|
|
||||||
from template_project.web_api.configuration import load_configuration
|
from template_project.web_api.configuration import load_configuration
|
||||||
from template_project.web_api.ioc.make import make_ioc
|
from template_project.web_api.ioc.make import make_ioc
|
||||||
@@ -35,11 +35,13 @@ LOG_CONFIG: Final = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@asynccontextmanager
|
@asynccontextmanager
|
||||||
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
|
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
|
||||||
yield
|
yield
|
||||||
await app.state.dishka_container.close()
|
await app.state.dishka_container.close()
|
||||||
|
|
||||||
|
|
||||||
def make_asgi_application(
|
def make_asgi_application(
|
||||||
ioc: AsyncContainer,
|
ioc: AsyncContainer,
|
||||||
) -> FastAPI:
|
) -> FastAPI:
|
||||||
@@ -65,6 +67,7 @@ def make_asgi_application(
|
|||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
def _main(
|
def _main(
|
||||||
configuration_path: Path,
|
configuration_path: Path,
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -83,7 +86,8 @@ def _main(
|
|||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
from asyncio import WindowsSelectorEventLoopPolicy
|
from asyncio import WindowsSelectorEventLoopPolicy # noqa: PLC0415
|
||||||
|
|
||||||
asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy())
|
asyncio.set_event_loop_policy(WindowsSelectorEventLoopPolicy())
|
||||||
|
|
||||||
arg_parser = argparse.ArgumentParser()
|
arg_parser = argparse.ArgumentParser()
|
||||||
@@ -95,5 +99,6 @@ def main() -> None:
|
|||||||
args = arg_parser.parse_args()
|
args = arg_parser.parse_args()
|
||||||
_main(args.configuration)
|
_main(args.configuration)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from abc import abstractmethod
|
from typing import override
|
||||||
|
|
||||||
from fastapi import Request
|
from fastapi import Request
|
||||||
|
|
||||||
@@ -11,8 +11,7 @@ from template_project.application.user.data_gateway import UserDataGateway
|
|||||||
from template_project.application.user.entity import User
|
from template_project.application.user.entity import User
|
||||||
from template_project.application.user.errors import UserUnauthorizedError
|
from template_project.application.user.errors import UserUnauthorizedError
|
||||||
|
|
||||||
|
TOKEN_TYPE = "Bearer" # noqa: S105
|
||||||
TOKEN_TYPE = "Bearer"
|
|
||||||
BEARER_SECTIONS = 2
|
BEARER_SECTIONS = 2
|
||||||
AUTH_HEADER = "Authorization"
|
AUTH_HEADER = "Authorization"
|
||||||
|
|
||||||
@@ -31,7 +30,7 @@ class WebApiIdentityProvider(IdentityProvider):
|
|||||||
self._access_token_data_gateway = access_token_data_gateway
|
self._access_token_data_gateway = access_token_data_gateway
|
||||||
self._access_token_cryptographer = access_token_cryptographer
|
self._access_token_cryptographer = access_token_cryptographer
|
||||||
|
|
||||||
@abstractmethod
|
@override
|
||||||
async def get_current_user(self) -> User:
|
async def get_current_user(self) -> User:
|
||||||
auth_tokn = self._request.headers[AUTH_HEADER]
|
auth_tokn = self._request.headers[AUTH_HEADER]
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from typing import AsyncIterable
|
from collections.abc import AsyncIterable
|
||||||
|
|
||||||
from dishka import Provider, Scope, provide
|
from dishka import Provider, Scope, provide
|
||||||
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine
|
from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, create_async_engine
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import argon2
|
import argon2
|
||||||
from cryptography.fernet import Fernet
|
from cryptography.fernet import Fernet
|
||||||
from dishka import Provider, Scope, WithParents, provide, provide_all
|
from dishka import BaseScope, Provider, Scope, WithParents, provide, provide_all
|
||||||
|
|
||||||
from template_project.adapters.access_token.cryptographer import FernetAccessTokenCryptographer
|
from template_project.adapters.access_token.cryptographer import FernetAccessTokenCryptographer
|
||||||
from template_project.adapters.password_utils import ArgonPasswordHasher, ArgonPasswordVerifying
|
from template_project.adapters.password_utils import ArgonPasswordHasher, ArgonPasswordVerifying
|
||||||
@@ -8,7 +8,7 @@ from template_project.web_api.configuration import AccessTokenConfiguration
|
|||||||
|
|
||||||
|
|
||||||
class CryptographerProvider(Provider):
|
class CryptographerProvider(Provider):
|
||||||
scope = Scope.APP
|
scope: BaseScope | None = Scope.APP
|
||||||
|
|
||||||
@provide
|
@provide
|
||||||
def argon_password_hasher(self) -> argon2.PasswordHasher:
|
def argon_password_hasher(self) -> argon2.PasswordHasher:
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from dishka import Provider, Scope, WithParents, provide, provide_all
|
from dishka import BaseScope, Provider, Scope, WithParents, provide, provide_all
|
||||||
|
|
||||||
from template_project.adapters.data_gateways.access_token import DefaultAccessTokenDataGateway
|
from template_project.adapters.data_gateways.access_token import DefaultAccessTokenDataGateway
|
||||||
from template_project.adapters.data_gateways.user import DefaultUserDataGateway
|
from template_project.adapters.data_gateways.user import DefaultUserDataGateway
|
||||||
@@ -6,7 +6,7 @@ from template_project.adapters.unit_of_work import DefaultUnitOfWork
|
|||||||
|
|
||||||
|
|
||||||
class DataGatewayProvider(Provider):
|
class DataGatewayProvider(Provider):
|
||||||
scope = Scope.REQUEST
|
scope: BaseScope | None = Scope.REQUEST
|
||||||
|
|
||||||
unit_of_work = provide(WithParents[DefaultUnitOfWork])
|
unit_of_work = provide(WithParents[DefaultUnitOfWork])
|
||||||
data_gateways = provide_all(
|
data_gateways = provide_all(
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
from dishka import Provider, Scope, WithParents, provide_all
|
from dishka import BaseScope, Provider, Scope, WithParents, provide_all
|
||||||
|
|
||||||
from template_project.adapters.access_token.factory import DefaultAccessTokenFactory
|
from template_project.adapters.access_token.factory import DefaultAccessTokenFactory
|
||||||
|
|
||||||
|
|
||||||
class FactoryProvider(Provider):
|
class FactoryProvider(Provider):
|
||||||
scope = Scope.APP
|
scope: BaseScope | None = Scope.APP
|
||||||
|
|
||||||
provides = provide_all(
|
provides = provide_all(
|
||||||
WithParents[DefaultAccessTokenFactory],
|
WithParents[DefaultAccessTokenFactory],
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
from dishka import Provider, Scope, provide_all
|
from dishka import BaseScope, Provider, Scope, provide_all
|
||||||
|
|
||||||
from template_project.application.user.interactors.sign_up import UserSignUpInteractor
|
from template_project.application.user.interactors.sign_up import UserSignUpInteractor
|
||||||
|
|
||||||
|
|
||||||
class InteractorProvider(Provider):
|
class InteractorProvider(Provider):
|
||||||
scope = Scope.REQUEST
|
scope: BaseScope | None = Scope.REQUEST
|
||||||
|
|
||||||
interactors = provide_all(
|
interactors = provide_all(
|
||||||
UserSignUpInteractor,
|
UserSignUpInteractor,
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
from dishka import AsyncContainer, make_async_container
|
from dishka import AsyncContainer, make_async_container
|
||||||
from dishka.integrations.fastapi import FastapiProvider
|
from dishka.integrations.fastapi import FastapiProvider
|
||||||
|
|
||||||
from template_project.web_api.configuration import AccessTokenConfiguration, Configuration, DatabaseConfiguration, ServerConfiguration
|
from template_project.web_api.configuration import (
|
||||||
|
AccessTokenConfiguration,
|
||||||
|
Configuration,
|
||||||
|
DatabaseConfiguration,
|
||||||
|
ServerConfiguration,
|
||||||
|
)
|
||||||
|
from template_project.web_api.ioc.cryptographer import CryptographerProvider
|
||||||
from template_project.web_api.ioc.data_gateway import DataGatewayProvider
|
from template_project.web_api.ioc.data_gateway import DataGatewayProvider
|
||||||
from template_project.web_api.ioc.factory import FactoryProvider
|
from template_project.web_api.ioc.factory import FactoryProvider
|
||||||
from template_project.web_api.ioc.interactor import InteractorProvider
|
from template_project.web_api.ioc.interactor import InteractorProvider
|
||||||
from template_project.web_api.ioc.cryptographer import CryptographerProvider
|
|
||||||
|
|
||||||
|
|
||||||
def make_ioc(configuration: Configuration) -> AsyncContainer:
|
def make_ioc(configuration: Configuration) -> AsyncContainer:
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ from pydantic import BaseModel, SecretStr
|
|||||||
from template_project.application.common.containers import SecretString
|
from template_project.application.common.containers import SecretString
|
||||||
from template_project.application.user.interactors.sign_up import UserSignUpInteractor
|
from template_project.application.user.interactors.sign_up import UserSignUpInteractor
|
||||||
|
|
||||||
|
|
||||||
router = APIRouter(route_class=DishkaRoute)
|
router = APIRouter(route_class=DishkaRoute)
|
||||||
|
|
||||||
|
|
||||||
@@ -25,8 +24,7 @@ async def sign_up(
|
|||||||
interactor: FromDishka[UserSignUpInteractor],
|
interactor: FromDishka[UserSignUpInteractor],
|
||||||
) -> UserSignUpResponse:
|
) -> UserSignUpResponse:
|
||||||
response_interactor = await interactor.execute(
|
response_interactor = await interactor.execute(
|
||||||
email=request.email,
|
email=request.email, password=SecretString(request.password.get_secret_value())
|
||||||
password=SecretString(request.password.get_secret_value())
|
|
||||||
)
|
)
|
||||||
return UserSignUpResponse(
|
return UserSignUpResponse(
|
||||||
access_token=response_interactor.access_token,
|
access_token=response_interactor.access_token,
|
||||||
|
|||||||
Reference in New Issue
Block a user