diff --git a/V2/source/activities_data.json b/V2/source/activities_data.json new file mode 100644 index 00000000..0d949902 --- /dev/null +++ b/V2/source/activities_data.json @@ -0,0 +1,66 @@ +[ + { + "name": "Development", + "description": "Development", + "tenant_id": "cc925a5d-9644-4a4f-8d99-0bee49aadd05", + "id": "c61a4a49-3364-49a3-a7f7-0c5f2d15072b", + "_rid": "QUwFAPuumiRhAAAAAAAAAA==", + "_self": "dbs/QUwFAA==/colls/QUwFAPuumiQ=/docs/QUwFAPuumiRhAAAAAAAAAA==/", + "_etag": "\"4e006cc9-0000-0500-0000-607dcc0d0000\"", + "_attachments": "attachments/", + "_last_event_ctx": { + "user_id": "dd76e5d6-3949-46fd-b418-f15bf7c354fa", + "tenant_id": "cc925a5d-9644-4a4f-8d99-0bee49aadd05", + "action": "delete", + "description": null, + "container_id": "activity", + "session_id": null + }, + "deleted": "b4327ba6-9f96-49ee-a9ac-3c1edf525172", + "status": null, + "_ts": 1618856973 + }, + { + "name": "Management", + "description": null, + "tenant_id": "cc925a5d-9644-4a4f-8d99-0bee49aadd05", + "id": "94ec92e2-a500-4700-a9f6-e41eb7b5507c", + "_last_event_ctx": { + "user_id": "dd76e5d6-3949-46fd-b418-f15bf7c354fa", + "tenant_id": "cc925a5d-9644-4a4f-8d99-0bee49aadd05", + "action": "delete", + "description": null, + "container_id": "activity", + "session_id": null + }, + "_rid": "QUwFAPuumiRfAAAAAAAAAA==", + "_self": "dbs/QUwFAA==/colls/QUwFAPuumiQ=/docs/QUwFAPuumiRfAAAAAAAAAA==/", + "_etag": "\"4e0069c9-0000-0500-0000-607dcc0d0000\"", + "_attachments": "attachments/", + "deleted": "7cf6efe5-a221-4fe4-b94f-8945127a489a", + "status": null, + "_ts": 1618856973 + }, + { + "name": "Operations", + "description": "Operation activities performed.", + "tenant_id": "cc925a5d-9644-4a4f-8d99-0bee49aadd05", + "id": "d45c770a-b1a0-4bd8-a713-22c01a23e41b", + "_rid": "QUwFAPuumiRjAAAAAAAAAA==", + "_self": "dbs/QUwFAA==/colls/QUwFAPuumiQ=/docs/QUwFAPuumiRjAAAAAAAAAA==/", + "_etag": "\"09009a4d-0000-0500-0000-614b66fb0000\"", + "_attachments": "attachments/", + "_last_event_ctx": { + "user_id": "82ed0f65-051c-4898-890f-870805900e21", + "tenant_id": "cc925a5d-9644-4a4f-8d99-0bee49aadd05", + "action": "update", + "description": null, + "container_id": "activity", + "session_id": null + }, + "deleted": "7cf6efe5-a221-4fe4-b94f-8945127a489a", + "status": "active", + "_ts": 1632331515 + } +] + diff --git a/V2/source/daos/activities_dao.py b/V2/source/daos/activities_dao.py new file mode 100644 index 00000000..11cfb0f9 --- /dev/null +++ b/V2/source/daos/activities_dao.py @@ -0,0 +1,13 @@ +from V2.source.dtos.activity import Activity +import abc +import typing + + +class ActivitiesDao(abc.ABC): + @abc.abstractmethod + def get_by_id(self, id: str) -> Activity: + pass + + @abc.abstractmethod + def get_all(self) -> typing.List[Activity]: + pass diff --git a/V2/source/daos/activities_json_dao.py b/V2/source/daos/activities_json_dao.py new file mode 100644 index 00000000..c86e2ec0 --- /dev/null +++ b/V2/source/daos/activities_json_dao.py @@ -0,0 +1,42 @@ +from V2.source.daos.activities_dao import ActivitiesDao +from V2.source.dtos.activity import Activity +import dataclasses +import json +import typing + + +class ActivitiesJsonDao(ActivitiesDao): + def __init__(self, json_data_file_path: str): + self.json_data_file_path = json_data_file_path + self.activity_keys = [ + field.name for field in dataclasses.fields(Activity) + ] + + def get_by_id(self, activity_id: str) -> Activity: + activity = { + activity.get('id'): activity + for activity in self.__get_activities_from_file() + }.get(activity_id) + + return self.__create_activity_dto(activity) if activity else None + + def get_all(self) -> typing.List[Activity]: + return [ + self.__create_activity_dto(activity) + for activity in self.__get_activities_from_file() + ] + + def __get_activities_from_file(self) -> typing.List[dict]: + try: + file = open(self.json_data_file_path) + activities = json.load(file) + file.close() + + return activities + + except FileNotFoundError: + return [] + + def __create_activity_dto(self, activity: dict) -> Activity: + activity = {key: activity.get(key) for key in self.activity_keys} + return Activity(**activity) diff --git a/V2/source/dtos/activity.py b/V2/source/dtos/activity.py new file mode 100644 index 00000000..86f56ee9 --- /dev/null +++ b/V2/source/dtos/activity.py @@ -0,0 +1,11 @@ +from dataclasses import dataclass + + +@dataclass(frozen=True) +class Activity: + id: str + name: str + description: str + deleted: str + status: str + tenant_id: str diff --git a/V2/source/services/activity_service.py b/V2/source/services/activity_service.py new file mode 100644 index 00000000..fdba3390 --- /dev/null +++ b/V2/source/services/activity_service.py @@ -0,0 +1,14 @@ +from V2.source.daos.activities_dao import ActivitiesDao +from V2.source.dtos.activity import Activity +import typing + + +class ActivityService: + def __init__(self, activities_dao: ActivitiesDao): + self.activities_dao = activities_dao + + def get_by_id(self, activity_id: str) -> Activity: + return self.activities_dao.get_by_id(activity_id) + + def get_all(self) -> typing.List[Activity]: + return self.activities_dao.get_all() diff --git a/V2/tests/daos/activities_json_dao_test.py b/V2/tests/daos/activities_json_dao_test.py new file mode 100644 index 00000000..d4f87b96 --- /dev/null +++ b/V2/tests/daos/activities_json_dao_test.py @@ -0,0 +1,85 @@ +from V2.source.daos.activities_json_dao import ActivitiesJsonDao +from V2.source.dtos.activity import Activity +from faker import Faker +import json +import pytest +import typing + + +@pytest.fixture(name='create_fake_activities') +def _create_fake_activities(mocker) -> typing.List[Activity]: + def _creator(activities): + read_data = json.dumps(activities) + mocker.patch('builtins.open', mocker.mock_open(read_data=read_data)) + return [Activity(**activity) for activity in activities] + + return _creator + + +def test_get_by_id__returns_an_activity_dto__when_found_one_activity_that_matches_its_id( + create_fake_activities, +): + activities_json_dao = ActivitiesJsonDao(Faker().file_path()) + activities = create_fake_activities( + [ + { + "name": "test_name", + "description": "test_description", + "tenant_id": "test_tenant_id", + "id": "test_id", + "deleted": "test_deleted", + "status": "test_status", + } + ] + ) + activity_dto = activities.pop() + + result = activities_json_dao.get_by_id(activity_dto.id) + + assert result == activity_dto + + +def test__get_by_id__returns_none__when_no_activity_matches_its_id( + create_fake_activities, +): + activities_json_dao = ActivitiesJsonDao(Faker().file_path()) + create_fake_activities([]) + + result = activities_json_dao.get_by_id(Faker().uuid4()) + + assert result == None + + +def test__get_all__returns_a_list_of_activity_dto_objects__when_one_or_more_activities_are_found( + create_fake_activities, +): + activities_json_dao = ActivitiesJsonDao(Faker().file_path()) + number_of_activities = 3 + activities = create_fake_activities( + [ + { + "name": "test_name", + "description": "test_description", + "tenant_id": "test_tenant_id", + "id": "test_id", + "deleted": "test_deleted", + "status": "test_status", + } + ] + * number_of_activities + ) + + result = activities_json_dao.get_all() + + assert result == activities + + +def test_get_all__returns_an_empty_list__when_doesnt_found_any_activities( + create_fake_activities, +): + activities_json_dao = ActivitiesJsonDao(Faker().file_path()) + activities = create_fake_activities([]) + + result = activities_json_dao.get_all() + + assert result == activities diff --git a/V2/tests/services/activity_service_test.py b/V2/tests/services/activity_service_test.py new file mode 100644 index 00000000..e2e62b04 --- /dev/null +++ b/V2/tests/services/activity_service_test.py @@ -0,0 +1,28 @@ +from V2.source.services.activity_service import ActivityService +from faker import Faker + + +def test__get_all__uses_the_activity_dao__to_retrieve_activities(mocker): + expected_activities = mocker.Mock() + activity_dao = mocker.Mock( + get_all=mocker.Mock(return_value=expected_activities) + ) + activity_service = ActivityService(activity_dao) + + actual_activities = activity_service.get_all() + + assert activity_dao.get_all.called + assert expected_activities == actual_activities + + +def test__get_by_id__uses_the_activity_dao__to_retrieve_one_activity(mocker): + expected_activity = mocker.Mock() + activity_dao = mocker.Mock( + get_by_id=mocker.Mock(return_value=expected_activity) + ) + activity_service = ActivityService(activity_dao) + + actual_activity = activity_service.get_by_id(Faker().uuid4()) + + assert activity_dao.get_by_id.called + assert expected_activity == actual_activity