diff --git a/api/v1/routes/invitations.py b/api/v1/routes/invitations.py index 4265f257a..686b7d236 100644 --- a/api/v1/routes/invitations.py +++ b/api/v1/routes/invitations.py @@ -1,10 +1,11 @@ -from fastapi import APIRouter, Depends, HTTPException, Request +from fastapi import APIRouter, Depends, HTTPException, Request, status from sqlalchemy.orm import Session from urllib.parse import urlparse, parse_qs from api.v1.schemas import invitations from api.db.database import get_db as get_session from api.v1.services import invite from api.v1.models.user import User +from api.utils.success_response import success_response from api.v1.services.user import user_service import logging @@ -42,4 +43,18 @@ async def add_user_to_organization( logging.info(f"Processing invitation ID: {invite_id}") - return invite.InviteService.add_user_to_organization(invite_id, session) \ No newline at end of file + return invite.InviteService.add_user_to_organization(invite_id, session) + +@invites.delete("/{invite_id}", status_code=status.HTTP_204_NO_CONTENT) +def delete_invite( + invite_id: str, + db: Session = Depends(get_session), + admin: User = Depends(user_service.get_current_super_admin) +): + """ Delete invite from database """ + invite_is_deleted = invite.InviteService.delete(db, invite_id) + + if not invite_is_deleted: + raise HTTPException(status_code=404, detail="Invalid invitation id") + + logging.info(f"Deleted invite. ID: {invite_id}") \ No newline at end of file diff --git a/api/v1/services/invite.py b/api/v1/services/invite.py index 5f57222c7..87ac4d9c1 100644 --- a/api/v1/services/invite.py +++ b/api/v1/services/invite.py @@ -11,10 +11,11 @@ from api.v1.models.user import User from api.v1.models.associations import user_organization_association from api.v1.schemas import invitations +from api.core.base.services import Service from urllib.parse import urlencode -class InviteService: +class InviteService(Service): @staticmethod def create( invite: invitations.InvitationCreate, request: Request, session: Session @@ -139,5 +140,35 @@ def add_user_to_organization(invite_id: str, session: Session): status_code=500, detail="An error occurred while adding the user to the organization", ) + @staticmethod + def delete(session: Session, id: str): + """Function to delete invite link + + Args: + session(Session): The current ORM session object. + id(str): Invite id string + + Returns: + True if delete is successful else False + + """ + invite = ( + session.query(Invitation).filter_by(id=id).first() + ) + + if invite is None: + return False + session.delete(invite) + session.commit() + return True + + def fetch(self): + pass + + def fetch_all(self): + pass + def update(self): + pass + invite_service = InviteService() \ No newline at end of file diff --git a/tests/v1/invitation/test_delete_invitation.py b/tests/v1/invitation/test_delete_invitation.py new file mode 100644 index 000000000..9f01222b0 --- /dev/null +++ b/tests/v1/invitation/test_delete_invitation.py @@ -0,0 +1,72 @@ +# Dependencies: +# pip install pytest-mock +import pytest +from unittest.mock import patch, MagicMock +from fastapi.testclient import TestClient +from main import app +from api.v1.services.user import user_service +from api.db.database import get_db +from sqlalchemy.orm import Session +from datetime import datetime +from api.v1.services.user import oauth2_scheme + + +def mock_deps(): + return MagicMock(id="user_id") + +def mock_oauth(): + return 'access_token' + +def mock_db(): + return MagicMock(spec=Session) + +@pytest.fixture +def client(): + client = TestClient(app) + yield client + +DELETE_ENDPOINT = "/api/v1/invite/invite_id" +class TestCodeUnderTest: + @classmethod + def setup_class(cls): + app.dependency_overrides[user_service.get_current_super_admin] = mock_deps + app.dependency_overrides[get_db] = mock_db + + @classmethod + def teardown_class(cls): + app.dependency_overrides = {} + + # Successfully delete invite from to the database + def test_delete_invite_success(self, client): + + response = client.delete(DELETE_ENDPOINT) + + assert response.status_code == 204 + + # Invalid invite id + def test_delete_invite_invalid_id(self, client): + + + with patch('api.v1.services.invite.InviteService.delete', return_value=False) as tmp: + response = client.delete(DELETE_ENDPOINT) + assert response.status_code == 404 + assert response.json()['message'] == "Invalid invitation id" + + # Handling unauthorized request + def test_delete_invite_unauth(self, client): + app.dependency_overrides = {} + + response = client.delete(DELETE_ENDPOINT) + assert response.status_code == 401 + assert response.json()['message'] == 'Not authenticated' + + # Handling forbidden request + def test_delete_invite_forbidden(self, client): + app.dependency_overrides = {} + app.dependency_overrides[get_db] = mock_db + app.dependency_overrides[oauth2_scheme] = mock_oauth + + with patch('api.v1.services.user.user_service.get_current_user', return_value=MagicMock(is_super_admin=False)) as cu: + response = client.delete(DELETE_ENDPOINT) + assert response.status_code == 403 + assert response.json()['message'] == 'You do not have permission to access this resource'