Skip to content

Commit

Permalink
Pdct 313 Added endpoint for deleting events (#28)
Browse files Browse the repository at this point in the history
  • Loading branch information
katybaulch authored Oct 23, 2023
1 parent b1ef7cf commit 6c993a4
Show file tree
Hide file tree
Showing 11 changed files with 242 additions and 1 deletion.
28 changes: 28 additions & 0 deletions app/api/api_v1/routers/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,31 @@ async def update_event(
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=detail)

return event


@r.delete(
"/events/{import_id}",
)
async def delete_event(
import_id: str,
) -> None:
"""
Deletes a specific event given the import id.
:param str import_id: Specified import_id.
:raises HTTPException: If the event is not found a 404 is returned.
"""
try:
event_deleted = event_service.delete(import_id)
except ValidationError as e:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=e.message)
except RepositoryError as e:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail=e.message
)

if not event_deleted:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Event not deleted: {import_id}",
)
1 change: 1 addition & 0 deletions app/clients/db/models/law_policy/family.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ class EventStatus(BaseModelEnum):
# case we will need to validate, remove unnecessary duplicates & create new
# events through a data cleaning exercise.
DUPLICATED = "Duplicated"
DELETED = "Deleted"


class FamilyEventType(Base):
Expand Down
30 changes: 29 additions & 1 deletion app/repository/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def update(db: Session, import_id: str, event: EventWriteDTO) -> bool:
:param db Session: the database connection
:param str import_id: The event import id to change.
:param DocumentDTO event: The new values
:param EventDTO event: The new values
:return bool: True if new values were set otherwise false.
"""
new_values = event.model_dump()
Expand Down Expand Up @@ -208,6 +208,34 @@ def update(db: Session, import_id: str, event: EventWriteDTO) -> bool:
return True


def delete(db: Session, import_id: str) -> bool:
"""
Deletes a single event by the import id.
:param db Session: the database connection
:param str import_id: The event import id to delete.
:return bool: True if deleted False if not.
"""

found = (
db.query(FamilyEvent).filter(FamilyEvent.import_id == import_id).one_or_none()
)
if found is None:
return False

result = db.execute(
db_update(FamilyEvent)
.where(FamilyEvent.import_id == import_id)
.values(status=EventStatus.DELETED)
)
if result.rowcount == 0: # type: ignore
msg = f"Could not delete event : {import_id}"
_LOGGER.error(msg)
raise RepositoryError(msg)

return True


def count(db: Session) -> Optional[int]:
"""
Counts the number of family events in the repository.
Expand Down
15 changes: 15 additions & 0 deletions app/service/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,21 @@ def update(
raise RepositoryError(f"Error when updating event {import_id}")


@db_session.with_transaction(__name__)
@validate_call(config=ConfigDict(arbitrary_types_allowed=True))
def delete(import_id: str, db: Session = db_session.get_db()) -> bool:
"""
Deletes the event specified by the import_id.
:param str import_id: The import_id of the event to delete.
:raises RepositoryError: raised on a database error.
:raises ValidationError: raised should the import_id be invalid.
:return bool: True if deleted else False.
"""
id.validate(import_id)
return event_repo.delete(db, import_id)


@validate_call(config=ConfigDict(arbitrary_types_allowed=True))
def count() -> Optional[int]:
"""
Expand Down
89 changes: 89 additions & 0 deletions integration_tests/event/test_delete.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
from fastapi.testclient import TestClient
from fastapi import status
from sqlalchemy.orm import Session
from app.clients.db.models.law_policy import FamilyEvent, EventStatus
from integration_tests.setup_db import setup_db
import app.repository.event as event_repo


def test_delete_event(client: TestClient, test_db: Session, admin_user_header_token):
setup_db(test_db)
response = client.delete("/api/v1/events/E.0.0.2", headers=admin_user_header_token)
assert response.status_code == status.HTTP_200_OK
assert test_db.query(FamilyEvent).count() == 3
assert (
test_db.query(FamilyEvent)
.filter(FamilyEvent.status == EventStatus.DELETED)
.count()
== 1
)
assert test_db.query(FamilyEvent).count() == 3


def test_delete_event_when_not_authenticated(
client: TestClient, test_db: Session, mocker
):
setup_db(test_db)
mocker.spy(event_repo, "delete")
response = client.delete(
"/api/v1/events/E.0.0.2",
)
assert response.status_code == status.HTTP_401_UNAUTHORIZED
assert test_db.query(FamilyEvent).count() == 3
assert (
test_db.query(FamilyEvent)
.filter(FamilyEvent.status == EventStatus.DELETED)
.count()
== 0
)
assert test_db.query(FamilyEvent).count() == 3
assert event_repo.delete.call_count == 0


def test_delete_event_rollback(
client: TestClient,
test_db: Session,
rollback_event_repo,
admin_user_header_token,
):
setup_db(test_db)
response = client.delete("/api/v1/events/E.0.0.2", headers=admin_user_header_token)
assert response.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
assert test_db.query(FamilyEvent).count() == 3
assert (
test_db.query(FamilyEvent)
.filter(FamilyEvent.status == EventStatus.DELETED)
.count()
== 0
)
assert test_db.query(FamilyEvent).count() == 3
assert rollback_event_repo.delete.call_count == 1


def test_delete_event_when_not_found(
client: TestClient, test_db: Session, admin_user_header_token
):
setup_db(test_db)
response = client.delete("/api/v1/events/E.0.0.22", headers=admin_user_header_token)
assert response.status_code == status.HTTP_404_NOT_FOUND
data = response.json()
assert data["detail"] == "Event not deleted: E.0.0.22"
assert test_db.query(FamilyEvent).count() == 3
assert (
test_db.query(FamilyEvent)
.filter(FamilyEvent.status == EventStatus.DELETED)
.count()
== 0
)
assert test_db.query(FamilyEvent).count() == 3


def test_delete_event_when_db_error(
client: TestClient, test_db: Session, bad_event_repo, admin_user_header_token
):
setup_db(test_db)
response = client.delete("/api/v1/events/E.0.0.1", headers=admin_user_header_token)
assert response.status_code == status.HTTP_503_SERVICE_UNAVAILABLE
data = response.json()
assert data["detail"] == "Bad Repo"
assert bad_event_repo.delete.call_count == 1
6 changes: 6 additions & 0 deletions integration_tests/mocks/bad_event_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ def mock_create(_, data: EventCreateDTO) -> Optional[EventReadDTO]:
def mock_update(_, import_id, data: EventReadDTO) -> Optional[EventReadDTO]:
raise RepositoryError("Bad Repo")

def mock_delete(_, import_id: str) -> bool:
raise RepositoryError("Bad Repo")

def mock_get_count(_) -> Optional[int]:
raise RepositoryError("Bad Repo")

Expand All @@ -39,6 +42,9 @@ def mock_get_count(_) -> Optional[int]:
monkeypatch.setattr(repo, "update", mock_update)
mocker.spy(repo, "update")

monkeypatch.setattr(repo, "delete", mock_delete)
mocker.spy(repo, "delete")

monkeypatch.setattr(repo, "count", mock_get_count)
mocker.spy(repo, "count")

Expand Down
8 changes: 8 additions & 0 deletions integration_tests/mocks/rollback_event_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
def mock_rollback_event_repo(event_repo, monkeypatch: MonkeyPatch, mocker):
actual_create = event_repo.create
actual_update = event_repo.update
actual_delete = event_repo.delete

def mock_create_event(db, data: EventCreateDTO) -> Optional[EventReadDTO]:
actual_create(db, data)
Expand All @@ -18,8 +19,15 @@ def mock_update_event(db, import_id: str, data: EventWriteDTO) -> EventReadDTO:
actual_update(db, import_id, data)
raise NoResultFound()

def mock_delete_document(db, import_id: str) -> bool:
actual_delete(db, import_id)
raise NoResultFound()

monkeypatch.setattr(event_repo, "create", mock_create_event)
mocker.spy(event_repo, "create")

monkeypatch.setattr(event_repo, "update", mock_update_event)
mocker.spy(event_repo, "update")

monkeypatch.setattr(event_repo, "delete", mock_delete_document)
mocker.spy(event_repo, "delete")
7 changes: 7 additions & 0 deletions unit_tests/mocks/repos/event_repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ def mock_update(_, import_id: str, data: EventWriteDTO) -> EventReadDTO:
raise exc.NoResultFound()
return create_event_read_dto("a.b.c.d")

def mock_delete(_, import_id: str) -> bool:
maybe_throw()
return not event_repo.return_empty

def mock_get_count(_) -> Optional[int]:
maybe_throw()
if not event_repo.return_empty:
Expand All @@ -64,5 +68,8 @@ def mock_get_count(_) -> Optional[int]:
monkeypatch.setattr(event_repo, "update", mock_update)
mocker.spy(event_repo, "update")

monkeypatch.setattr(event_repo, "delete", mock_delete)
mocker.spy(event_repo, "delete")

monkeypatch.setattr(event_repo, "count", mock_get_count)
mocker.spy(event_repo, "count")
7 changes: 7 additions & 0 deletions unit_tests/mocks/services/event_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ def mock_update_event(
import_id, "family_import_id", data.event_title
)

def mock_delete_event(_) -> bool:
maybe_throw()
return not event_service.missing

def mock_count_event() -> Optional[int]:
maybe_throw()
if event_service.missing:
Expand All @@ -67,5 +71,8 @@ def mock_count_event() -> Optional[int]:
monkeypatch.setattr(event_service, "update", mock_update_event)
mocker.spy(event_service, "update")

monkeypatch.setattr(event_service, "delete", mock_delete_event)
mocker.spy(event_service, "delete")

monkeypatch.setattr(event_service, "count", mock_count_event)
mocker.spy(event_service, "count")
27 changes: 27 additions & 0 deletions unit_tests/routers/test_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,30 @@ def test_update_when_not_found(
data = response.json()
assert data["detail"] == "Event not updated: a.b.c.d"
assert event_service_mock.update.call_count == 1


def test_delete_when_ok(
client: TestClient, event_service_mock, admin_user_header_token
):
response = client.delete("/api/v1/events/event1", headers=admin_user_header_token)
assert response.status_code == status.HTTP_200_OK
assert event_service_mock.delete.call_count == 1


def test_delete_event_fails_if_not_admin(
client: TestClient, event_service_mock, user_header_token
):
response = client.delete("/api/v1/events/event1", headers=user_header_token)
assert response.status_code == status.HTTP_403_FORBIDDEN
assert event_service_mock.delete.call_count == 0


def test_delete_when_not_found(
client: TestClient, event_service_mock, admin_user_header_token
):
event_service_mock.missing = True
response = client.delete("/api/v1/events/event1", headers=admin_user_header_token)
assert response.status_code == status.HTTP_404_NOT_FOUND
data = response.json()
assert data["detail"] == "Event not deleted: event1"
assert event_service_mock.delete.call_count == 1
25 changes: 25 additions & 0 deletions unit_tests/service/test_event_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,31 @@ def test_update_raises_when_invalid_id(
assert event_repo_mock.update.call_count == 0


# --- DELETE


def test_delete(event_repo_mock):
ok = event_service.delete("a.b.c.d")
assert ok
assert event_repo_mock.delete.call_count == 1


def test_delete_when_missing(event_repo_mock):
event_repo_mock.return_empty = True
ok = event_service.delete("a.b.c.d")
assert not ok
assert event_repo_mock.delete.call_count == 1


def test_delete_raises_when_invalid_id(event_repo_mock):
import_id = "invalid"
with pytest.raises(ValidationError) as e:
event_service.delete(import_id)
expected_msg = f"The import id {import_id} is invalid!"
assert e.value.message == expected_msg
assert event_repo_mock.delete.call_count == 0


# --- COUNT


Expand Down

0 comments on commit 6c993a4

Please sign in to comment.