Skip to content

Commit

Permalink
Merge branch 'add-breadcrumbs-endpoints-#87' into replace-paths-with-…
Browse files Browse the repository at this point in the history
…ids-#87
  • Loading branch information
joelvdavies committed Oct 19, 2023
2 parents d77cb45 + c4e40b8 commit df83b77
Show file tree
Hide file tree
Showing 11 changed files with 168 additions and 15 deletions.
4 changes: 2 additions & 2 deletions inventory_management_system_api/core/breadcrumbs.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from inventory_management_system_api.core.consts import BREADCRUMBS_TRAIL_MAX_LENGTH
from inventory_management_system_api.core.custom_object_id import CustomObjectId
from inventory_management_system_api.core.exceptions import DatabaseIntegrityError, EntityNotFoundError
from inventory_management_system_api.core.exceptions import DatabaseIntegrityError, MissingRecordError
from inventory_management_system_api.schemas.breadcrumbs import BreadcrumbsGetSchema

logger = logging.getLogger()
Expand Down Expand Up @@ -67,7 +67,7 @@ def query_breadcrumbs(entity_id: str, entity_collection: Collection, graph_looku
)
)[0]["result"]
if len(result) == 0:
raise EntityNotFoundError(f"Entity with the ID {entity_id} was not found in the collection {graph_lookup_from}")
raise MissingRecordError(f"Entity with the ID {entity_id} was not found in the collection {graph_lookup_from}")
for element in result:
trail.append((str(element["_id"]), element["name"]))
full_trail = result[0]["parent_id"] is None
Expand Down
6 changes: 0 additions & 6 deletions inventory_management_system_api/core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,3 @@ class DatabaseIntegrityError(DatabaseError):
"""
Exception raised when something is found in the database that shouldn't have been
"""


class EntityNotFoundError(Exception):
"""
An entity with a given ID was not found
"""
16 changes: 16 additions & 0 deletions inventory_management_system_api/repositories/catalogue_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

from inventory_management_system_api.core.custom_object_id import CustomObjectId
from inventory_management_system_api.core.database import get_database
from inventory_management_system_api.core.exceptions import MissingRecordError
from inventory_management_system_api.models.catalogue_item import CatalogueItemOut, CatalogueItemIn

logger = logging.getLogger()
Expand Down Expand Up @@ -41,6 +42,21 @@ def create(self, catalogue_item: CatalogueItemIn) -> CatalogueItemOut:
catalogue_item = self.get(str(result.inserted_id))
return catalogue_item

def delete(self, catalogue_item_id: str) -> None:
"""
Delete a catalogue item by its ID from a MongoDB database.
:param catalogue_item_id: The ID of the catalogue item to delete.
:raises MissingRecordError: If the catalogue item doesn't exist.
"""
catalogue_item_id = CustomObjectId(catalogue_item_id)
# pylint: disable=fixme
# TODO - (when the relevant item logic is implemented) check if catalogue item has children elements
logger.info("Deleting catalogue item with ID: %s from the database", catalogue_item_id)
result = self._collection.delete_one({"_id": catalogue_item_id})
if result.deleted_count == 0:
raise MissingRecordError(f"No catalogue item found with ID: {str(catalogue_item_id)}")

def get(self, catalogue_item_id: str) -> Optional[CatalogueItemOut]:
"""
Retrieve a catalogue item by its ID from a MongoDB database.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
ChildrenElementsExistError,
DatabaseIntegrityError,
DuplicateRecordError,
EntityNotFoundError,
InvalidObjectIdError,
LeafCategoryError,
MissingRecordError,
Expand Down Expand Up @@ -77,7 +76,7 @@ def get_catalogue_category_breadcrumbs(
# pylint: disable=missing-function-docstring
try:
return catalogue_category_service.get_breadcrumbs(catalogue_category_id)
except (InvalidObjectIdError, EntityNotFoundError) as exc:
except (MissingRecordError, InvalidObjectIdError) as exc:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail="Catalogue category with such ID was not found"
) from exc
Expand Down
20 changes: 20 additions & 0 deletions inventory_management_system_api/routers/v1/catalogue_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,23 @@ def partial_update_catalogue_item(
message = "Adding a catalogue item to a non-leaf catalogue category is not allowed"
logger.exception(message)
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=message) from exc


@router.delete(
path="/{catalogue_item_id}",
summary="Delete a catalogue item by ID",
response_description="Catalogue item deleted successfully",
status_code=status.HTTP_204_NO_CONTENT,
)
def delete_catalogue_item(
catalogue_item_id: str = Path(description="The ID of the catalogue item to delete"),
catalogue_item_service: CatalogueItemService = Depends(),
) -> None:
# pylint: disable=missing-function-docstring
logger.info("Deleting catalogue item with ID: %s", catalogue_item_id)
try:
catalogue_item_service.delete(catalogue_item_id)
except (MissingRecordError, InvalidObjectIdError) as exc:
message = "A catalogue item with such ID was not found"
logger.exception(message)
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=message) from exc
3 changes: 1 addition & 2 deletions inventory_management_system_api/routers/v1/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
ChildrenElementsExistError,
DatabaseIntegrityError,
DuplicateRecordError,
EntityNotFoundError,
InvalidObjectIdError,
MissingRecordError,
)
Expand Down Expand Up @@ -70,7 +69,7 @@ def get_system_breadcrumbs(
# pylint: disable=duplicate-code
try:
return system_service.get_breadcrumbs(system_id)
except (InvalidObjectIdError, EntityNotFoundError) as exc:
except (MissingRecordError, InvalidObjectIdError) as exc:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="System with such ID was not found") from exc
except DatabaseIntegrityError as exc:
raise HTTPException(
Expand Down
8 changes: 8 additions & 0 deletions inventory_management_system_api/services/catalogue_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,14 @@ def create(self, catalogue_item: CatalogueItemPostRequestSchema) -> CatalogueIte
)
)

def delete(self, catalogue_item_id: str) -> None:
"""
Delete a catalogue item by its ID.
:param catalogue_item_id: The ID of the catalogue item to delete.
"""
return self._catalogue_item_repository.delete(catalogue_item_id)

def get(self, catalogue_item_id: str) -> Optional[CatalogueItemOut]:
"""
Retrieve a catalogue item by its ID.
Expand Down
44 changes: 44 additions & 0 deletions test/e2e/test_catalogue_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,50 @@ def test_create_catalogue_item_with_invalid_value_type_for_boolean_property(test
)


def test_delete_catalogue_item(test_client):
"""
Test deleting a catalogue item.
"""
response = test_client.post("/v1/catalogue-categories", json=CATALOGUE_CATEGORY_POST_A)
catalogue_item_post = get_catalogue_item_a_dict(response.json()["id"])
response = test_client.post("/v1/catalogue-items", json=catalogue_item_post)
catalogue_item = response.json()

response = test_client.delete(f"/v1/catalogue-items/{catalogue_item['id']}")

assert response.status_code == 204
response = test_client.delete(f"/v1/catalogue-items/{catalogue_item['id']}")
assert response.status_code == 404


def test_delete_catalogue_item_with_invalid_id(test_client):
"""
Test deleting a catalogue item with an invalid ID.
"""
response = test_client.delete("/v1/catalogue-items/invalid")

assert response.status_code == 404
assert response.json()["detail"] == "A catalogue item with such ID was not found"


def test_delete_catalogue_item_with_nonexistent_id(test_client):
"""
Test deleting a catalogue item with a nonexistent ID.
"""
response = test_client.delete(f"/v1/catalogue-items/{str(ObjectId())}")

assert response.status_code == 404
assert response.json()["detail"] == "A catalogue item with such ID was not found"


def test_delete_catalogue_item_with_children_items():
"""
Test deleting a catalogue item with children items.
"""
# pylint: disable=fixme
# TODO - Implement this test when the relevant item logic is implemented


def test_get_catalogue_item(test_client):
"""
Test getting a catalogue item.
Expand Down
4 changes: 2 additions & 2 deletions test/unit/core/test_breacrumbs.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
from inventory_management_system_api.core.custom_object_id import CustomObjectId
from inventory_management_system_api.core.exceptions import (
DatabaseIntegrityError,
EntityNotFoundError,
InvalidObjectIdError,
MissingRecordError,
)

MOCK_AGGREGATE_RESPONSE_LESS_THAN_MAX_LENGTH = [
Expand Down Expand Up @@ -138,7 +138,7 @@ def test_query_breadcrumbs_when_entity_not_found(self):

mock_entity_collection.aggregate.return_value = MOCK_AGGREGATE_RESPONSE_NON_EXISTENT_ID

with pytest.raises(EntityNotFoundError) as exc:
with pytest.raises(MissingRecordError) as exc:
breadcrumbs.query_breadcrumbs(
entity_id=entity_id, entity_collection=mock_entity_collection, graph_lookup_from=graph_lookup_from
)
Expand Down
62 changes: 61 additions & 1 deletion test/unit/repositories/test_catalogue_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from bson import ObjectId

from inventory_management_system_api.core.custom_object_id import CustomObjectId
from inventory_management_system_api.core.exceptions import InvalidObjectIdError
from inventory_management_system_api.core.exceptions import InvalidObjectIdError, MissingRecordError
from inventory_management_system_api.models.catalogue_item import (
CatalogueItemOut,
Property,
Expand Down Expand Up @@ -81,6 +81,66 @@ def test_create(test_helpers, database_mock, catalogue_item_repository):
assert created_catalogue_item == catalogue_item


def test_delete(test_helpers, database_mock, catalogue_item_repository):
"""
Test deleting a catalogue item.
Verify that the `delete` method properly handles the deletion of a catalogue item by ID.
"""
catalogue_item_id = str(ObjectId())

# Mock `delete_one` to return that one document has been deleted
test_helpers.mock_delete_one(database_mock.catalogue_items, 1)

# pylint: disable=fixme
# TODO - (when the relevant item logic is implemented) mock it so that no children items are returned

catalogue_item_repository.delete(catalogue_item_id)

database_mock.catalogue_items.delete_one.assert_called_once_with({"_id": CustomObjectId(catalogue_item_id)})


def test_delete_with_children_items():
"""
Test deleting a catalogue item with children items.
Verify that the `delete` method properly handles the deletion of a catalogue item with children items.
"""
# pylint: disable=fixme
# TODO - Implement this test when the relevant item logic is implemented


def test_delete_with_invalid_id(catalogue_item_repository):
"""
Test deleting a catalogue item with an invalid ID.
Verify that the `delete` method properly handles the deletion of a catalogue item with an invalid ID.
"""
with pytest.raises(InvalidObjectIdError) as exc:
catalogue_item_repository.delete("invalid")
assert str(exc.value) == "Invalid ObjectId value 'invalid'"


def test_delete_with_nonexistent_id(test_helpers, database_mock, catalogue_item_repository):
"""
Test deleting a catalogue item with a nonexistent ID.
Verify that the `delete` method properly handles the deletion of a catalogue item with a nonexistent ID.
"""
catalogue_item_id = str(ObjectId())

# Mock `delete_one` to return that no document has been deleted
test_helpers.mock_delete_one(database_mock.catalogue_items, 0)

# pylint: disable=fixme
# TODO - (when the relevant item logic is implemented) mock it so that no children items are returned

with pytest.raises(MissingRecordError) as exc:
catalogue_item_repository.delete(catalogue_item_id)
assert str(exc.value) == f"No catalogue item found with ID: {catalogue_item_id}"
database_mock.catalogue_items.delete_one.assert_called_once_with({"_id": CustomObjectId(catalogue_item_id)})


def test_get(test_helpers, database_mock, catalogue_item_repository):
"""
Test getting a catalogue item.
Expand Down
13 changes: 13 additions & 0 deletions test/unit/services/test_catalogue_item.py
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,19 @@ def test_create_with_with_invalid_value_type_for_boolean_property(
catalogue_category_repository_mock.get.assert_called_once_with(catalogue_category.id)


def test_delete(catalogue_item_repository_mock, catalogue_item_service):
"""
Test deleting a catalogue item.
Verify that the `delete` method properly handles the deletion of a catalogue item by ID.
"""
catalogue_item_id = str(ObjectId)

catalogue_item_service.delete(catalogue_item_id)

catalogue_item_repository_mock.delete.assert_called_once_with(catalogue_item_id)


def test_get(test_helpers, catalogue_item_repository_mock, catalogue_item_service):
"""
Test getting a catalogue item.
Expand Down

0 comments on commit df83b77

Please sign in to comment.