-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Chim/feature/setup notification repository (#284)
Add fcm token repository and Api endpoint for register and unregister fcm token. Add a new domain FCMToken for representing fcm token. - [Jira](https://fireapp-emergiq-2024.atlassian.net/jira/software/projects/FIR/boards/2?selectedIssue=FIR-99) - [Documentation](https://fireapp-emergiq-2024.atlassian.net/wiki/spaces/fireapp202/pages/99713028/Notification+Feature) merged new changes from origin/main into branch to make merging more seamless
- Loading branch information
1 parent
71aef6d
commit 9cd3c64
Showing
10 changed files
with
207 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from .api import FCMToken |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import logging | ||
from controllers.v2.fcm_tokens.response_models import response_model | ||
from controllers.v2.v2_blueprint import v2_api | ||
from exception import InvalidTokenError | ||
from flask_restful import Resource, reqparse, marshal_with | ||
from repository.fcm_token_repository import FCMTokenRepository | ||
from repository.user_repository import UserRepository | ||
from services.jwk import requires_auth, JWKService | ||
|
||
|
||
parser = reqparse.RequestParser() | ||
parser.add_argument('token', type=str, required=True, help ="Token must be provided.") | ||
parser.add_argument('device_type', type=str, required=True, help ="DeviceType must be provided.") | ||
unregister_parser = reqparse.RequestParser() | ||
unregister_parser.add_argument('token', type=str, required=True, help ="Token must be provided.") | ||
|
||
|
||
class FCMToken(Resource): | ||
|
||
token_repository: FCMTokenRepository | ||
user_repository: UserRepository | ||
|
||
def __init__( | ||
self, | ||
token_repository: FCMTokenRepository = FCMTokenRepository(), | ||
user_repository: UserRepository = UserRepository() | ||
): | ||
self.token_repository = token_repository | ||
self.user_repository = user_repository | ||
|
||
@requires_auth | ||
@marshal_with(response_model) | ||
def post(self, user_id: int): | ||
|
||
# Decode authenticated user id | ||
authenticated_user_id = JWKService.decode_user_id() | ||
|
||
# Check if they are matched | ||
if authenticated_user_id != user_id: | ||
return {"message": "User ID mismatch"}, 403 | ||
|
||
# Check if decoding fail | ||
args = parser.parse_args() | ||
fcm_token = args['token'] | ||
device_type = args['device_type'] | ||
|
||
try: | ||
# 1. Check if the user exist | ||
if not self.user_repository.check_user_exists(user_id): | ||
return {"message": "User not found"}, 400 | ||
|
||
# 2. Register the token for the user | ||
self.token_repository.register_token(user_id, fcm_token, device_type) | ||
return {"message": "FCM token registered successfully"}, 200 | ||
|
||
except Exception as e: | ||
|
||
logging.error(f"Error registering FCM token: {e}") | ||
return {"message": "Internal server error"}, 500 | ||
|
||
@requires_auth | ||
@marshal_with(response_model) | ||
def delete(self, user_id: int): | ||
|
||
# Decode authenticated user id | ||
authenticated_user_id = JWKService.decode_user_id() | ||
|
||
# Check if they are matched | ||
if authenticated_user_id != user_id: | ||
return {"message": "User ID mismatch"}, 403 | ||
|
||
args = unregister_parser.parse_args() | ||
fcm_token = args['token'] | ||
|
||
try: | ||
# Check if user exist | ||
if not self.user_repository.check_user_exists(user_id): | ||
return {"message": "User not found"}, 400 | ||
|
||
# Unregister the token | ||
self.token_repository.unregister_token(user_id, fcm_token) | ||
return {"message": "FCM token unregistered successfully"}, 200 | ||
|
||
except InvalidTokenError as e: | ||
logging.error(f"Error unregistering FCM token: {e}") | ||
return {"message": "Invalid FCM token"}, 400 | ||
|
||
except Exception as e: | ||
logging.error(f"Error unregistering FCM token: {e}") | ||
return {"message": "Internal server error"}, 500 | ||
|
||
|
||
v2_api.add_resource(FCMToken,'/v2/user/<int:user_id>/token') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
from flask_restful import fields | ||
|
||
|
||
response_model = { | ||
'message': fields.String | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
from datetime import datetime | ||
from domain.base import Base | ||
from sqlalchemy import Column, String, ForeignKey, Integer, Boolean, TIMESTAMP | ||
from sqlalchemy.orm import relationship | ||
|
||
|
||
class FCMToken(Base): | ||
|
||
__tablename__ = 'fcm_tokens' | ||
id = Column(Integer, primary_key=True, autoincrement=True) | ||
user_id = Column(Integer, ForeignKey('user.id'), name='user_id', nullable=False) | ||
fcm_token = Column(String(255), name='fcm_token', nullable=False) | ||
device_type = Column(String(50), name='device_type', nullable=False) | ||
created_at = Column(TIMESTAMP, name='created_at', default=datetime.now(), nullable=False) | ||
updated_at = Column(TIMESTAMP, name='updated_at', default=datetime.now(), onupdate=datetime.now, nullable=False) | ||
is_active = Column(Boolean, name='is_active', default=True, nullable=False) | ||
|
||
user = relationship("User") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
from .client_exception import EventNotFoundError, InvalidArgumentError | ||
from .invalid_token_exception import InvalidTokenError | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
from .fireapp_exception import FireAppException | ||
|
||
|
||
class InvalidTokenError(FireAppException): | ||
""" | ||
A custom exception used to handle invalid tokens for the user. | ||
""" | ||
def __init__(self, message='Invalid token for the user'): | ||
self.message = message | ||
super().__init__(self.message) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import logging | ||
from datetime import datetime | ||
from domain.entity.fcm_tokens import FCMToken | ||
from domain import session_scope | ||
from exception import InvalidTokenError | ||
from sqlalchemy.exc import SQLAlchemyError | ||
|
||
|
||
class FCMTokenRepository: | ||
def __init__(self): | ||
pass | ||
|
||
def register_token(self, user_id: int, fcm_token: str, device_type: str) -> None: | ||
|
||
with session_scope() as session: | ||
try: | ||
# 1. Check if there is user given userId | ||
existing_token = session.query(FCMToken).filter_by(user_id=user_id, fcm_token=fcm_token).first() | ||
|
||
if existing_token: | ||
existing_token.updated_at = datetime.now() | ||
session.commit() | ||
logging.info(f" A New token is registered for user {user_id}") | ||
else: | ||
new_token = FCMToken( | ||
user_id=user_id, | ||
fcm_token=fcm_token, | ||
device_type=device_type, | ||
created_at=datetime.now(), | ||
updated_at=datetime.now() | ||
) | ||
session.add(new_token) | ||
session.commit() | ||
logging.info(f" A New token is registered for user {user_id}") | ||
|
||
except Exception as e: | ||
logging.error(f"Error registering FCM token for user {user_id}: {e}") | ||
session.rollback() | ||
|
||
def unregister_token(self, user_id: int, fcm_token: str) -> None: | ||
|
||
with session_scope() as session: | ||
try: | ||
existing_token = session.query(FCMToken).filter_by(user_id=user_id, fcm_token=fcm_token).first() | ||
|
||
if existing_token: | ||
session.delete(existing_token) | ||
session.commit() | ||
logging.info(f" Unregistered the token for user {user_id}") | ||
else: | ||
logging.error(f"Invalid token for user {user_id}") | ||
raise InvalidTokenError(f"Invalid token for user {user_id}") | ||
|
||
except SQLAlchemyError as e: | ||
logging.error(f"Database error while unregistering FCM token for user {user_id}:{e}") | ||
session.rollback() | ||
raise e |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters