From 2e073146bf42905d030f717a9d5bd775a27450bf Mon Sep 17 00:00:00 2001 From: gitgernit Date: Fri, 21 Nov 2025 00:18:55 +0300 Subject: [PATCH] feat(): register device route --- .../data_gateways/notification_device.py | 17 ++++++++- .../adapters/data_gateways/tables.py | 2 +- .../__init__.py | 0 .../notification_device/data_gateway.py | 10 ++++- .../{user => }/notification_device/entity.py | 0 .../{user => }/notification_device/errors.py | 0 .../interactors/register_device.py | 37 +++++++++++++++++++ .../interactors/send_notification.py | 4 +- .../user/notification_device/__init__.py | 0 .../web_api/ioc/interactor.py | 6 ++- .../web_api/routes/notification.py | 21 ++++++++++- 11 files changed, 88 insertions(+), 9 deletions(-) rename src/template_project/application/{user/notification/interactors => notification_device}/__init__.py (100%) rename src/template_project/application/{user => }/notification_device/data_gateway.py (51%) rename src/template_project/application/{user => }/notification_device/entity.py (100%) rename src/template_project/application/{user => }/notification_device/errors.py (100%) create mode 100644 src/template_project/application/notification_device/interactors/register_device.py rename src/template_project/application/{user/notification => notification_device}/interactors/send_notification.py (84%) delete mode 100644 src/template_project/application/user/notification_device/__init__.py diff --git a/src/template_project/adapters/data_gateways/notification_device.py b/src/template_project/adapters/data_gateways/notification_device.py index 5c0dbc2..76b6457 100644 --- a/src/template_project/adapters/data_gateways/notification_device.py +++ b/src/template_project/adapters/data_gateways/notification_device.py @@ -4,9 +4,9 @@ from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from template_project.adapters.data_gateways.tables import notification_device_table +from template_project.application.notification_device.data_gateway import NotificationDeviceDataGateway +from template_project.application.notification_device.entity import NotificationDevice from template_project.application.user.entity import UserId -from template_project.application.user.notification_device.data_gateway import NotificationDeviceDataGateway -from template_project.application.user.notification_device.entity import NotificationDevice class DefaultNotificationDeviceDataGateway(NotificationDeviceDataGateway): @@ -18,3 +18,16 @@ class DefaultNotificationDeviceDataGateway(NotificationDeviceDataGateway): statement = select(NotificationDevice).where(notification_device_table.c.user_id == user_id) result = await self._session.execute(statement) return result.scalar_one_or_none() + + @override + async def load_by_user_id_and_device_id( + self, + user_id: UserId, + device_id: str, + ) -> NotificationDevice | None: + statement = select(NotificationDevice).where( + notification_device_table.c.user_id == user_id, + notification_device_table.c.device_id == device_id, + ) + result = await self._session.execute(statement) + return result.scalar_one_or_none() diff --git a/src/template_project/adapters/data_gateways/tables.py b/src/template_project/adapters/data_gateways/tables.py index 411130e..ffa4e3e 100644 --- a/src/template_project/adapters/data_gateways/tables.py +++ b/src/template_project/adapters/data_gateways/tables.py @@ -14,8 +14,8 @@ from sqlalchemy.orm import registry from template_project.application.access_token.entity import AccessToken from template_project.application.auth_identity.entity import AuthIdentity, AuthMethod +from template_project.application.notification_device.entity import NotificationDevice from template_project.application.user.entity import User -from template_project.application.user.notification_device.entity import NotificationDevice from template_project.application.user.profile.entity import Profile meta_data = MetaData() diff --git a/src/template_project/application/user/notification/interactors/__init__.py b/src/template_project/application/notification_device/__init__.py similarity index 100% rename from src/template_project/application/user/notification/interactors/__init__.py rename to src/template_project/application/notification_device/__init__.py diff --git a/src/template_project/application/user/notification_device/data_gateway.py b/src/template_project/application/notification_device/data_gateway.py similarity index 51% rename from src/template_project/application/user/notification_device/data_gateway.py rename to src/template_project/application/notification_device/data_gateway.py index 0225fcd..2805924 100644 --- a/src/template_project/application/user/notification_device/data_gateway.py +++ b/src/template_project/application/notification_device/data_gateway.py @@ -1,11 +1,19 @@ from abc import abstractmethod from typing import Protocol +from template_project.application.notification_device.entity import NotificationDevice from template_project.application.user.entity import UserId -from template_project.application.user.notification_device.entity import NotificationDevice class NotificationDeviceDataGateway(Protocol): @abstractmethod async def load_by_user_id(self, user_id: UserId) -> NotificationDevice | None: raise NotImplementedError + + @abstractmethod + async def load_by_user_id_and_device_id( + self, + user_id: UserId, + device_id: str, + ) -> NotificationDevice | None: + raise NotImplementedError diff --git a/src/template_project/application/user/notification_device/entity.py b/src/template_project/application/notification_device/entity.py similarity index 100% rename from src/template_project/application/user/notification_device/entity.py rename to src/template_project/application/notification_device/entity.py diff --git a/src/template_project/application/user/notification_device/errors.py b/src/template_project/application/notification_device/errors.py similarity index 100% rename from src/template_project/application/user/notification_device/errors.py rename to src/template_project/application/notification_device/errors.py diff --git a/src/template_project/application/notification_device/interactors/register_device.py b/src/template_project/application/notification_device/interactors/register_device.py new file mode 100644 index 0000000..2e64fa8 --- /dev/null +++ b/src/template_project/application/notification_device/interactors/register_device.py @@ -0,0 +1,37 @@ +from template_project.application.common.data_structure import to_data_structure +from template_project.application.common.identity_provider import IdentityProvider +from template_project.application.common.interactor import to_interactor +from template_project.application.common.unit_of_work import UnitOfWork +from template_project.application.notification_device.data_gateway import NotificationDeviceDataGateway +from template_project.application.notification_device.entity import NotificationDevice + + +@to_data_structure +class RegisterNotificationDeviceRequest: + device_id: str + + +@to_interactor +class RegisterNotificationDeviceInteractor: + identity_provider: IdentityProvider + notification_device_data_gateway: NotificationDeviceDataGateway + unit_of_work: UnitOfWork + + async def execute(self, request: RegisterNotificationDeviceRequest) -> None: + current_user = await self.identity_provider.get_current_user() + + existing_device = await self.notification_device_data_gateway.load_by_user_id_and_device_id( + user_id=current_user.id, + device_id=request.device_id, + ) + + if existing_device: + return + + notification_device = NotificationDevice.factory( + user_id=current_user.id, + device_id=request.device_id, + ) + + await self.unit_of_work.add(notification_device) + await self.unit_of_work.commit() diff --git a/src/template_project/application/user/notification/interactors/send_notification.py b/src/template_project/application/notification_device/interactors/send_notification.py similarity index 84% rename from src/template_project/application/user/notification/interactors/send_notification.py rename to src/template_project/application/notification_device/interactors/send_notification.py index 7ebc6d8..0eeefe5 100644 --- a/src/template_project/application/user/notification/interactors/send_notification.py +++ b/src/template_project/application/notification_device/interactors/send_notification.py @@ -2,8 +2,8 @@ from template_project.application.common.data_structure import to_data_structure from template_project.application.common.identity_provider import IdentityProvider from template_project.application.common.interactor import to_interactor from template_project.application.common.notifications.service import NotificationService -from template_project.application.user.notification_device.data_gateway import NotificationDeviceDataGateway -from template_project.application.user.notification_device.errors import NotificationDeviceNotFoundError +from template_project.application.notification_device.data_gateway import NotificationDeviceDataGateway +from template_project.application.notification_device.errors import NotificationDeviceNotFoundError @to_data_structure diff --git a/src/template_project/application/user/notification_device/__init__.py b/src/template_project/application/user/notification_device/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/template_project/web_api/ioc/interactor.py b/src/template_project/web_api/ioc/interactor.py index 264d56b..ce8fac4 100644 --- a/src/template_project/web_api/ioc/interactor.py +++ b/src/template_project/web_api/ioc/interactor.py @@ -2,7 +2,10 @@ from dishka import BaseScope, Provider, Scope, provide_all from template_project.application.auth_identity.interactors.sign_in import SignInInteractor from template_project.application.auth_identity.interactors.sign_up import SignUpInteractor -from template_project.application.user.notification.interactors.send_notification import NotificationInteractor +from template_project.application.notification_device.interactors.register_device import ( + RegisterNotificationDeviceInteractor, +) +from template_project.application.notification_device.interactors.send_notification import NotificationInteractor from template_project.application.user.profile.interactors.get_profile import GetProfileInteractor from template_project.application.user.profile.interactors.patch_profile import PatchProfileInteractor @@ -16,4 +19,5 @@ class InteractorProvider(Provider): GetProfileInteractor, PatchProfileInteractor, NotificationInteractor, + RegisterNotificationDeviceInteractor, ) diff --git a/src/template_project/web_api/routes/notification.py b/src/template_project/web_api/routes/notification.py index 2345465..c258c66 100644 --- a/src/template_project/web_api/routes/notification.py +++ b/src/template_project/web_api/routes/notification.py @@ -4,11 +4,15 @@ from fastapi import APIRouter, Depends, HTTPException, status from fastapi.security import HTTPBearer from pydantic import BaseModel -from template_project.application.user.notification.interactors.send_notification import ( +from template_project.application.notification_device.errors import NotificationDeviceNotFoundError +from template_project.application.notification_device.interactors.register_device import ( + RegisterNotificationDeviceInteractor, + RegisterNotificationDeviceRequest, +) +from template_project.application.notification_device.interactors.send_notification import ( NotificationInteractor, SendNotificationRequest, ) -from template_project.application.user.notification_device.errors import NotificationDeviceNotFoundError security = HTTPBearer() router = APIRouter(route_class=DishkaRoute, tags=["Notifications"], dependencies=[Depends(security)]) @@ -19,6 +23,10 @@ class SendNotificationRequestModel(BaseModel): body: str +class RegisterNotificationDeviceRequestModel(BaseModel): + device_id: str + + @router.post("/notifications/send") async def send_notification( request: SendNotificationRequestModel, @@ -29,3 +37,12 @@ async def send_notification( await interactor.send_notification(notification_request) except NotificationDeviceNotFoundError as error: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Notification device not found") from error + + +@router.post("/notifications/register-device") +async def register_notification_device( + request: RegisterNotificationDeviceRequestModel, + interactor: FromDishka[RegisterNotificationDeviceInteractor], +) -> None: + register_request = RegisterNotificationDeviceRequest(device_id=request.device_id) + await interactor.execute(register_request)