From 48f641170a968c7f12bc60f7882b0f4eda6cede2 Mon Sep 17 00:00:00 2001 From: Jipson Murillo <38593785+Jobzi@users.noreply.github.com> Date: Thu, 25 Nov 2021 12:38:09 -0500 Subject: [PATCH 1/8] feat: TT-402 put v2 time entries (#347) * feat: TT-402 add put time entries * refactor: TT-402 rebase post time entry * test: TT-402 add integration test of UPDATE * refactor: TT-402 delete time_entires_sql_dao_test * refactor: TT-404 revert changes _db.py * refactor: TT-402 Andres's resolve comments * fix: TT-402 refactor azure update endpoint * fix: TT-402 change name test --- V2/serverless.yml | 12 +++ .../azure/time_entry_azure_endpoints_test.py | 77 ++++++++++++++++++- V2/tests/fixtures.py | 2 +- .../integration/daos/time_entries_dao_test.py | 35 ++++++++- .../unit/services/time_entry_service_test.py | 17 ++++ .../use_cases/time_entries_use_case_test.py | 13 ++++ .../time_entries/_application/__init__.py | 4 +- .../_application/_time_entries/__init__.py | 3 +- .../_time_entries/_update_time_entry.py | 46 +++++++++++ .../time_entries/_domain/__init__.py | 3 +- .../_domain/_entities/__init__.py | 2 +- .../_persistence_contracts/__init__.py | 2 +- .../_time_entries_dao.py | 4 + .../_domain/_services/__init__.py | 2 +- .../_domain/_services/_time_entry.py | 3 + .../_domain/_use_cases/__init__.py | 1 + .../_use_cases/_update_time_entry_use_case.py | 11 +++ .../_data_persistence/_time_entries_dao.py | 23 +++++- V2/time_tracker/time_entries/interface.py | 3 +- 19 files changed, 247 insertions(+), 16 deletions(-) create mode 100644 V2/time_tracker/time_entries/_application/_time_entries/_update_time_entry.py create mode 100644 V2/time_tracker/time_entries/_domain/_use_cases/_update_time_entry_use_case.py diff --git a/V2/serverless.yml b/V2/serverless.yml index e5dea8e9..bac6bcc9 100644 --- a/V2/serverless.yml +++ b/V2/serverless.yml @@ -97,6 +97,17 @@ functions: route: time-entries/{id} authLevel: anonymous + + update_time_entry: + handler: time_tracker/time_entries/interface.update_time_entry + events: + - http: true + x-azure-settings: + methods: + - PUT + route: time-entries/{id} + authLevel: anonymous + create_customer: handler: time_tracker/customers/interface.create_customer events: @@ -106,3 +117,4 @@ functions: - POST route: customers/ authLevel: anonymous + \ No newline at end of file diff --git a/V2/tests/api/azure/time_entry_azure_endpoints_test.py b/V2/tests/api/azure/time_entry_azure_endpoints_test.py index 8422c4b5..f57db585 100644 --- a/V2/tests/api/azure/time_entry_azure_endpoints_test.py +++ b/V2/tests/api/azure/time_entry_azure_endpoints_test.py @@ -1,5 +1,6 @@ import pytest import json +from faker import Faker import azure.functions as func @@ -25,7 +26,7 @@ def test__time_entry_azure_endpoint__creates_an_time_entry__when_time_entry_has_ test_db, time_entry_factory, activity_factory, insert_activity ): inserted_activity = insert_activity(activity_factory(), test_db) - time_entry_body = time_entry_factory(activity_id=inserted_activity.id, technologies="[jira,sql]").__dict__ + time_entry_body = time_entry_factory(activity_id=inserted_activity.id).__dict__ body = json.dumps(time_entry_body).encode("utf-8") req = func.HttpRequest( @@ -46,7 +47,7 @@ def test__delete_time_entries_azure_endpoint__returns_an_time_entry_with_true_de test_db, time_entry_factory, insert_time_entry, insert_activity, activity_factory, ): inserted_activity = insert_activity(activity_factory(), test_db).__dict__ - time_entry_body = time_entry_factory(activity_id=inserted_activity["id"], technologies="[jira,sql]") + time_entry_body = time_entry_factory(activity_id=inserted_activity["id"]) inserted_time_entry = insert_time_entry(time_entry_body, test_db) req = func.HttpRequest( @@ -76,3 +77,75 @@ def test__delete_time_entries_azure_endpoint__returns_a_status_code_400__when_ti assert response.status_code == 400 assert response.get_body() == b'Invalid Format ID' + + +def test__update_time_entry_azure_endpoint__returns_an_time_entry__when_found_an_time_entry_to_update( + test_db, time_entry_factory, insert_time_entry, activity_factory, insert_activity +): + inserted_activity = insert_activity(activity_factory(), test_db) + existent_time_entries = time_entry_factory(activity_id=inserted_activity.id) + inserted_time_entries = insert_time_entry(existent_time_entries, test_db).__dict__ + + time_entry_body = {"description": Faker().sentence()} + + req = func.HttpRequest( + method='PUT', + body=json.dumps(time_entry_body).encode("utf-8"), + url=TIME_ENTRY_URL, + route_params={"id": inserted_time_entries["id"]}, + ) + + response = azure_time_entries._update_time_entry.update_time_entry(req) + activitiy_json_data = response.get_body().decode("utf-8") + inserted_time_entries.update(time_entry_body) + + assert response.status_code == 200 + assert activitiy_json_data == json.dumps(inserted_time_entries) + + +def test__update_time_entries_azure_endpoint__returns_a_status_code_400__when_time_entry_recive_invalid_format_id(): + time_entry_body = {"description": Faker().sentence()} + + req = func.HttpRequest( + method="PUT", + body=json.dumps(time_entry_body).encode("utf-8"), + url=TIME_ENTRY_URL, + route_params={"id": Faker().sentence()}, + ) + + response = azure_time_entries._update_time_entry.update_time_entry(req) + + assert response.status_code == 400 + assert response.get_body() == b'Invalid Format ID' + + +def test__update_time_entries_azure_endpoint__returns_a_status_code_404__when_not_found_an_time_entry_to_update(): + time_entry_body = {"description": Faker().sentence()} + + req = func.HttpRequest( + method="PUT", + body=json.dumps(time_entry_body).encode("utf-8"), + url=TIME_ENTRY_URL, + route_params={"id": Faker().pyint()}, + ) + + response = azure_time_entries._update_time_entry.update_time_entry(req) + + assert response.status_code == 404 + assert response.get_body() == b'Not found' + + +def test__update_time_entries_azure_endpoint__returns_a_status_code_400__when_time_entry_recive_invalid_body(): + + time_entry_body = Faker().pydict(5, True, str) + req = func.HttpRequest( + method="PUT", + body=json.dumps(time_entry_body).encode("utf-8"), + url=TIME_ENTRY_URL, + route_params={"id": Faker().pyint()}, + ) + + response = azure_time_entries._update_time_entry.update_time_entry(req) + + assert response.status_code == 400 + assert response.get_body() == b'Incorrect time entry body' diff --git a/V2/tests/fixtures.py b/V2/tests/fixtures.py index a02a74bb..91b0a801 100644 --- a/V2/tests/fixtures.py +++ b/V2/tests/fixtures.py @@ -44,7 +44,7 @@ def _make_time_entry( description=Faker().sentence(), activity_id=Faker().random_int(), uri=Faker().domain_name(), - technologies=["jira", "git"], + technologies=str(Faker().pylist()), end_date=str(Faker().date_time()), deleted=False, timezone_offset="300", diff --git a/V2/tests/integration/daos/time_entries_dao_test.py b/V2/tests/integration/daos/time_entries_dao_test.py index 901bce34..fbe5a7ed 100644 --- a/V2/tests/integration/daos/time_entries_dao_test.py +++ b/V2/tests/integration/daos/time_entries_dao_test.py @@ -29,7 +29,7 @@ def test__time_entry__returns_a_time_entry_dto__when_saves_correctly_with_sql_da dao = create_fake_dao(test_db) inserted_activity = insert_activity(activity_factory(), dao.db) - time_entry_to_insert = time_entry_factory(activity_id=inserted_activity.id, technologies="[jira,sql]") + time_entry_to_insert = time_entry_factory(activity_id=inserted_activity.id) inserted_time_entry = dao.create(time_entry_to_insert) @@ -41,7 +41,7 @@ def test__time_entry__returns_None__when_not_saves_correctly( time_entry_factory, create_fake_dao, test_db ): dao = create_fake_dao(test_db) - time_entry_to_insert = time_entry_factory(activity_id=1203, technologies="[jira,sql]") + time_entry_to_insert = time_entry_factory(activity_id=1203) inserted_time_entry = dao.create(time_entry_to_insert) @@ -53,7 +53,7 @@ def test_delete__returns_an_time_entry_with_true_deleted__when_an_time_entry_mat ): dao = create_fake_dao(test_db) inserted_activity = insert_activity(activity_factory(), dao.db) - existent_time_entry = time_entry_factory(activity_id=inserted_activity.id, technologies="[jira,sql]") + existent_time_entry = time_entry_factory(activity_id=inserted_activity.id) inserted_time_entry = dao.create(existent_time_entry) result = dao.delete(inserted_time_entry.id) @@ -69,3 +69,32 @@ def test_delete__returns_none__when_no_time_entry_matching_its_id_is_found( result = dao.delete(Faker().pyint()) assert result is None + + +def test_update__returns_an_time_entry_dto__when_found_one_time_entry_to_update( + test_db, create_fake_dao, time_entry_factory, insert_activity, activity_factory +): + dao = create_fake_dao(test_db) + inserted_activity = insert_activity(activity_factory(), dao.db) + existent_time_entries = time_entry_factory(activity_id=inserted_activity.id) + inserted_time_entries = dao.create(existent_time_entries).__dict__ + time_entry_id = inserted_time_entries["id"] + inserted_time_entries.update({"description": "description updated"}) + + time_entry = dao.update(time_entry_id=time_entry_id, time_entry_data=inserted_time_entries) + + assert time_entry.id == time_entry_id + assert time_entry.description == inserted_time_entries.get("description") + + +def test_update__returns_none__when_doesnt_found_one_time_entry_to_update( + test_db, create_fake_dao, time_entry_factory, insert_activity, activity_factory +): + dao = create_fake_dao(test_db) + inserted_activity = insert_activity(activity_factory(), dao.db) + existent_time_entries = time_entry_factory(activity_id=inserted_activity.id) + inserted_time_entries = dao.create(existent_time_entries).__dict__ + + time_entry = dao.update(0, inserted_time_entries) + + assert time_entry is None diff --git a/V2/tests/unit/services/time_entry_service_test.py b/V2/tests/unit/services/time_entry_service_test.py index e83b6afb..0952f8a9 100644 --- a/V2/tests/unit/services/time_entry_service_test.py +++ b/V2/tests/unit/services/time_entry_service_test.py @@ -29,3 +29,20 @@ def test__delete_time_entry__uses_the_time_entry_dao__to_delete_time_entry_selec assert time_entry_dao.delete.called assert expected_time_entry == deleted_time_entry + + +def test__update_time_entry__uses_the_time_entry_dao__to_update_one_time_entry( + mocker, +): + expected_time_entry = mocker.Mock() + time_entry_dao = mocker.Mock( + update=mocker.Mock(return_value=expected_time_entry) + ) + time_entry_service = TimeEntryService(time_entry_dao) + + updated_time_entry = time_entry_service.update( + Faker().pyint(), Faker().pydict() + ) + + assert time_entry_dao.update.called + assert expected_time_entry == updated_time_entry diff --git a/V2/tests/unit/use_cases/time_entries_use_case_test.py b/V2/tests/unit/use_cases/time_entries_use_case_test.py index e0994df4..1a679f37 100644 --- a/V2/tests/unit/use_cases/time_entries_use_case_test.py +++ b/V2/tests/unit/use_cases/time_entries_use_case_test.py @@ -30,3 +30,16 @@ def test__delete_time_entry_function__uses_the_time_entry_service__to_delete_tim assert time_entry_service.delete.called assert expected_time_entry == deleted_time_entry + + +def test__update_time_entries_function__uses_the_time_entry_service__to_update_an_time_entry( + mocker: MockFixture, +): + expected_time_entry = mocker.Mock() + time_entry_service = mocker.Mock(update=mocker.Mock(return_value=expected_time_entry)) + + time_entry_use_case = _use_cases.UpdateTimeEntryUseCase(time_entry_service) + updated_time_entry = time_entry_use_case.update_time_entry(Faker().uuid4(), Faker().pydict()) + + assert time_entry_service.update.called + assert expected_time_entry == updated_time_entry diff --git a/V2/time_tracker/time_entries/_application/__init__.py b/V2/time_tracker/time_entries/_application/__init__.py index 2810c87d..0ca4e272 100644 --- a/V2/time_tracker/time_entries/_application/__init__.py +++ b/V2/time_tracker/time_entries/_application/__init__.py @@ -1,2 +1,4 @@ # flake8: noqa -from ._time_entries import create_time_entry, delete_time_entry \ No newline at end of file +from ._time_entries import create_time_entry +from ._time_entries import delete_time_entry +from ._time_entries import update_time_entry diff --git a/V2/time_tracker/time_entries/_application/_time_entries/__init__.py b/V2/time_tracker/time_entries/_application/_time_entries/__init__.py index 4cb4d4b0..0f6cf2db 100644 --- a/V2/time_tracker/time_entries/_application/_time_entries/__init__.py +++ b/V2/time_tracker/time_entries/_application/_time_entries/__init__.py @@ -1,3 +1,4 @@ # flake8: noqa from ._create_time_entry import create_time_entry -from ._delete_time_entry import delete_time_entry \ No newline at end of file +from ._delete_time_entry import delete_time_entry +from ._update_time_entry import update_time_entry diff --git a/V2/time_tracker/time_entries/_application/_time_entries/_update_time_entry.py b/V2/time_tracker/time_entries/_application/_time_entries/_update_time_entry.py new file mode 100644 index 00000000..63366869 --- /dev/null +++ b/V2/time_tracker/time_entries/_application/_time_entries/_update_time_entry.py @@ -0,0 +1,46 @@ +import dataclasses +import json + +import azure.functions as func + +from ... import _domain +from ... import _infrastructure +from time_tracker._infrastructure import DB + + +def update_time_entry(req: func.HttpRequest) -> func.HttpResponse: + database = DB() + time_entry_dao = _infrastructure.TimeEntriesSQLDao(database) + time_entry_service = _domain.TimeEntryService(time_entry_dao) + use_case = _domain._use_cases.UpdateTimeEntryUseCase(time_entry_service) + + try: + time_entry_id = int(req.route_params.get("id")) + time_entry_data = req.get_json() + + if not _validate_time_entry(time_entry_data): + status_code = 400 + response = b"Incorrect time entry body" + else: + updated_time_entry = use_case.update_time_entry(time_entry_id, time_entry_data) + status_code, response = [ + 404, b"Not found" + ] if not updated_time_entry else [200, json.dumps(updated_time_entry.__dict__)] + + return func.HttpResponse( + body=response, + status_code=status_code, + mimetype="application/json", + ) + + except ValueError: + return func.HttpResponse( + body=b"Invalid Format ID", + status_code=400, + mimetype="application/json" + ) + + +def _validate_time_entry(time_entry_data: dict) -> bool: + time_entry_keys = [field.name for field in dataclasses.fields(_domain.TimeEntry)] + return all(key in time_entry_keys for key in time_entry_data.keys()) diff --git a/V2/time_tracker/time_entries/_domain/__init__.py b/V2/time_tracker/time_entries/_domain/__init__.py index ad927811..de58675c 100644 --- a/V2/time_tracker/time_entries/_domain/__init__.py +++ b/V2/time_tracker/time_entries/_domain/__init__.py @@ -4,5 +4,6 @@ from ._services import TimeEntryService from ._use_cases import ( CreateTimeEntryUseCase, - DeleteTimeEntryUseCase + DeleteTimeEntryUseCase, + UpdateTimeEntryUseCase, ) \ No newline at end of file diff --git a/V2/time_tracker/time_entries/_domain/_entities/__init__.py b/V2/time_tracker/time_entries/_domain/_entities/__init__.py index 88b4a739..3245a461 100644 --- a/V2/time_tracker/time_entries/_domain/_entities/__init__.py +++ b/V2/time_tracker/time_entries/_domain/_entities/__init__.py @@ -1,2 +1,2 @@ # flake8: noqa -from ._time_entry import TimeEntry \ No newline at end of file +from ._time_entry import TimeEntry diff --git a/V2/time_tracker/time_entries/_domain/_persistence_contracts/__init__.py b/V2/time_tracker/time_entries/_domain/_persistence_contracts/__init__.py index e10700ce..3f17d5ee 100644 --- a/V2/time_tracker/time_entries/_domain/_persistence_contracts/__init__.py +++ b/V2/time_tracker/time_entries/_domain/_persistence_contracts/__init__.py @@ -1,2 +1,2 @@ # flake8: noqa -from ._time_entries_dao import TimeEntriesDao \ No newline at end of file +from ._time_entries_dao import TimeEntriesDao diff --git a/V2/time_tracker/time_entries/_domain/_persistence_contracts/_time_entries_dao.py b/V2/time_tracker/time_entries/_domain/_persistence_contracts/_time_entries_dao.py index e7d94608..8c1dc9d9 100644 --- a/V2/time_tracker/time_entries/_domain/_persistence_contracts/_time_entries_dao.py +++ b/V2/time_tracker/time_entries/_domain/_persistence_contracts/_time_entries_dao.py @@ -11,3 +11,7 @@ def create(self, time_entry_data: TimeEntry) -> TimeEntry: @abc.abstractmethod def delete(self, id: int) -> TimeEntry: pass + + @abc.abstractmethod + def update(self, id: int, new_time_entry: dict) -> TimeEntry: + pass diff --git a/V2/time_tracker/time_entries/_domain/_services/__init__.py b/V2/time_tracker/time_entries/_domain/_services/__init__.py index e5e6ba1b..1a06f65b 100644 --- a/V2/time_tracker/time_entries/_domain/_services/__init__.py +++ b/V2/time_tracker/time_entries/_domain/_services/__init__.py @@ -1,2 +1,2 @@ # flake8: noqa -from ._time_entry import TimeEntryService \ No newline at end of file +from ._time_entry import TimeEntryService diff --git a/V2/time_tracker/time_entries/_domain/_services/_time_entry.py b/V2/time_tracker/time_entries/_domain/_services/_time_entry.py index 9d47d5e0..5c32c1e3 100644 --- a/V2/time_tracker/time_entries/_domain/_services/_time_entry.py +++ b/V2/time_tracker/time_entries/_domain/_services/_time_entry.py @@ -11,3 +11,6 @@ def create(self, time_entry_data: TimeEntry) -> TimeEntry: def delete(self, id: int) -> TimeEntry: return self.time_entry_dao.delete(id) + + def update(self, time_entry_id: int, new_time_entry: dict) -> TimeEntry: + return self.time_entry_dao.update(time_entry_id, new_time_entry) diff --git a/V2/time_tracker/time_entries/_domain/_use_cases/__init__.py b/V2/time_tracker/time_entries/_domain/_use_cases/__init__.py index 17b2442a..4f0ac92e 100644 --- a/V2/time_tracker/time_entries/_domain/_use_cases/__init__.py +++ b/V2/time_tracker/time_entries/_domain/_use_cases/__init__.py @@ -1,3 +1,4 @@ # flake8: noqa from ._create_time_entry_use_case import CreateTimeEntryUseCase from ._delete_time_entry_use_case import DeleteTimeEntryUseCase +from ._update_time_entry_use_case import UpdateTimeEntryUseCase diff --git a/V2/time_tracker/time_entries/_domain/_use_cases/_update_time_entry_use_case.py b/V2/time_tracker/time_entries/_domain/_use_cases/_update_time_entry_use_case.py new file mode 100644 index 00000000..0e2cdf70 --- /dev/null +++ b/V2/time_tracker/time_entries/_domain/_use_cases/_update_time_entry_use_case.py @@ -0,0 +1,11 @@ +from time_tracker.time_entries._domain import TimeEntryService, TimeEntry + + +class UpdateTimeEntryUseCase: + def __init__(self, time_entry_service: TimeEntryService): + self.time_entry_service = time_entry_service + + def update_time_entry( + self, time_entry_id: int, new_time_entry: dict + ) -> TimeEntry: + return self.time_entry_service.update(time_entry_id, new_time_entry) diff --git a/V2/time_tracker/time_entries/_infrastructure/_data_persistence/_time_entries_dao.py b/V2/time_tracker/time_entries/_infrastructure/_data_persistence/_time_entries_dao.py index 6037af9f..9c0740fa 100644 --- a/V2/time_tracker/time_entries/_infrastructure/_data_persistence/_time_entries_dao.py +++ b/V2/time_tracker/time_entries/_infrastructure/_data_persistence/_time_entries_dao.py @@ -44,9 +44,19 @@ def create(self, time_entry_data: domain.TimeEntry) -> domain.TimeEntry: except sqlalchemy.exc.SQLAlchemyError: return None - def __create_time_entry_dto(self, time_entry: dict) -> domain.TimeEntry: - time_entry = {key: time_entry.get(key) for key in self.time_entry_key} - return domain.TimeEntry(**time_entry) + def update(self, time_entry_id: int, time_entry_data: dict) -> domain.TimeEntry: + try: + query = self.time_entry.update().where(self.time_entry.c.id == time_entry_id).values(time_entry_data) + self.db.get_session().execute(query) + query_updated_time_entry = ( + sqlalchemy.sql.select(self.time_entry) + .where(self.time_entry.c.id == time_entry_id) + ) + time_entry = self.db.get_session().execute(query_updated_time_entry).one_or_none() + + return self.__create_time_entry_dto(dict(time_entry)) if time_entry else None + except sqlalchemy.exc.SQLAlchemyError: + return None def delete(self, time_entry_id: int) -> domain.TimeEntry: query = ( @@ -58,3 +68,10 @@ def delete(self, time_entry_id: int) -> domain.TimeEntry: query_deleted_time_entry = sqlalchemy.sql.select(self.time_entry).where(self.time_entry.c.id == time_entry_id) time_entry = self.db.get_session().execute(query_deleted_time_entry).one_or_none() return self.__create_time_entry_dto(dict(time_entry)) if time_entry else None + + def __create_time_entry_dto(self, time_entry: dict) -> domain.TimeEntry: + time_entry.update({ + "start_date": str(time_entry.get("start_date")), + "end_date": str(time_entry.get("end_date"))}) + time_entry = {key: time_entry.get(key) for key in self.time_entry_key} + return domain.TimeEntry(**time_entry) diff --git a/V2/time_tracker/time_entries/interface.py b/V2/time_tracker/time_entries/interface.py index 773314bb..7e1be4ef 100644 --- a/V2/time_tracker/time_entries/interface.py +++ b/V2/time_tracker/time_entries/interface.py @@ -1,3 +1,4 @@ # flake8: noqa from ._application import create_time_entry -from ._application import delete_time_entry \ No newline at end of file +from ._application import delete_time_entry +from ._application import update_time_entry From 237d47e3809dead79ff5e57336bb2e317776789c Mon Sep 17 00:00:00 2001 From: Alexander Date: Tue, 23 Nov 2021 10:09:59 -0500 Subject: [PATCH 2/8] feat: TT-417 created CRUD project --- V2/serverless.yml | 42 ++++++++++- .../projects/_application/__init__.py | 5 ++ .../_application/_projects/__init__.py | 5 ++ .../_application/_projects/_create_project.py | 62 ++++++++++++++++ .../_application/_projects/_delete_project.py | 36 +++++++++ .../_application/_projects/_get_projects.py | 57 +++++++++++++++ .../_application/_projects/_update_project.py | 55 ++++++++++++++ V2/time_tracker/projects/_domain/__init__.py | 11 +++ .../projects/_domain/_entities/__init__.py | 2 + .../projects/_domain/_entities/_project.py | 13 ++++ .../_persistence_contracts/__init__.py | 2 + .../_persistence_contracts/_projects_dao.py | 25 +++++++ .../projects/_domain/_services/__init__.py | 2 + .../projects/_domain/_services/_project.py | 24 ++++++ .../projects/_domain/_use_cases/__init__.py | 6 ++ .../_use_cases/_create_project_use_case.py | 10 +++ .../_use_cases/_delete_project_use_case.py | 10 +++ .../_use_cases/_get_project_by_id_use_case.py | 9 +++ .../_use_cases/_get_projects_use_case.py | 11 +++ .../_use_cases/_update_project_use_case.py | 11 +++ .../projects/_infrastructure/__init__.py | 2 + .../_data_persistence/__init__.py | 2 + .../_data_persistence/_projects_dao.py | 73 +++++++++++++++++++ V2/time_tracker/projects/interface.py | 5 ++ 24 files changed, 478 insertions(+), 2 deletions(-) create mode 100644 V2/time_tracker/projects/_application/__init__.py create mode 100644 V2/time_tracker/projects/_application/_projects/__init__.py create mode 100644 V2/time_tracker/projects/_application/_projects/_create_project.py create mode 100644 V2/time_tracker/projects/_application/_projects/_delete_project.py create mode 100644 V2/time_tracker/projects/_application/_projects/_get_projects.py create mode 100644 V2/time_tracker/projects/_application/_projects/_update_project.py create mode 100644 V2/time_tracker/projects/_domain/__init__.py create mode 100644 V2/time_tracker/projects/_domain/_entities/__init__.py create mode 100644 V2/time_tracker/projects/_domain/_entities/_project.py create mode 100644 V2/time_tracker/projects/_domain/_persistence_contracts/__init__.py create mode 100644 V2/time_tracker/projects/_domain/_persistence_contracts/_projects_dao.py create mode 100644 V2/time_tracker/projects/_domain/_services/__init__.py create mode 100644 V2/time_tracker/projects/_domain/_services/_project.py create mode 100644 V2/time_tracker/projects/_domain/_use_cases/__init__.py create mode 100644 V2/time_tracker/projects/_domain/_use_cases/_create_project_use_case.py create mode 100644 V2/time_tracker/projects/_domain/_use_cases/_delete_project_use_case.py create mode 100644 V2/time_tracker/projects/_domain/_use_cases/_get_project_by_id_use_case.py create mode 100644 V2/time_tracker/projects/_domain/_use_cases/_get_projects_use_case.py create mode 100644 V2/time_tracker/projects/_domain/_use_cases/_update_project_use_case.py create mode 100644 V2/time_tracker/projects/_infrastructure/__init__.py create mode 100644 V2/time_tracker/projects/_infrastructure/_data_persistence/__init__.py create mode 100644 V2/time_tracker/projects/_infrastructure/_data_persistence/_projects_dao.py create mode 100644 V2/time_tracker/projects/interface.py diff --git a/V2/serverless.yml b/V2/serverless.yml index bac6bcc9..e02e8fb6 100644 --- a/V2/serverless.yml +++ b/V2/serverless.yml @@ -97,7 +97,6 @@ functions: route: time-entries/{id} authLevel: anonymous - update_time_entry: handler: time_tracker/time_entries/interface.update_time_entry events: @@ -117,4 +116,43 @@ functions: - POST route: customers/ authLevel: anonymous - \ No newline at end of file + + get_projects: + handler: time_tracker/projects/interface.get_projects + events: + - http: true + x-azure-settings: + methods: + - GET + route: projects/{id:?} + authLevel: anonymous + + delete_project: + handler: time_tracker/projects/interface.delete_project + events: + - http: true + x-azure-settings: + methods: + - DELETE + route: projects/{id} + authLevel: anonymous + + update_project: + handler: time_tracker/projects/interface.update_project + events: + - http: true + x-azure-settings: + methods: + - PUT + route: projects/{id} + authLevel: anonymous + + create_project: + handler: time_tracker/projects/interface.create_project + events: + - http: true + x-azure-settings: + methods: + - POST + route: projects/ + authLevel: anonymous diff --git a/V2/time_tracker/projects/_application/__init__.py b/V2/time_tracker/projects/_application/__init__.py new file mode 100644 index 00000000..6b48fb8a --- /dev/null +++ b/V2/time_tracker/projects/_application/__init__.py @@ -0,0 +1,5 @@ +# flake8: noqa +from ._projects import create_project +from ._projects import delete_project +from ._projects import get_projects +from ._projects import update_project \ No newline at end of file diff --git a/V2/time_tracker/projects/_application/_projects/__init__.py b/V2/time_tracker/projects/_application/_projects/__init__.py new file mode 100644 index 00000000..9f87eef2 --- /dev/null +++ b/V2/time_tracker/projects/_application/_projects/__init__.py @@ -0,0 +1,5 @@ +# flake8: noqa +from ._create_project import create_project +from ._delete_project import delete_project +from ._get_projects import get_projects +from ._update_project import update_project \ No newline at end of file diff --git a/V2/time_tracker/projects/_application/_projects/_create_project.py b/V2/time_tracker/projects/_application/_projects/_create_project.py new file mode 100644 index 00000000..d9a2aa2c --- /dev/null +++ b/V2/time_tracker/projects/_application/_projects/_create_project.py @@ -0,0 +1,62 @@ +import dataclasses +import json +import typing + +import azure.functions as func + +from ... import _domain +from ... import _infrastructure +from time_tracker._infrastructure import DB + + +def create_project(req: func.HttpRequest) -> func.HttpResponse: + database = DB() + project_dao = _infrastructure.ProjectsSQLDao(database) + project_service = _domain.ProjectService(project_dao) + use_case = _domain._use_cases.CreateProjectUseCase(project_service) + + project_data = req.get_json() + + validation_errors = _validate_project(project_data) + if validation_errors: + return func.HttpResponse( + body=json.dumps(validation_errors), status_code=400, mimetype="application/json" + ) + + project_to_create = _domain.Project( + id=None, + name=project_data["name"], + description=project_data["description"], + project_type_id=1, + customer_id=project_data["customer_id"], + status=1, + deleted=False + ) + + created_project = use_case.create_project(project_to_create) + + if not created_project: + return func.HttpResponse( + body=json.dumps({'error': 'project could not be created'}), + status_code=500, + mimetype="application/json" + ) + + return func.HttpResponse( + body=json.dumps(created_project.__dict__), + status_code=201, + mimetype="application/json" + ) + + +def _validate_project(project_data: dict) -> typing.List[str]: + project_fields = [field.name for field in dataclasses.fields(_domain.Project)] + project_fields.pop(6) + project_fields.pop(5) + project_fields.pop(3) + project_fields.pop(0) + missing_keys = [field for field in project_fields if field not in project_data] + return [ + f'The {missing_key} key is missing in the input data' + for missing_key in missing_keys + ] diff --git a/V2/time_tracker/projects/_application/_projects/_delete_project.py b/V2/time_tracker/projects/_application/_projects/_delete_project.py new file mode 100644 index 00000000..f78fd587 --- /dev/null +++ b/V2/time_tracker/projects/_application/_projects/_delete_project.py @@ -0,0 +1,36 @@ +import json + +import azure.functions as func + +from ... import _domain +from ... import _infrastructure +from time_tracker._infrastructure import DB + + +def delete_project(req: func.HttpRequest) -> func.HttpResponse: + project_dao = _infrastructure.ProjectsSQLDao(DB()) + project_service = _domain.ProjectService(project_dao) + use_case = _domain._use_cases.DeleteProjectUseCase(project_service) + + try: + project_id = int(req.route_params.get("id")) + deleted_project = use_case.delete_project(project_id) + if not deleted_project: + return func.HttpResponse( + body="Not found", + status_code=404, + mimetype="application/json" + ) + + return func.HttpResponse( + body=json.dumps(deleted_project.__dict__, default=str), + status_code=200, + mimetype="application/json", + ) + + except ValueError: + return func.HttpResponse( + body=b"Invalid Format ID", + status_code=400, + mimetype="application/json" + ) diff --git a/V2/time_tracker/projects/_application/_projects/_get_projects.py b/V2/time_tracker/projects/_application/_projects/_get_projects.py new file mode 100644 index 00000000..961e433a --- /dev/null +++ b/V2/time_tracker/projects/_application/_projects/_get_projects.py @@ -0,0 +1,57 @@ +import dataclasses +import json +import typing + +import azure.functions as func + +from ... import _domain +from ... import _infrastructure +from time_tracker._infrastructure import DB + + +def get_projects(req: func.HttpRequest) -> func.HttpResponse: + database = DB() + project_dao = _infrastructure.ProjectsSQLDao(database) + project_service = _domain.ProjectService(project_dao) + + project_id = req.route_params.get("id") + + try: + if project_id: + response = _get_by_id(int(project_id), project_service) + if not response: + return func.HttpResponse( + body="Not found", + status_code=404, + mimetype="application/json" + ) + else: + response = _get_all(project_service) + + return func.HttpResponse( + body=json.dumps(response, default=str), + status_code=200, + mimetype="application/json", + ) + + except ValueError: + return func.HttpResponse( + body=b"Invalid format id", + status_code=400, + mimetype="application/json" + ) + + +def _get_by_id(project_id: int, project_service: _domain.ProjectService) -> str: + use_case = _domain._use_cases.GetProjectUseCase(project_service) + project = use_case.get_project_by_id(project_id) + + return project.__dict__ if project else None + + +def _get_all(project_service: _domain.ProjectService) -> typing.List: + use_case = _domain._use_cases.GetProjectsUseCase(project_service) + return [ + project.__dict__ + for project in use_case.get_projects() + ] \ No newline at end of file diff --git a/V2/time_tracker/projects/_application/_projects/_update_project.py b/V2/time_tracker/projects/_application/_projects/_update_project.py new file mode 100644 index 00000000..88e639c7 --- /dev/null +++ b/V2/time_tracker/projects/_application/_projects/_update_project.py @@ -0,0 +1,55 @@ +import dataclasses +import json +import typing + +import azure.functions as func + +from ... import _domain +from ... import _infrastructure +from time_tracker._infrastructure import DB + + +def update_project(req: func.HttpRequest) -> func.HttpResponse: + database = DB() + project_dao = _infrastructure.ProjectsSQLDao(database) + project_service = _domain.ProjectService(project_dao) + use_case = _domain._use_cases.UpdateProjectUseCase(project_service) + + try: + project_id = int(req.route_params.get("id")) + project_data = req.get_json() + status_code = 200 + + if not _validate_project(project_data): + status_code = 400 + response = "Incorrect project body" + + response = use_case.update_project( + project_id, + project_data.get("name"), + project_data.get("description"), + project_data.get("customer_id"), + project_data.get("status") + ).__dict__ + + if not update_project: + status_code = 404 + response = "Not found" + + return func.HttpResponse( + body=json.dumps(response, default=str), + status_code=status_code, + mimetype="application/json", + ) + + except ValueError: + return func.HttpResponse( + body=b"Invalid Format ID", + status_code=400, + mimetype="application/json" + ) + + +def _validate_project(project_data: dict) -> bool: + project_keys = [field.name for field in dataclasses.fields(_domain.Project)] + return all(key in project_keys for key in project_data.keys()) \ No newline at end of file diff --git a/V2/time_tracker/projects/_domain/__init__.py b/V2/time_tracker/projects/_domain/__init__.py new file mode 100644 index 00000000..c90dbcaf --- /dev/null +++ b/V2/time_tracker/projects/_domain/__init__.py @@ -0,0 +1,11 @@ +# flake8: noqa +from ._entities import Project +from ._persistence_contracts import ProjectsDao +from ._services import ProjectService +from ._use_cases import ( + CreateProjectUseCase, + DeleteProjectUseCase, + GetProjectsUseCase, + GetProjectUseCase, + UpdateProjectUseCase +) \ No newline at end of file diff --git a/V2/time_tracker/projects/_domain/_entities/__init__.py b/V2/time_tracker/projects/_domain/_entities/__init__.py new file mode 100644 index 00000000..693c3a41 --- /dev/null +++ b/V2/time_tracker/projects/_domain/_entities/__init__.py @@ -0,0 +1,2 @@ +# flake8: noqa +from ._project import Project \ No newline at end of file diff --git a/V2/time_tracker/projects/_domain/_entities/_project.py b/V2/time_tracker/projects/_domain/_entities/_project.py new file mode 100644 index 00000000..3f8d332b --- /dev/null +++ b/V2/time_tracker/projects/_domain/_entities/_project.py @@ -0,0 +1,13 @@ +from dataclasses import dataclass +from typing import List + + +@dataclass(frozen=True) +class Project: + id: str + name: str + description: str + project_type_id: int + customer_id: int + status: int + deleted: bool \ No newline at end of file diff --git a/V2/time_tracker/projects/_domain/_persistence_contracts/__init__.py b/V2/time_tracker/projects/_domain/_persistence_contracts/__init__.py new file mode 100644 index 00000000..b17214a7 --- /dev/null +++ b/V2/time_tracker/projects/_domain/_persistence_contracts/__init__.py @@ -0,0 +1,2 @@ +# flake8: noqa +from ._projects_dao import ProjectsDao \ No newline at end of file diff --git a/V2/time_tracker/projects/_domain/_persistence_contracts/_projects_dao.py b/V2/time_tracker/projects/_domain/_persistence_contracts/_projects_dao.py new file mode 100644 index 00000000..421f40fe --- /dev/null +++ b/V2/time_tracker/projects/_domain/_persistence_contracts/_projects_dao.py @@ -0,0 +1,25 @@ +import abc + +from .. import Project + + +class ProjectsDao(abc.ABC): + @abc.abstractmethod + def create(self, time_entry_data: Project) -> Project: + pass + + @abc.abstractmethod + def get_all(self) -> Project: + pass + + @abc.abstractmethod + def get_by_id(self, id: int) -> Project: + pass + + @abc.abstractmethod + def update(self, id: int, name: str, description: str, customer_id: int, status: int) -> Project: + pass + + @abc.abstractmethod + def delete(self, id: int) -> Project: + pass diff --git a/V2/time_tracker/projects/_domain/_services/__init__.py b/V2/time_tracker/projects/_domain/_services/__init__.py new file mode 100644 index 00000000..5eb9532b --- /dev/null +++ b/V2/time_tracker/projects/_domain/_services/__init__.py @@ -0,0 +1,2 @@ +# flake8: noqa +from ._project import ProjectService \ No newline at end of file diff --git a/V2/time_tracker/projects/_domain/_services/_project.py b/V2/time_tracker/projects/_domain/_services/_project.py new file mode 100644 index 00000000..cd69ccd4 --- /dev/null +++ b/V2/time_tracker/projects/_domain/_services/_project.py @@ -0,0 +1,24 @@ +import typing + +from .. import Project, ProjectsDao + + +class ProjectService: + + def __init__(self, project_dao: ProjectsDao): + self.project_dao = project_dao + + def create(self, project_data: Project) -> Project: + return self.project_dao.create(project_data) + + def get_all(self) -> typing.List[Project]: + return self.project_dao.get_all() + + def get_by_id(self, id: int) -> Project: + return self.project_dao.get_by_id(id) + + def update(self, id: int, name: str, description: str, customer_id: int, status: int) -> Project: + return self.project_dao.update(id, name, description, customer_id, status) + + def delete(self, id: int) -> Project: + return self.project_dao.delete(id) diff --git a/V2/time_tracker/projects/_domain/_use_cases/__init__.py b/V2/time_tracker/projects/_domain/_use_cases/__init__.py new file mode 100644 index 00000000..defb127d --- /dev/null +++ b/V2/time_tracker/projects/_domain/_use_cases/__init__.py @@ -0,0 +1,6 @@ +# flake8: noqa +from ._create_project_use_case import CreateProjectUseCase +from ._delete_project_use_case import DeleteProjectUseCase +from ._get_project_by_id_use_case import GetProjectUseCase +from ._get_projects_use_case import GetProjectsUseCase +from ._update_project_use_case import UpdateProjectUseCase diff --git a/V2/time_tracker/projects/_domain/_use_cases/_create_project_use_case.py b/V2/time_tracker/projects/_domain/_use_cases/_create_project_use_case.py new file mode 100644 index 00000000..60b50687 --- /dev/null +++ b/V2/time_tracker/projects/_domain/_use_cases/_create_project_use_case.py @@ -0,0 +1,10 @@ +from .. import Project, ProjectService + + +class CreateProjectUseCase: + + def __init__(self, project_service: ProjectService): + self.project_service = project_service + + def create_project(self, project_data: Project) -> Project: + return self.project_service.create(project_data) diff --git a/V2/time_tracker/projects/_domain/_use_cases/_delete_project_use_case.py b/V2/time_tracker/projects/_domain/_use_cases/_delete_project_use_case.py new file mode 100644 index 00000000..9dd91d4b --- /dev/null +++ b/V2/time_tracker/projects/_domain/_use_cases/_delete_project_use_case.py @@ -0,0 +1,10 @@ +from .. import Project, ProjectService + + +class DeleteProjectUseCase: + + def __init__(self, project_service: ProjectService): + self.project_service = project_service + + def delete_project(self, id: int) -> Project: + return self.project_service.delete(id) diff --git a/V2/time_tracker/projects/_domain/_use_cases/_get_project_by_id_use_case.py b/V2/time_tracker/projects/_domain/_use_cases/_get_project_by_id_use_case.py new file mode 100644 index 00000000..94573496 --- /dev/null +++ b/V2/time_tracker/projects/_domain/_use_cases/_get_project_by_id_use_case.py @@ -0,0 +1,9 @@ +from .. import ProjectService, Project + + +class GetProjectUseCase: + def __init__(self, project_service: ProjectService): + self.project_service = project_service + + def get_project_by_id(self, id: int) -> Project: + return self.project_service.get_by_id(id) diff --git a/V2/time_tracker/projects/_domain/_use_cases/_get_projects_use_case.py b/V2/time_tracker/projects/_domain/_use_cases/_get_projects_use_case.py new file mode 100644 index 00000000..ccf0b3a4 --- /dev/null +++ b/V2/time_tracker/projects/_domain/_use_cases/_get_projects_use_case.py @@ -0,0 +1,11 @@ +import typing + +from .. import Project, ProjectService + + +class GetProjectsUseCase: + def __init__(self, project_service: ProjectService): + self.project_service = project_service + + def get_projects(self) -> typing.List[Project]: + return self.project_service.get_all() diff --git a/V2/time_tracker/projects/_domain/_use_cases/_update_project_use_case.py b/V2/time_tracker/projects/_domain/_use_cases/_update_project_use_case.py new file mode 100644 index 00000000..32506498 --- /dev/null +++ b/V2/time_tracker/projects/_domain/_use_cases/_update_project_use_case.py @@ -0,0 +1,11 @@ +from .. import ProjectService, Project + + +class UpdateProjectUseCase: + def __init__(self, projects_service: ProjectService): + self.projects_service = projects_service + + def update_project( + self, id: int, name: str, description: str, customer_id: int, status: int + ) -> Project: + return self.projects_service.update(id, name, description, customer_id, status) diff --git a/V2/time_tracker/projects/_infrastructure/__init__.py b/V2/time_tracker/projects/_infrastructure/__init__.py new file mode 100644 index 00000000..b940cba3 --- /dev/null +++ b/V2/time_tracker/projects/_infrastructure/__init__.py @@ -0,0 +1,2 @@ +# flake8: noqa +from ._data_persistence import ProjectsSQLDao diff --git a/V2/time_tracker/projects/_infrastructure/_data_persistence/__init__.py b/V2/time_tracker/projects/_infrastructure/_data_persistence/__init__.py new file mode 100644 index 00000000..b73fcf44 --- /dev/null +++ b/V2/time_tracker/projects/_infrastructure/_data_persistence/__init__.py @@ -0,0 +1,2 @@ +# flake8: noqa +from ._projects_dao import ProjectsSQLDao diff --git a/V2/time_tracker/projects/_infrastructure/_data_persistence/_projects_dao.py b/V2/time_tracker/projects/_infrastructure/_data_persistence/_projects_dao.py new file mode 100644 index 00000000..5a7339bf --- /dev/null +++ b/V2/time_tracker/projects/_infrastructure/_data_persistence/_projects_dao.py @@ -0,0 +1,73 @@ +import typing +import dataclasses + +import sqlalchemy as sq + +from ... import _domain as domain +from time_tracker._infrastructure import _db + + +class ProjectsSQLDao(domain.ProjectsDao): + + def __init__(self, database: _db.DB): + self.project_key = [field.name for field in dataclasses.fields(domain.Project)] + self.db = database + self.project = sq.Table( + 'project', + self.db.metadata, + sq.Column('id', sq.Integer, primary_key=True, autoincrement=True), + sq.Column('name', sq.String), + sq.Column('description', sq.String), + sq.Column('project_type_id', sq.Integer), + sq.Column('customer_id', sq.Integer), + sq.Column('status', sq.SmallInteger), + sq.Column('deleted', sq.BOOLEAN), + extend_existing=True, + ) + + def create(self, project_data: domain.Project) -> domain.Project: + try: + new_project = project_data.__dict__ + new_project.pop('id', None) + + query = self.project.insert().values(new_project).return_defaults() + project = self.db.get_session().execute(query) + new_project.update({"id": project.inserted_primary_key[0]}) + return self.__create_project_dto(new_project) + + except sq.exc.sqError: + return None + + def get_by_id(self, id: int) -> domain.Project: + query = sq.sql.select(self.project).where(self.project.c.id == id) + project = self.db.get_session().execute(query).one_or_none() + return self.__create_project_dto(dict(project)) if project else None + + def get_all(self) -> typing.List[domain.Project]: + query = sq.sql.select(self.project) + result = self.db.get_session().execute(query) + return [ + self.__create_project_dto(dict(project)) + for project in result + ] + + def delete(self, id: int) -> domain.Project: + query = ( + self.project.update() + .where(self.project.c.id == id) + .values({"deleted": True, "status": 0}) + ) + self.db.get_session().execute(query) + return self.get_by_id(id) + + def update(self, id: int, name: str, description: str, customer_id: int, status: int) -> domain.Project: + new_project = {"name": name, "description": description, "customer_id": customer_id, "status": status} + project_data_validated = {key: value for (key, value) in new_project.items() if value is not None} + + query = self.project.update().where(self.project.c.id == id).values(project_data_validated) + self.db.get_session().execute(query) + return self.get_by_id(id) + + def __create_project_dto(self, project: dict) -> domain.Project: + project = {key: project.get(key) for key in self.project_key} + return domain.Project(**project) \ No newline at end of file diff --git a/V2/time_tracker/projects/interface.py b/V2/time_tracker/projects/interface.py new file mode 100644 index 00000000..2fb3244b --- /dev/null +++ b/V2/time_tracker/projects/interface.py @@ -0,0 +1,5 @@ +# flake8: noqa +from ._application import create_project +from ._application import delete_project +from ._application import get_projects +from ._application import update_project \ No newline at end of file From da64eeff66ce68363365a34d36b3b74d4e1224ad Mon Sep 17 00:00:00 2001 From: Jipson Murillo Date: Tue, 23 Nov 2021 13:32:25 -0500 Subject: [PATCH 3/8] test: TT-417 add test of projects --- .../api/azure/project_azure_endpoints_test.py | 165 ++++++++++++++++++ V2/tests/conftest.py | 1 + V2/tests/fixtures.py | 27 ++- .../integration/daos/projects_dao_test.py | 136 +++++++++++++++ .../unit/services/project_service_test.py | 74 ++++++++ .../unit/use_cases/projects_use_case_test.py | 80 +++++++++ .../_application/_projects/_create_project.py | 14 +- .../_application/_projects/_get_projects.py | 7 +- .../_application/_projects/_update_project.py | 19 +- .../projects/_domain/_entities/_project.py | 7 +- .../_persistence_contracts/_projects_dao.py | 2 +- .../projects/_domain/_services/_project.py | 4 +- .../_use_cases/_update_project_use_case.py | 6 +- .../_data_persistence/_projects_dao.py | 15 +- 14 files changed, 514 insertions(+), 43 deletions(-) create mode 100644 V2/tests/api/azure/project_azure_endpoints_test.py create mode 100644 V2/tests/integration/daos/projects_dao_test.py create mode 100644 V2/tests/unit/services/project_service_test.py create mode 100644 V2/tests/unit/use_cases/projects_use_case_test.py diff --git a/V2/tests/api/azure/project_azure_endpoints_test.py b/V2/tests/api/azure/project_azure_endpoints_test.py new file mode 100644 index 00000000..137c3939 --- /dev/null +++ b/V2/tests/api/azure/project_azure_endpoints_test.py @@ -0,0 +1,165 @@ +import json + +import pytest +from faker import Faker +import azure.functions as func + +from time_tracker.projects._application import _projects as azure_projects +from time_tracker.projects import _domain as domain +from time_tracker.projects import _infrastructure as infrastructure +from time_tracker._infrastructure import DB + +PROJECT_URL = '/api/projects/' + + +@pytest.fixture(name='insert_project') +def _insert_project() -> domain.Project: + def _new_project(project: domain.Project, database: DB): + dao = infrastructure.ProjectsSQLDao(database) + new_project = dao.create(project) + return new_project + return _new_project + + +def test__project_azure_endpoint__returns_all_projects( + test_db, project_factory, insert_project +): + project_to_insert = [project_factory(), project_factory()] + inserted_projects = [ + insert_project(project_to_insert[0], test_db).__dict__, + insert_project(project_to_insert[1], test_db).__dict__ + ] + + req = func.HttpRequest(method='GET', body=None, url=PROJECT_URL) + response = azure_projects._get_projects.get_projects(req) + projects_json_data = response.get_body().decode("utf-8") + + assert response.status_code == 200 + assert projects_json_data == json.dumps(inserted_projects) + + +def test__project_azure_endpoint__returns_an_project__when_project_matches_its_id( + test_db, project_factory, insert_project +): + project_to_insert = project_factory() + inserted_project = insert_project(project_to_insert, test_db).__dict__ + + req = func.HttpRequest( + method='GET', + body=None, + url=PROJECT_URL, + route_params={"id": inserted_project["id"]}, + ) + + response = azure_projects._get_projects.get_projects(req) + activitiy_json_data = response.get_body().decode("utf-8") + + assert response.status_code == 200 + assert activitiy_json_data == json.dumps(inserted_project) + + +def test__projects_azure_endpoint__returns_a_status_code_400__when_project_recive_invalid_id( +): + req = func.HttpRequest( + method="GET", + body=None, + url=PROJECT_URL, + route_params={"id": "invalid id"}, + ) + + response = azure_projects._get_projects.get_projects(req) + + assert response.status_code == 400 + assert response.get_body() == b'Invalid Format ID' + + +def test__project_azure_endpoint__returns_an_project_with_inactive_status__when_an_project_matching_its_id_is_found( + test_db, project_factory, insert_project +): + project_to_insert = project_factory() + inserted_project = insert_project(project_to_insert, test_db).__dict__ + + req = func.HttpRequest( + method='DELETE', + body=None, + url=PROJECT_URL, + route_params={"id": inserted_project["id"]}, + ) + + response = azure_projects._delete_project.delete_project(req) + project_json_data = json.loads(response.get_body().decode("utf-8")) + + assert response.status_code == 200 + assert project_json_data['status'] == 0 + assert project_json_data['deleted'] is True + + +def test__delete_projects_azure_endpoint__returns_a_status_code_400__when_project_recive_invalid_id( +): + req = func.HttpRequest( + method="DELETE", + body=None, + url=PROJECT_URL, + route_params={"id": "invalid id"}, + ) + + response = azure_projects._delete_project.delete_project(req) + + assert response.status_code == 400 + assert response.get_body() == b'Invalid Format ID' + + +def test__update_project_azure_endpoint__returns_an_project__when_found_an_project_to_update( + test_db, project_factory, insert_project +): + project_to_insert = project_factory() + inserted_project = insert_project(project_to_insert, test_db).__dict__ + + project_body = {"description": Faker().sentence()} + req = func.HttpRequest( + method='PUT', + body=json.dumps(project_body).encode("utf-8"), + url=PROJECT_URL, + route_params={"id": inserted_project["id"]}, + ) + + response = azure_projects._update_project.update_project(req) + activitiy_json_data = response.get_body().decode("utf-8") + inserted_project.update(project_body) + + assert response.status_code == 200 + assert activitiy_json_data == json.dumps(inserted_project) + + +def test__update_projects_azure_endpoint__returns_a_status_code_400__when_project_recive_invalid_id( +): + req = func.HttpRequest( + method="PUT", + body=None, + url=PROJECT_URL, + route_params={"id": "invalid id"}, + ) + + response = azure_projects._update_project.update_project(req) + + assert response.status_code == 400 + assert response.get_body() == b'Invalid Format ID' + + +def test__project_azure_endpoint__creates_an_project__when_project_has_all_attributes( + project_factory +): + project_body = project_factory().__dict__ + body = json.dumps(project_body).encode("utf-8") + req = func.HttpRequest( + method='POST', + body=body, + url=PROJECT_URL, + ) + + response = azure_projects._create_project.create_project(req) + project_json_data = json.loads(response.get_body()) + project_body['id'] = project_json_data['id'] + + assert response.status_code == 201 + assert project_json_data == project_body diff --git a/V2/tests/conftest.py b/V2/tests/conftest.py index 4ad03c51..d593c439 100644 --- a/V2/tests/conftest.py +++ b/V2/tests/conftest.py @@ -2,3 +2,4 @@ from fixtures import _activity_factory, _test_db, _insert_activity from fixtures import _time_entry_factory from fixtures import _customer_factory +from fixtures import _project_factory diff --git a/V2/tests/fixtures.py b/V2/tests/fixtures.py index 91b0a801..ea8e2d1f 100644 --- a/V2/tests/fixtures.py +++ b/V2/tests/fixtures.py @@ -2,9 +2,10 @@ from faker import Faker import time_tracker.activities._domain as activities_domain +import time_tracker.activities._infrastructure as activities_infrastructure import time_tracker.time_entries._domain as time_entries_domain import time_tracker.customers._domain as customers_domain -import time_tracker.activities._infrastructure as activities_infrastructure +import time_tracker.projects._domain as projects_domain from time_tracker._infrastructure import DB @@ -94,3 +95,27 @@ def _make_customer( return customer return _make_customer +@pytest.fixture(name='project_factory') +def _project_factory() -> projects_domain.Project: + def _make_project( + id=Faker().pyint(), + name=Faker().name(), + description=Faker().sentence(), + project_type_id=Faker().pyint(), + customer_id=Faker().pyint(), + status=Faker().pyint(), + deleted=False, + technologies=str(Faker().pylist()) + ): + project = projects_domain.Project( + id=id, + name=name, + description=description, + project_type_id=project_type_id, + customer_id=customer_id, + status=status, + deleted=deleted, + technologies=technologies + ) + return project + return _make_project diff --git a/V2/tests/integration/daos/projects_dao_test.py b/V2/tests/integration/daos/projects_dao_test.py new file mode 100644 index 00000000..79cfa76f --- /dev/null +++ b/V2/tests/integration/daos/projects_dao_test.py @@ -0,0 +1,136 @@ +import pytest +import typing +from faker import Faker + +from time_tracker.projects import _domain as domain +from time_tracker.projects import _infrastructure as infrastructure +from time_tracker._infrastructure import DB + + +@pytest.fixture(name='create_fake_dao') +def _create_fake_dao() -> domain.ProjectsDao: + db_fake = DB() + dao = infrastructure.ProjectsSQLDao(db_fake) + return dao + + +@pytest.fixture(name='clean_database', autouse=True) +def _clean_database(): + yield + db_fake = DB() + dao = infrastructure.ProjectsSQLDao(db_fake) + query = dao.project.delete() + dao.db.get_session().execute(query) + + +def test__create_project__returns_a_project_dto__when_saves_correctly_with_sql_database( + create_fake_dao, project_factory +): + dao = create_fake_dao + project_to_insert = project_factory() + + inserted_project = dao.create(project_to_insert) + + assert isinstance(inserted_project, domain.Project) + assert inserted_project == project_to_insert + + +def test_update__returns_an_update_project__when_an_project_matching_its_id_is_found_with_sql_database( + create_fake_dao, project_factory +): + dao = create_fake_dao + project_to_insert = project_factory() + inserted_project = dao.create(project_to_insert) + + expected_description = Faker().sentence() + updated_project = dao.update(inserted_project.id, {"description": expected_description}) + + assert isinstance(updated_project, domain.Project) + assert updated_project.id == inserted_project.id + assert updated_project.description == expected_description + + +def test_update__returns_none__when_no_project_matching_its_id_is_found_with_sql_database( + create_fake_dao, project_factory +): + dao = create_fake_dao + project_to_insert = project_factory() + + results = dao.update(project_to_insert.id, {"description": Faker().sentence()}) + + assert results is None + + +def test__get_all__returns_a_list_of_project_dto_objects__when_one_or_more_projects_are_found_with_sql_database( + create_fake_dao, project_factory +): + dao = create_fake_dao + project_to_inserts = [project_factory(), project_factory()] + inserted_projects = [ + dao.create(project_to_inserts[0]), + dao.create(project_to_inserts[1]) + ] + + projects = dao.get_all() + assert isinstance(projects, typing.List) + assert projects == inserted_projects + + +def test_get_by_id__returns_an_project_dto__when_found_one_project_that_matches_its_id_with_sql_database( + create_fake_dao, project_factory +): + dao = create_fake_dao + project_to_insert = project_factory() + inserted_project = dao.create(project_to_insert) + + project = dao.get_by_id(inserted_project.id) + + assert isinstance(project, domain.Project) + assert project.id == inserted_project.id + assert project == inserted_project + + +def test__get_by_id__returns_none__when_no_project_matches_its_id_with_sql_database( + create_fake_dao, project_factory +): + dao = create_fake_dao + project_to_insert = project_factory() + + project = dao.get_by_id(project_to_insert.id) + + assert project is None + + +def test_get_all__returns_an_empty_list__when_doesnt_found_any_projects_with_sql_database( + create_fake_dao +): + projects = create_fake_dao.get_all() + + assert isinstance(projects, typing.List) + assert projects == [] + + +def test_delete__returns_an_project_with_inactive_status__when_an_project_matching_its_id_is_found_with_sql_database( + create_fake_dao, project_factory +): + dao = create_fake_dao + project_to_insert = project_factory() + inserted_project = dao.create(project_to_insert) + + project = dao.delete(inserted_project.id) + + assert isinstance(project, domain.Project) + assert project.id == inserted_project.id + assert project.status == 0 + assert project.deleted is True + + +def test_delete__returns_none__when_no_project_matching_its_id_is_found_with_sql_database( + create_fake_dao, project_factory +): + dao = create_fake_dao + project_to_insert = project_factory() + + results = dao.delete(project_to_insert.id) + + assert results is None diff --git a/V2/tests/unit/services/project_service_test.py b/V2/tests/unit/services/project_service_test.py new file mode 100644 index 00000000..9baf657e --- /dev/null +++ b/V2/tests/unit/services/project_service_test.py @@ -0,0 +1,74 @@ +from faker import Faker + +from time_tracker.projects._domain import ProjectService + + +def test__get_all__uses_the_project_dao__to_retrieve_projects(mocker): + expected_projects = mocker.Mock() + project_dao = mocker.Mock( + get_all=mocker.Mock(return_value=expected_projects) + ) + project_service = ProjectService(project_dao) + + actual_projects = project_service.get_all() + + assert project_dao.get_all.called + assert expected_projects == actual_projects + + +def test__get_by_id__uses_the_project_dao__to_retrieve_one_project(mocker): + expected_project = mocker.Mock() + project_dao = mocker.Mock( + get_by_id=mocker.Mock(return_value=expected_project) + ) + project_service = ProjectService(project_dao) + + actual_project = project_service.get_by_id(Faker().pyint()) + + assert project_dao.get_by_id.called + assert expected_project == actual_project + + +def test__delete_project__uses_the_project_dao__to_change_project_status( + mocker, +): + expected_project = mocker.Mock() + project_dao = mocker.Mock( + delete=mocker.Mock(return_value=expected_project) + ) + + project_service = ProjectService(project_dao) + deleted_project = project_service.delete(Faker().pyint()) + + assert project_dao.delete.called + assert expected_project == deleted_project + + +def test__update_project__uses_the_project_dao__to_update_one_project( + mocker, +): + expected_project = mocker.Mock() + project_dao = mocker.Mock( + update=mocker.Mock(return_value=expected_project) + ) + project_service = ProjectService(project_dao) + + updated_project = project_service.update( + Faker().pyint(), Faker().pydict() + ) + + assert project_dao.update.called + assert expected_project == updated_project + + +def test__create_project__uses_the_project_dao__to_create_an_project(mocker, project_factory): + expected_project = mocker.Mock() + project_dao = mocker.Mock( + create=mocker.Mock(return_value=expected_project) + ) + project_service = ProjectService(project_dao) + + actual_project = project_service.create(project_factory()) + + assert project_dao.create.called + assert expected_project == actual_project diff --git a/V2/tests/unit/use_cases/projects_use_case_test.py b/V2/tests/unit/use_cases/projects_use_case_test.py new file mode 100644 index 00000000..22167418 --- /dev/null +++ b/V2/tests/unit/use_cases/projects_use_case_test.py @@ -0,0 +1,80 @@ +from pytest_mock import MockFixture +from faker import Faker + +from time_tracker.projects._domain import _use_cases + + +def test__create_project_function__uses_the_projects_service__to_create_project( + mocker: MockFixture, project_factory +): + expected_project = mocker.Mock() + project_service = mocker.Mock( + create=mocker.Mock(return_value=expected_project) + ) + + project_use_case = _use_cases.CreateProjectUseCase(project_service) + actual_project = project_use_case.create_project(project_factory()) + + assert project_service.create.called + assert expected_project == actual_project + + +def test__delete_project_function__uses_the_project_service__to_delete_project_selected( + mocker: MockFixture, +): + expected_project = mocker.Mock() + project_service = mocker.Mock(delete=mocker.Mock(return_value=expected_project)) + + project_use_case = _use_cases.DeleteProjectUseCase(project_service) + deleted_project = project_use_case.delete_project(Faker().pyint()) + + assert project_service.delete.called + assert expected_project == deleted_project + + +def test__get_list_projects_function__uses_the_project_service__to_retrieve_projects( + mocker: MockFixture, +): + expected_projects = mocker.Mock() + project_service = mocker.Mock( + get_all=mocker.Mock(return_value=expected_projects) + ) + + projects_use_case = _use_cases.GetProjectsUseCase(project_service) + actual_projects = projects_use_case.get_projects() + + assert project_service.get_all.called + assert expected_projects == actual_projects + + +def test__get_project_by_id_function__uses_the_project_service__to_retrieve_project( + mocker: MockFixture, +): + expected_project = mocker.Mock() + project_service = mocker.Mock( + get_by_id=mocker.Mock(return_value=expected_project) + ) + + project_use_case = _use_cases.GetProjectUseCase(project_service) + actual_project = project_use_case.get_project_by_id(Faker().pyint()) + + assert project_service.get_by_id.called + assert expected_project == actual_project + + +def test__update_project_function__uses_the_projects_service__to_update_an_project( + mocker: MockFixture, project_factory +): + expected_project = mocker.Mock() + project_service = mocker.Mock( + update=mocker.Mock(return_value=expected_project) + ) + project_to_update = project_factory() + + project_use_case = _use_cases.UpdateProjectUseCase(project_service) + updated_project = project_use_case.update_project( + Faker().pyint(), project_to_update.__dict__ + ) + + assert project_service.update.called + assert expected_project == updated_project diff --git a/V2/time_tracker/projects/_application/_projects/_create_project.py b/V2/time_tracker/projects/_application/_projects/_create_project.py index d9a2aa2c..2332d59e 100644 --- a/V2/time_tracker/projects/_application/_projects/_create_project.py +++ b/V2/time_tracker/projects/_application/_projects/_create_project.py @@ -27,10 +27,11 @@ def create_project(req: func.HttpRequest) -> func.HttpResponse: id=None, name=project_data["name"], description=project_data["description"], - project_type_id=1, + project_type_id=project_data["project_type_id"], customer_id=project_data["customer_id"], - status=1, - deleted=False + status=project_data["status"], + deleted=False, + technologies=project_data["technologies"] ) created_project = use_case.create_project(project_to_create) @@ -50,11 +51,8 @@ def create_project(req: func.HttpRequest) -> func.HttpResponse: def _validate_project(project_data: dict) -> typing.List[str]: - project_fields = [field.name for field in dataclasses.fields(_domain.Project)] - project_fields.pop(6) - project_fields.pop(5) - project_fields.pop(3) - project_fields.pop(0) + project_fields = [field.name for field in dataclasses.fields(_domain.Project) + if field.type != typing.Optional[field.type]] missing_keys = [field for field in project_fields if field not in project_data] return [ f'The {missing_key} key is missing in the input data' diff --git a/V2/time_tracker/projects/_application/_projects/_get_projects.py b/V2/time_tracker/projects/_application/_projects/_get_projects.py index 961e433a..dc7adb84 100644 --- a/V2/time_tracker/projects/_application/_projects/_get_projects.py +++ b/V2/time_tracker/projects/_application/_projects/_get_projects.py @@ -1,4 +1,3 @@ -import dataclasses import json import typing @@ -36,8 +35,8 @@ def get_projects(req: func.HttpRequest) -> func.HttpResponse: except ValueError: return func.HttpResponse( - body=b"Invalid format id", - status_code=400, + body=b"Invalid Format ID", + status_code=400, mimetype="application/json" ) @@ -54,4 +53,4 @@ def _get_all(project_service: _domain.ProjectService) -> typing.List: return [ project.__dict__ for project in use_case.get_projects() - ] \ No newline at end of file + ] diff --git a/V2/time_tracker/projects/_application/_projects/_update_project.py b/V2/time_tracker/projects/_application/_projects/_update_project.py index 88e639c7..7e543cdc 100644 --- a/V2/time_tracker/projects/_application/_projects/_update_project.py +++ b/V2/time_tracker/projects/_application/_projects/_update_project.py @@ -1,6 +1,5 @@ import dataclasses import json -import typing import azure.functions as func @@ -19,19 +18,13 @@ def update_project(req: func.HttpRequest) -> func.HttpResponse: project_id = int(req.route_params.get("id")) project_data = req.get_json() status_code = 200 - + if not _validate_project(project_data): status_code = 400 - response = "Incorrect project body" - - response = use_case.update_project( - project_id, - project_data.get("name"), - project_data.get("description"), - project_data.get("customer_id"), - project_data.get("status") - ).__dict__ - + response = "Incorrect project body" + + response = use_case.update_project(project_id, project_data).__dict__ + if not update_project: status_code = 404 response = "Not found" @@ -52,4 +45,4 @@ def update_project(req: func.HttpRequest) -> func.HttpResponse: def _validate_project(project_data: dict) -> bool: project_keys = [field.name for field in dataclasses.fields(_domain.Project)] - return all(key in project_keys for key in project_data.keys()) \ No newline at end of file + return all(key in project_keys for key in project_data.keys()) diff --git a/V2/time_tracker/projects/_domain/_entities/_project.py b/V2/time_tracker/projects/_domain/_entities/_project.py index 3f8d332b..0b2ffe1a 100644 --- a/V2/time_tracker/projects/_domain/_entities/_project.py +++ b/V2/time_tracker/projects/_domain/_entities/_project.py @@ -1,13 +1,14 @@ from dataclasses import dataclass -from typing import List +from typing import List, Optional @dataclass(frozen=True) class Project: - id: str + id: Optional[int] name: str description: str project_type_id: int customer_id: int status: int - deleted: bool \ No newline at end of file + deleted: Optional[bool] + technologies: List[str] diff --git a/V2/time_tracker/projects/_domain/_persistence_contracts/_projects_dao.py b/V2/time_tracker/projects/_domain/_persistence_contracts/_projects_dao.py index 421f40fe..f38c8ebd 100644 --- a/V2/time_tracker/projects/_domain/_persistence_contracts/_projects_dao.py +++ b/V2/time_tracker/projects/_domain/_persistence_contracts/_projects_dao.py @@ -17,7 +17,7 @@ def get_by_id(self, id: int) -> Project: pass @abc.abstractmethod - def update(self, id: int, name: str, description: str, customer_id: int, status: int) -> Project: + def update(self, id: int, project_data: dict) -> Project: pass @abc.abstractmethod diff --git a/V2/time_tracker/projects/_domain/_services/_project.py b/V2/time_tracker/projects/_domain/_services/_project.py index cd69ccd4..0f99dafb 100644 --- a/V2/time_tracker/projects/_domain/_services/_project.py +++ b/V2/time_tracker/projects/_domain/_services/_project.py @@ -17,8 +17,8 @@ def get_all(self) -> typing.List[Project]: def get_by_id(self, id: int) -> Project: return self.project_dao.get_by_id(id) - def update(self, id: int, name: str, description: str, customer_id: int, status: int) -> Project: - return self.project_dao.update(id, name, description, customer_id, status) + def update(self, id: int, project_data: dict) -> Project: + return self.project_dao.update(id, project_data) def delete(self, id: int) -> Project: return self.project_dao.delete(id) diff --git a/V2/time_tracker/projects/_domain/_use_cases/_update_project_use_case.py b/V2/time_tracker/projects/_domain/_use_cases/_update_project_use_case.py index 32506498..628d7437 100644 --- a/V2/time_tracker/projects/_domain/_use_cases/_update_project_use_case.py +++ b/V2/time_tracker/projects/_domain/_use_cases/_update_project_use_case.py @@ -5,7 +5,5 @@ class UpdateProjectUseCase: def __init__(self, projects_service: ProjectService): self.projects_service = projects_service - def update_project( - self, id: int, name: str, description: str, customer_id: int, status: int - ) -> Project: - return self.projects_service.update(id, name, description, customer_id, status) + def update_project(self, id: int, project_data: dict) -> Project: + return self.projects_service.update(id, project_data) diff --git a/V2/time_tracker/projects/_infrastructure/_data_persistence/_projects_dao.py b/V2/time_tracker/projects/_infrastructure/_data_persistence/_projects_dao.py index 5a7339bf..5e1fcd9f 100644 --- a/V2/time_tracker/projects/_infrastructure/_data_persistence/_projects_dao.py +++ b/V2/time_tracker/projects/_infrastructure/_data_persistence/_projects_dao.py @@ -22,6 +22,10 @@ def __init__(self, database: _db.DB): sq.Column('customer_id', sq.Integer), sq.Column('status', sq.SmallInteger), sq.Column('deleted', sq.BOOLEAN), + sq.Column( + 'technologies', + sq.ARRAY(sq.String).with_variant(sq.String, "sqlite") + ), extend_existing=True, ) @@ -37,7 +41,7 @@ def create(self, project_data: domain.Project) -> domain.Project: except sq.exc.sqError: return None - + def get_by_id(self, id: int) -> domain.Project: query = sq.sql.select(self.project).where(self.project.c.id == id) project = self.db.get_session().execute(query).one_or_none() @@ -60,14 +64,11 @@ def delete(self, id: int) -> domain.Project: self.db.get_session().execute(query) return self.get_by_id(id) - def update(self, id: int, name: str, description: str, customer_id: int, status: int) -> domain.Project: - new_project = {"name": name, "description": description, "customer_id": customer_id, "status": status} - project_data_validated = {key: value for (key, value) in new_project.items() if value is not None} - - query = self.project.update().where(self.project.c.id == id).values(project_data_validated) + def update(self, id: int, project_data: dict) -> domain.Project: + query = self.project.update().where(self.project.c.id == id).values(project_data) self.db.get_session().execute(query) return self.get_by_id(id) def __create_project_dto(self, project: dict) -> domain.Project: project = {key: project.get(key) for key in self.project_key} - return domain.Project(**project) \ No newline at end of file + return domain.Project(**project) From 6376755a1e57a1ae7b0506ac33d27073b7347863 Mon Sep 17 00:00:00 2001 From: Jipson Murillo Date: Wed, 24 Nov 2021 13:15:31 -0500 Subject: [PATCH 4/8] test: TT-417 add test with customer id --- .../api/azure/project_azure_endpoints_test.py | 26 ++++++++++------- V2/tests/conftest.py | 2 +- V2/tests/fixtures.py | 12 ++++++++ .../integration/daos/projects_dao_test.py | 29 ++++++++++++------- .../_data_persistence/_projects_dao.py | 4 +-- 5 files changed, 50 insertions(+), 23 deletions(-) diff --git a/V2/tests/api/azure/project_azure_endpoints_test.py b/V2/tests/api/azure/project_azure_endpoints_test.py index 137c3939..912a4fdd 100644 --- a/V2/tests/api/azure/project_azure_endpoints_test.py +++ b/V2/tests/api/azure/project_azure_endpoints_test.py @@ -22,9 +22,11 @@ def _new_project(project: domain.Project, database: DB): def test__project_azure_endpoint__returns_all_projects( - test_db, project_factory, insert_project + test_db, project_factory, insert_project, insert_customer, customer_factory ): - project_to_insert = [project_factory(), project_factory()] + inserted_customer = insert_customer(customer_factory(), test_db) + project_to_insert = [project_factory(customer_id=inserted_customer.id), + project_factory(customer_id=inserted_customer.id)] inserted_projects = [ insert_project(project_to_insert[0], test_db).__dict__, insert_project(project_to_insert[1], test_db).__dict__ @@ -39,9 +41,10 @@ def test__project_azure_endpoint__returns_all_projects( def test__project_azure_endpoint__returns_an_project__when_project_matches_its_id( - test_db, project_factory, insert_project + test_db, project_factory, insert_project, insert_customer, customer_factory ): - project_to_insert = project_factory() + inserted_customer = insert_customer(customer_factory(), test_db) + project_to_insert = project_factory(customer_id=inserted_customer.id) inserted_project = insert_project(project_to_insert, test_db).__dict__ req = func.HttpRequest( @@ -74,9 +77,10 @@ def test__projects_azure_endpoint__returns_a_status_code_400__when_project_reciv def test__project_azure_endpoint__returns_an_project_with_inactive_status__when_an_project_matching_its_id_is_found( - test_db, project_factory, insert_project + test_db, project_factory, insert_project, insert_customer, customer_factory ): - project_to_insert = project_factory() + inserted_customer = insert_customer(customer_factory(), test_db) + project_to_insert = project_factory(customer_id=inserted_customer.id) inserted_project = insert_project(project_to_insert, test_db).__dict__ req = func.HttpRequest( @@ -110,9 +114,10 @@ def test__delete_projects_azure_endpoint__returns_a_status_code_400__when_projec def test__update_project_azure_endpoint__returns_an_project__when_found_an_project_to_update( - test_db, project_factory, insert_project + test_db, project_factory, insert_project, insert_customer, customer_factory ): - project_to_insert = project_factory() + inserted_customer = insert_customer(customer_factory(), test_db) + project_to_insert = project_factory(customer_id=inserted_customer.id) inserted_project = insert_project(project_to_insert, test_db).__dict__ project_body = {"description": Faker().sentence()} @@ -147,9 +152,10 @@ def test__update_projects_azure_endpoint__returns_a_status_code_400__when_projec def test__project_azure_endpoint__creates_an_project__when_project_has_all_attributes( - project_factory + test_db, project_factory, insert_customer, customer_factory ): - project_body = project_factory().__dict__ + inserted_customer = insert_customer(customer_factory(), test_db) + project_body = project_factory(customer_id=inserted_customer.id).__dict__ body = json.dumps(project_body).encode("utf-8") req = func.HttpRequest( method='POST', diff --git a/V2/tests/conftest.py b/V2/tests/conftest.py index d593c439..ff67203c 100644 --- a/V2/tests/conftest.py +++ b/V2/tests/conftest.py @@ -1,5 +1,5 @@ # flake8: noqa from fixtures import _activity_factory, _test_db, _insert_activity from fixtures import _time_entry_factory -from fixtures import _customer_factory +from fixtures import _customer_factory, _insert_customer from fixtures import _project_factory diff --git a/V2/tests/fixtures.py b/V2/tests/fixtures.py index ea8e2d1f..f45eef57 100644 --- a/V2/tests/fixtures.py +++ b/V2/tests/fixtures.py @@ -5,6 +5,7 @@ import time_tracker.activities._infrastructure as activities_infrastructure import time_tracker.time_entries._domain as time_entries_domain import time_tracker.customers._domain as customers_domain +import time_tracker.customers._infrastructure as customers_infrastructure import time_tracker.projects._domain as projects_domain from time_tracker._infrastructure import DB @@ -95,6 +96,8 @@ def _make_customer( return customer return _make_customer + + @pytest.fixture(name='project_factory') def _project_factory() -> projects_domain.Project: def _make_project( @@ -119,3 +122,12 @@ def _make_project( ) return project return _make_project + + +@pytest.fixture(name='insert_customer') +def _insert_customer() -> dict: + def _new_customer(activity: customers_domain.Customer, database: DB): + dao = customers_infrastructure.CustomersSQLDao(database) + new_activity = dao.create(activity) + return new_activity + return _new_customer diff --git a/V2/tests/integration/daos/projects_dao_test.py b/V2/tests/integration/daos/projects_dao_test.py index 79cfa76f..b1879894 100644 --- a/V2/tests/integration/daos/projects_dao_test.py +++ b/V2/tests/integration/daos/projects_dao_test.py @@ -24,10 +24,11 @@ def _clean_database(): def test__create_project__returns_a_project_dto__when_saves_correctly_with_sql_database( - create_fake_dao, project_factory + create_fake_dao, project_factory, insert_customer, customer_factory ): dao = create_fake_dao - project_to_insert = project_factory() + inserted_customer = insert_customer(customer_factory(), dao.db) + project_to_insert = project_factory(customer_id=inserted_customer.id) inserted_project = dao.create(project_to_insert) @@ -36,10 +37,12 @@ def test__create_project__returns_a_project_dto__when_saves_correctly_with_sql_d def test_update__returns_an_update_project__when_an_project_matching_its_id_is_found_with_sql_database( - create_fake_dao, project_factory + create_fake_dao, project_factory, insert_customer, customer_factory ): dao = create_fake_dao - project_to_insert = project_factory() + inserted_customer = insert_customer(customer_factory(), dao.db) + project_to_insert = project_factory(customer_id=inserted_customer.id) + inserted_project = dao.create(project_to_insert) expected_description = Faker().sentence() @@ -62,10 +65,13 @@ def test_update__returns_none__when_no_project_matching_its_id_is_found_with_sql def test__get_all__returns_a_list_of_project_dto_objects__when_one_or_more_projects_are_found_with_sql_database( - create_fake_dao, project_factory + create_fake_dao, project_factory, insert_customer, customer_factory ): dao = create_fake_dao - project_to_inserts = [project_factory(), project_factory()] + inserted_customer = insert_customer(customer_factory(), dao.db) + project_to_inserts = [project_factory(customer_id=inserted_customer.id), + project_factory(customer_id=inserted_customer.id)] + inserted_projects = [ dao.create(project_to_inserts[0]), dao.create(project_to_inserts[1]) @@ -77,10 +83,12 @@ def test__get_all__returns_a_list_of_project_dto_objects__when_one_or_more_proje def test_get_by_id__returns_an_project_dto__when_found_one_project_that_matches_its_id_with_sql_database( - create_fake_dao, project_factory + create_fake_dao, project_factory, insert_customer, customer_factory ): dao = create_fake_dao - project_to_insert = project_factory() + inserted_customer = insert_customer(customer_factory(), dao.db) + project_to_insert = project_factory(customer_id=inserted_customer.id) + inserted_project = dao.create(project_to_insert) project = dao.get_by_id(inserted_project.id) @@ -111,10 +119,11 @@ def test_get_all__returns_an_empty_list__when_doesnt_found_any_projects_with_sql def test_delete__returns_an_project_with_inactive_status__when_an_project_matching_its_id_is_found_with_sql_database( - create_fake_dao, project_factory + create_fake_dao, project_factory, insert_customer, customer_factory ): dao = create_fake_dao - project_to_insert = project_factory() + inserted_customer = insert_customer(customer_factory(), dao.db) + project_to_insert = project_factory(customer_id=inserted_customer.id) inserted_project = dao.create(project_to_insert) project = dao.delete(inserted_project.id) diff --git a/V2/time_tracker/projects/_infrastructure/_data_persistence/_projects_dao.py b/V2/time_tracker/projects/_infrastructure/_data_persistence/_projects_dao.py index 5e1fcd9f..3f7950ed 100644 --- a/V2/time_tracker/projects/_infrastructure/_data_persistence/_projects_dao.py +++ b/V2/time_tracker/projects/_infrastructure/_data_persistence/_projects_dao.py @@ -19,7 +19,7 @@ def __init__(self, database: _db.DB): sq.Column('name', sq.String), sq.Column('description', sq.String), sq.Column('project_type_id', sq.Integer), - sq.Column('customer_id', sq.Integer), + sq.Column('customer_id', sq.Integer, sq.ForeignKey('customer.id')), sq.Column('status', sq.SmallInteger), sq.Column('deleted', sq.BOOLEAN), sq.Column( @@ -39,7 +39,7 @@ def create(self, project_data: domain.Project) -> domain.Project: new_project.update({"id": project.inserted_primary_key[0]}) return self.__create_project_dto(new_project) - except sq.exc.sqError: + except sq.exc.SQLAlchemyError: return None def get_by_id(self, id: int) -> domain.Project: From 4e614c2a573221b72c50cc1cf321b043728f5b5b Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 24 Nov 2021 19:17:57 -0500 Subject: [PATCH 5/8] refactor: TT-417 created enums and use --- .../api/azure/project_azure_endpoints_test.py | 41 ++++++++---------- V2/tests/fixtures.py | 8 ++-- .../integration/daos/projects_dao_test.py | 40 +++++++++-------- .../_application/_projects/_create_project.py | 43 +++++++++---------- .../_application/_projects/_delete_project.py | 23 +++++----- .../_application/_projects/_get_projects.py | 19 ++++---- .../_application/_projects/_update_project.py | 26 +++++------ V2/time_tracker/utils/enums/__init__.py | 2 + V2/time_tracker/utils/enums/response_enums.py | 16 +++++++ 9 files changed, 115 insertions(+), 103 deletions(-) create mode 100644 V2/time_tracker/utils/enums/__init__.py create mode 100644 V2/time_tracker/utils/enums/response_enums.py diff --git a/V2/tests/api/azure/project_azure_endpoints_test.py b/V2/tests/api/azure/project_azure_endpoints_test.py index 912a4fdd..e31e6279 100644 --- a/V2/tests/api/azure/project_azure_endpoints_test.py +++ b/V2/tests/api/azure/project_azure_endpoints_test.py @@ -7,29 +7,28 @@ from time_tracker.projects._application import _projects as azure_projects from time_tracker.projects import _domain as domain from time_tracker.projects import _infrastructure as infrastructure -from time_tracker._infrastructure import DB PROJECT_URL = '/api/projects/' @pytest.fixture(name='insert_project') -def _insert_project() -> domain.Project: - def _new_project(project: domain.Project, database: DB): - dao = infrastructure.ProjectsSQLDao(database) - new_project = dao.create(project) - return new_project +def insert_project(test_db, insert_customer, project_factory, customer_factory) -> domain.Project: + inserted_customer = insert_customer(customer_factory(), test_db) + + def _new_project(): + project_to_insert = project_factory(customer_id=inserted_customer.id) + dao = infrastructure.ProjectsSQLDao(test_db) + inserted_project = dao.create(project_to_insert) + return inserted_project return _new_project def test__project_azure_endpoint__returns_all_projects( - test_db, project_factory, insert_project, insert_customer, customer_factory + insert_project ): - inserted_customer = insert_customer(customer_factory(), test_db) - project_to_insert = [project_factory(customer_id=inserted_customer.id), - project_factory(customer_id=inserted_customer.id)] inserted_projects = [ - insert_project(project_to_insert[0], test_db).__dict__, - insert_project(project_to_insert[1], test_db).__dict__ + insert_project().__dict__, + insert_project().__dict__ ] req = func.HttpRequest(method='GET', body=None, url=PROJECT_URL) @@ -41,11 +40,9 @@ def test__project_azure_endpoint__returns_all_projects( def test__project_azure_endpoint__returns_an_project__when_project_matches_its_id( - test_db, project_factory, insert_project, insert_customer, customer_factory + insert_project ): - inserted_customer = insert_customer(customer_factory(), test_db) - project_to_insert = project_factory(customer_id=inserted_customer.id) - inserted_project = insert_project(project_to_insert, test_db).__dict__ + inserted_project = insert_project().__dict__ req = func.HttpRequest( method='GET', @@ -77,11 +74,9 @@ def test__projects_azure_endpoint__returns_a_status_code_400__when_project_reciv def test__project_azure_endpoint__returns_an_project_with_inactive_status__when_an_project_matching_its_id_is_found( - test_db, project_factory, insert_project, insert_customer, customer_factory + insert_project ): - inserted_customer = insert_customer(customer_factory(), test_db) - project_to_insert = project_factory(customer_id=inserted_customer.id) - inserted_project = insert_project(project_to_insert, test_db).__dict__ + inserted_project = insert_project().__dict__ req = func.HttpRequest( method='DELETE', @@ -114,11 +109,9 @@ def test__delete_projects_azure_endpoint__returns_a_status_code_400__when_projec def test__update_project_azure_endpoint__returns_an_project__when_found_an_project_to_update( - test_db, project_factory, insert_project, insert_customer, customer_factory + insert_project ): - inserted_customer = insert_customer(customer_factory(), test_db) - project_to_insert = project_factory(customer_id=inserted_customer.id) - inserted_project = insert_project(project_to_insert, test_db).__dict__ + inserted_project = insert_project().__dict__ project_body = {"description": Faker().sentence()} req = func.HttpRequest( diff --git a/V2/tests/fixtures.py b/V2/tests/fixtures.py index f45eef57..2eae7b16 100644 --- a/V2/tests/fixtures.py +++ b/V2/tests/fixtures.py @@ -125,9 +125,9 @@ def _make_project( @pytest.fixture(name='insert_customer') -def _insert_customer() -> dict: - def _new_customer(activity: customers_domain.Customer, database: DB): +def _insert_customer() -> customers_domain.Customer: + def _new_customer(customer: customers_domain.Customer, database: DB): dao = customers_infrastructure.CustomersSQLDao(database) - new_activity = dao.create(activity) - return new_activity + new_customer = dao.create(customer) + return new_customer return _new_customer diff --git a/V2/tests/integration/daos/projects_dao_test.py b/V2/tests/integration/daos/projects_dao_test.py index b1879894..64837e37 100644 --- a/V2/tests/integration/daos/projects_dao_test.py +++ b/V2/tests/integration/daos/projects_dao_test.py @@ -7,6 +7,18 @@ from time_tracker._infrastructure import DB +@pytest.fixture(name='insert_project') +def _insert_project(customer_factory, test_db, insert_customer, create_fake_dao, project_factory) -> domain.Project: + inserted_customer = insert_customer(customer_factory(), test_db) + + def _new_project(): + project_to_insert = project_factory(customer_id=inserted_customer.id) + inserted_project = create_fake_dao.create(project_to_insert) + return inserted_project + + return _new_project + + @pytest.fixture(name='create_fake_dao') def _create_fake_dao() -> domain.ProjectsDao: db_fake = DB() @@ -37,13 +49,11 @@ def test__create_project__returns_a_project_dto__when_saves_correctly_with_sql_d def test_update__returns_an_update_project__when_an_project_matching_its_id_is_found_with_sql_database( - create_fake_dao, project_factory, insert_customer, customer_factory + create_fake_dao, insert_project ): dao = create_fake_dao - inserted_customer = insert_customer(customer_factory(), dao.db) - project_to_insert = project_factory(customer_id=inserted_customer.id) - inserted_project = dao.create(project_to_insert) + inserted_project = insert_project() expected_description = Faker().sentence() updated_project = dao.update(inserted_project.id, {"description": expected_description}) @@ -65,16 +75,13 @@ def test_update__returns_none__when_no_project_matching_its_id_is_found_with_sql def test__get_all__returns_a_list_of_project_dto_objects__when_one_or_more_projects_are_found_with_sql_database( - create_fake_dao, project_factory, insert_customer, customer_factory + create_fake_dao, insert_project ): dao = create_fake_dao - inserted_customer = insert_customer(customer_factory(), dao.db) - project_to_inserts = [project_factory(customer_id=inserted_customer.id), - project_factory(customer_id=inserted_customer.id)] inserted_projects = [ - dao.create(project_to_inserts[0]), - dao.create(project_to_inserts[1]) + insert_project(), + insert_project() ] projects = dao.get_all() @@ -83,13 +90,11 @@ def test__get_all__returns_a_list_of_project_dto_objects__when_one_or_more_proje def test_get_by_id__returns_an_project_dto__when_found_one_project_that_matches_its_id_with_sql_database( - create_fake_dao, project_factory, insert_customer, customer_factory + create_fake_dao, insert_project ): dao = create_fake_dao - inserted_customer = insert_customer(customer_factory(), dao.db) - project_to_insert = project_factory(customer_id=inserted_customer.id) - inserted_project = dao.create(project_to_insert) + inserted_project = insert_project() project = dao.get_by_id(inserted_project.id) @@ -119,12 +124,11 @@ def test_get_all__returns_an_empty_list__when_doesnt_found_any_projects_with_sql def test_delete__returns_an_project_with_inactive_status__when_an_project_matching_its_id_is_found_with_sql_database( - create_fake_dao, project_factory, insert_customer, customer_factory + create_fake_dao, insert_project ): dao = create_fake_dao - inserted_customer = insert_customer(customer_factory(), dao.db) - project_to_insert = project_factory(customer_id=inserted_customer.id) - inserted_project = dao.create(project_to_insert) + + inserted_project = insert_project() project = dao.delete(inserted_project.id) diff --git a/V2/time_tracker/projects/_application/_projects/_create_project.py b/V2/time_tracker/projects/_application/_projects/_create_project.py index 2332d59e..8b5612e8 100644 --- a/V2/time_tracker/projects/_application/_projects/_create_project.py +++ b/V2/time_tracker/projects/_application/_projects/_create_project.py @@ -7,6 +7,7 @@ from ... import _domain from ... import _infrastructure from time_tracker._infrastructure import DB +from time_tracker.utils.enums import ResponseEnums as enums def create_project(req: func.HttpRequest) -> func.HttpResponse: @@ -19,34 +20,30 @@ def create_project(req: func.HttpRequest) -> func.HttpResponse: validation_errors = _validate_project(project_data) if validation_errors: - return func.HttpResponse( - body=json.dumps(validation_errors), status_code=400, mimetype="application/json" + status_code = enums.STATUS_BAD_REQUEST.value + response = json.dumps(validation_errors) + else: + project_to_create = _domain.Project( + id=None, + name=project_data["name"], + description=project_data["description"], + project_type_id=project_data["project_type_id"], + customer_id=project_data["customer_id"], + status=project_data["status"], + deleted=False, + technologies=project_data["technologies"] ) - project_to_create = _domain.Project( - id=None, - name=project_data["name"], - description=project_data["description"], - project_type_id=project_data["project_type_id"], - customer_id=project_data["customer_id"], - status=project_data["status"], - deleted=False, - technologies=project_data["technologies"] - ) - - created_project = use_case.create_project(project_to_create) + created_project = use_case.create_project(project_to_create) - if not created_project: - return func.HttpResponse( - body=json.dumps({'error': 'project could not be created'}), - status_code=500, - mimetype="application/json" - ) + status_code, response = [ + enums.INTERNAL_SERVER_ERROR.value, json.dumps({'error': f'project {enums.NOT_CREATED.value}'}) + ] if not created_project else [enums.STATUS_CREATED.value, json.dumps(created_project.__dict__)] return func.HttpResponse( - body=json.dumps(created_project.__dict__), - status_code=201, - mimetype="application/json" + body=response, + status_code=status_code, + mimetype=enums.MIME_TYPE.value ) diff --git a/V2/time_tracker/projects/_application/_projects/_delete_project.py b/V2/time_tracker/projects/_application/_projects/_delete_project.py index f78fd587..f4c378ea 100644 --- a/V2/time_tracker/projects/_application/_projects/_delete_project.py +++ b/V2/time_tracker/projects/_application/_projects/_delete_project.py @@ -5,6 +5,7 @@ from ... import _domain from ... import _infrastructure from time_tracker._infrastructure import DB +from time_tracker.utils.enums import ResponseEnums as enums def delete_project(req: func.HttpRequest) -> func.HttpResponse: @@ -15,22 +16,20 @@ def delete_project(req: func.HttpRequest) -> func.HttpResponse: try: project_id = int(req.route_params.get("id")) deleted_project = use_case.delete_project(project_id) - if not deleted_project: - return func.HttpResponse( - body="Not found", - status_code=404, - mimetype="application/json" - ) + + status_code, response = [ + enums.STATUS_NOT_FOUND.value, enums.NOT_FOUND.value.encode() + ] if not deleted_project else [enums.STATUS_OK.value, json.dumps(deleted_project.__dict__)] return func.HttpResponse( - body=json.dumps(deleted_project.__dict__, default=str), - status_code=200, - mimetype="application/json", + body=response, + status_code=status_code, + mimetype=enums.MIME_TYPE.value, ) except ValueError: return func.HttpResponse( - body=b"Invalid Format ID", - status_code=400, - mimetype="application/json" + body=enums.INVALID_ID.value.encode(), + status_code=enums.STATUS_BAD_REQUEST.value, + mimetype=enums.MIME_TYPE.value ) diff --git a/V2/time_tracker/projects/_application/_projects/_get_projects.py b/V2/time_tracker/projects/_application/_projects/_get_projects.py index dc7adb84..0909479b 100644 --- a/V2/time_tracker/projects/_application/_projects/_get_projects.py +++ b/V2/time_tracker/projects/_application/_projects/_get_projects.py @@ -6,6 +6,7 @@ from ... import _domain from ... import _infrastructure from time_tracker._infrastructure import DB +from time_tracker.utils.enums import ResponseEnums as enums def get_projects(req: func.HttpRequest) -> func.HttpResponse: @@ -20,24 +21,24 @@ def get_projects(req: func.HttpRequest) -> func.HttpResponse: response = _get_by_id(int(project_id), project_service) if not response: return func.HttpResponse( - body="Not found", - status_code=404, - mimetype="application/json" + body=enums.NOT_FOUND.value.encode(), + status_code=enums.STATUS_NOT_FOUND.value, + mimetype=enums.MIME_TYPE.value ) else: response = _get_all(project_service) return func.HttpResponse( - body=json.dumps(response, default=str), - status_code=200, - mimetype="application/json", + body=json.dumps(response), + status_code=enums.STATUS_OK.value, + mimetype=enums.MIME_TYPE.value, ) except ValueError: return func.HttpResponse( - body=b"Invalid Format ID", - status_code=400, - mimetype="application/json" + body=enums.INVALID_ID.value.encode(), + status_code=enums.STATUS_BAD_REQUEST.value, + mimetype=enums.MIME_TYPE.value ) diff --git a/V2/time_tracker/projects/_application/_projects/_update_project.py b/V2/time_tracker/projects/_application/_projects/_update_project.py index 7e543cdc..59240a6c 100644 --- a/V2/time_tracker/projects/_application/_projects/_update_project.py +++ b/V2/time_tracker/projects/_application/_projects/_update_project.py @@ -6,6 +6,7 @@ from ... import _domain from ... import _infrastructure from time_tracker._infrastructure import DB +from time_tracker.utils.enums import ResponseEnums as enums def update_project(req: func.HttpRequest) -> func.HttpResponse: @@ -17,29 +18,28 @@ def update_project(req: func.HttpRequest) -> func.HttpResponse: try: project_id = int(req.route_params.get("id")) project_data = req.get_json() - status_code = 200 if not _validate_project(project_data): - status_code = 400 - response = "Incorrect project body" + status_code = enums.STATUS_BAD_REQUEST.value + response = bytes(f"{enums.INCORRECT_BODY.value} project") - response = use_case.update_project(project_id, project_data).__dict__ - - if not update_project: - status_code = 404 - response = "Not found" + else: + updated_project = use_case.update_project(project_id, project_data) + status_code, response = [ + enums.STATUS_NOT_FOUND.value, enums.NOT_FOUND.value.encode() + ] if not updated_project else [enums.STATUS_OK.value, json.dumps(updated_project.__dict__)] return func.HttpResponse( - body=json.dumps(response, default=str), + body=response, status_code=status_code, - mimetype="application/json", + mimetype=enums.MIME_TYPE.value, ) except ValueError: return func.HttpResponse( - body=b"Invalid Format ID", - status_code=400, - mimetype="application/json" + body=enums.INVALID_ID.value.encode(), + status_code=enums.STATUS_BAD_REQUEST.value, + mimetype=enums.MIME_TYPE.value, ) diff --git a/V2/time_tracker/utils/enums/__init__.py b/V2/time_tracker/utils/enums/__init__.py new file mode 100644 index 00000000..317ca876 --- /dev/null +++ b/V2/time_tracker/utils/enums/__init__.py @@ -0,0 +1,2 @@ +# flake8: noqa +from .response_enums import ResponseEnums \ No newline at end of file diff --git a/V2/time_tracker/utils/enums/response_enums.py b/V2/time_tracker/utils/enums/response_enums.py new file mode 100644 index 00000000..781cba86 --- /dev/null +++ b/V2/time_tracker/utils/enums/response_enums.py @@ -0,0 +1,16 @@ +from enum import Enum + + +class ResponseEnums(Enum): + STATUS_CREATED = 201 + STATUS_OK = 200 + STATUS_BAD_REQUEST = 400 + STATUS_NOT_FOUND = 404 + INTERNAL_SERVER_ERROR = 500 + + INVALID_ID = "Invalid Format ID" + NOT_FOUND = "Not found" + NOT_CREATED = "could not be created" + INCORRECT_BODY = "Incorrect body" + + MIME_TYPE = "application/json" From 4057a106d0c3a388a689922f294f65e2dec1b23f Mon Sep 17 00:00:00 2001 From: Jipson Murillo Date: Thu, 25 Nov 2021 12:34:16 -0500 Subject: [PATCH 6/8] test: TT-417 add missing tests --- .../api/azure/project_azure_endpoints_test.py | 120 +++++++++++++++--- .../_application/_projects/_create_project.py | 8 +- .../_application/_projects/_update_project.py | 2 +- 3 files changed, 110 insertions(+), 20 deletions(-) diff --git a/V2/tests/api/azure/project_azure_endpoints_test.py b/V2/tests/api/azure/project_azure_endpoints_test.py index e31e6279..a0cbc186 100644 --- a/V2/tests/api/azure/project_azure_endpoints_test.py +++ b/V2/tests/api/azure/project_azure_endpoints_test.py @@ -7,6 +7,7 @@ from time_tracker.projects._application import _projects as azure_projects from time_tracker.projects import _domain as domain from time_tracker.projects import _infrastructure as infrastructure +from time_tracker.utils.enums import ResponseEnums as enums PROJECT_URL = '/api/projects/' @@ -35,7 +36,7 @@ def test__project_azure_endpoint__returns_all_projects( response = azure_projects._get_projects.get_projects(req) projects_json_data = response.get_body().decode("utf-8") - assert response.status_code == 200 + assert response.status_code == enums.STATUS_OK.value assert projects_json_data == json.dumps(inserted_projects) @@ -54,7 +55,7 @@ def test__project_azure_endpoint__returns_an_project__when_project_matches_its_i response = azure_projects._get_projects.get_projects(req) activitiy_json_data = response.get_body().decode("utf-8") - assert response.status_code == 200 + assert response.status_code == enums.STATUS_OK.value assert activitiy_json_data == json.dumps(inserted_project) @@ -69,11 +70,11 @@ def test__projects_azure_endpoint__returns_a_status_code_400__when_project_reciv response = azure_projects._get_projects.get_projects(req) - assert response.status_code == 400 - assert response.get_body() == b'Invalid Format ID' + assert response.status_code == enums.STATUS_BAD_REQUEST.value + assert response.get_body() == enums.INVALID_ID.value.encode() -def test__project_azure_endpoint__returns_an_project_with_inactive_status__when_an_project_matching_its_id_is_found( +def test__project_azure_endpoint__returns_an_project_with_inactive_status__when_a_project_matching_its_id_is_found( insert_project ): inserted_project = insert_project().__dict__ @@ -88,7 +89,7 @@ def test__project_azure_endpoint__returns_an_project_with_inactive_status__when_ response = azure_projects._delete_project.delete_project(req) project_json_data = json.loads(response.get_body().decode("utf-8")) - assert response.status_code == 200 + assert response.status_code == enums.STATUS_OK.value assert project_json_data['status'] == 0 assert project_json_data['deleted'] is True @@ -104,11 +105,26 @@ def test__delete_projects_azure_endpoint__returns_a_status_code_400__when_projec response = azure_projects._delete_project.delete_project(req) - assert response.status_code == 400 - assert response.get_body() == b'Invalid Format ID' + assert response.status_code == enums.STATUS_BAD_REQUEST.value + assert response.get_body() == enums.INVALID_ID.value.encode() -def test__update_project_azure_endpoint__returns_an_project__when_found_an_project_to_update( +def test__delete_projects_azure_endpoint__returns_a_status_code_404__when_no_found_a_project_to_delete( +): + req = func.HttpRequest( + method="DELETE", + body=None, + url=PROJECT_URL, + route_params={"id": Faker().pyint()}, + ) + + response = azure_projects._delete_project.delete_project(req) + + assert response.status_code == enums.STATUS_NOT_FOUND.value + assert response.get_body() == enums.NOT_FOUND.value.encode() + + +def test__update_project_azure_endpoint__returns_an_project__when_found_a_project_to_update( insert_project ): inserted_project = insert_project().__dict__ @@ -125,10 +141,44 @@ def test__update_project_azure_endpoint__returns_an_project__when_found_an_proje activitiy_json_data = response.get_body().decode("utf-8") inserted_project.update(project_body) - assert response.status_code == 200 + assert response.status_code == enums.STATUS_OK.value assert activitiy_json_data == json.dumps(inserted_project) +def test__update_projects_azure_endpoint__returns_a_status_code_404__when_no_found_a_project_to_update( + project_factory +): + project_body = project_factory().__dict__ + + req = func.HttpRequest( + method="PUT", + body=json.dumps(project_body).encode("utf-8"), + url=PROJECT_URL, + route_params={"id": Faker().pyint()}, + ) + + response = azure_projects._update_project.update_project(req) + + assert response.status_code == enums.STATUS_NOT_FOUND.value + assert response.get_body() == enums.NOT_FOUND.value.encode() + + +def test__update_projects_azure_endpoint__returns_a_status_code_400__when_recive_an_incorrect_body( +): + project_body = Faker().pydict(5, True, str) + req = func.HttpRequest( + method="PUT", + body=json.dumps(project_body).encode("utf-8"), + url=PROJECT_URL, + route_params={"id": Faker().pyint()}, + ) + + response = azure_projects._update_project.update_project(req) + + assert response.status_code == enums.STATUS_BAD_REQUEST.value + assert response.get_body() == enums.INCORRECT_BODY.value.encode() + + def test__update_projects_azure_endpoint__returns_a_status_code_400__when_project_recive_invalid_id( ): req = func.HttpRequest( @@ -140,15 +190,15 @@ def test__update_projects_azure_endpoint__returns_a_status_code_400__when_projec response = azure_projects._update_project.update_project(req) - assert response.status_code == 400 - assert response.get_body() == b'Invalid Format ID' + assert response.status_code == enums.STATUS_BAD_REQUEST.value + assert response.get_body() == enums.INVALID_ID.value.encode() def test__project_azure_endpoint__creates_an_project__when_project_has_all_attributes( test_db, project_factory, insert_customer, customer_factory ): - inserted_customer = insert_customer(customer_factory(), test_db) - project_body = project_factory(customer_id=inserted_customer.id).__dict__ + insert_customer(customer_factory(), test_db) + project_body = project_factory().__dict__ body = json.dumps(project_body).encode("utf-8") req = func.HttpRequest( method='POST', @@ -160,5 +210,45 @@ def test__project_azure_endpoint__creates_an_project__when_project_has_all_attri project_json_data = json.loads(response.get_body()) project_body['id'] = project_json_data['id'] - assert response.status_code == 201 + assert response.status_code == enums.STATUS_CREATED.value assert project_json_data == project_body + + +def test__project_azure_endpoint__returns_a_status_code_400__when_project_does_not_all_attributes( + test_db, project_factory, insert_customer, customer_factory +): + inserted_customer = insert_customer(customer_factory(), test_db) + project_body = project_factory(customer_id=inserted_customer.id).__dict__ + project_body.pop('name') + + body = json.dumps(project_body).encode("utf-8") + req = func.HttpRequest( + method='POST', + body=body, + url=PROJECT_URL, + ) + + response = azure_projects._create_project.create_project(req) + + assert response.status_code == enums.STATUS_BAD_REQUEST.value + assert response.get_body() == json.dumps(['The name key is missing in the input data']).encode() + + +def test__project_azure_endpoint__returns_a_status_code_500__when_project_recive_incorrect_type_data( + project_factory, insert_customer, customer_factory, test_db +): + insert_customer(customer_factory(), test_db) + project_body = project_factory(technologies=Faker().pylist(2, True, str)).__dict__ + + body = json.dumps(project_body).encode("utf-8") + print(project_body) + req = func.HttpRequest( + method='POST', + body=body, + url=PROJECT_URL, + ) + + response = azure_projects._create_project.create_project(req) + + assert response.status_code == enums.INTERNAL_SERVER_ERROR.value + assert response.get_body() == enums.NOT_CREATED.value.encode() diff --git a/V2/time_tracker/projects/_application/_projects/_create_project.py b/V2/time_tracker/projects/_application/_projects/_create_project.py index 8b5612e8..7ed423ce 100644 --- a/V2/time_tracker/projects/_application/_projects/_create_project.py +++ b/V2/time_tracker/projects/_application/_projects/_create_project.py @@ -6,13 +6,13 @@ from ... import _domain from ... import _infrastructure -from time_tracker._infrastructure import DB +from time_tracker._infrastructure import DB as database from time_tracker.utils.enums import ResponseEnums as enums def create_project(req: func.HttpRequest) -> func.HttpResponse: - database = DB() - project_dao = _infrastructure.ProjectsSQLDao(database) + + project_dao = _infrastructure.ProjectsSQLDao(database()) project_service = _domain.ProjectService(project_dao) use_case = _domain._use_cases.CreateProjectUseCase(project_service) @@ -37,7 +37,7 @@ def create_project(req: func.HttpRequest) -> func.HttpResponse: created_project = use_case.create_project(project_to_create) status_code, response = [ - enums.INTERNAL_SERVER_ERROR.value, json.dumps({'error': f'project {enums.NOT_CREATED.value}'}) + enums.INTERNAL_SERVER_ERROR.value, enums.NOT_CREATED.value.encode() ] if not created_project else [enums.STATUS_CREATED.value, json.dumps(created_project.__dict__)] return func.HttpResponse( diff --git a/V2/time_tracker/projects/_application/_projects/_update_project.py b/V2/time_tracker/projects/_application/_projects/_update_project.py index 59240a6c..ecf064bd 100644 --- a/V2/time_tracker/projects/_application/_projects/_update_project.py +++ b/V2/time_tracker/projects/_application/_projects/_update_project.py @@ -21,7 +21,7 @@ def update_project(req: func.HttpRequest) -> func.HttpResponse: if not _validate_project(project_data): status_code = enums.STATUS_BAD_REQUEST.value - response = bytes(f"{enums.INCORRECT_BODY.value} project") + response = enums.INCORRECT_BODY.value.encode() else: updated_project = use_case.update_project(project_id, project_data) From 25f78608fd1784fde997456a569078cf461545e3 Mon Sep 17 00:00:00 2001 From: Alexander Date: Thu, 25 Nov 2021 13:09:04 -0500 Subject: [PATCH 7/8] test: TT-417 add missing tests and resolve comments --- .../_application/_projects/_delete_project.py | 4 ++-- .../projects/_application/_projects/_get_projects.py | 5 ++--- .../_application/_projects/_update_project.py | 11 ++++++++--- .../_data_persistence/_projects_dao.py | 9 ++++++--- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/V2/time_tracker/projects/_application/_projects/_delete_project.py b/V2/time_tracker/projects/_application/_projects/_delete_project.py index f4c378ea..9a1275d5 100644 --- a/V2/time_tracker/projects/_application/_projects/_delete_project.py +++ b/V2/time_tracker/projects/_application/_projects/_delete_project.py @@ -4,12 +4,12 @@ from ... import _domain from ... import _infrastructure -from time_tracker._infrastructure import DB +from time_tracker._infrastructure import DB as database from time_tracker.utils.enums import ResponseEnums as enums def delete_project(req: func.HttpRequest) -> func.HttpResponse: - project_dao = _infrastructure.ProjectsSQLDao(DB()) + project_dao = _infrastructure.ProjectsSQLDao(database()) project_service = _domain.ProjectService(project_dao) use_case = _domain._use_cases.DeleteProjectUseCase(project_service) diff --git a/V2/time_tracker/projects/_application/_projects/_get_projects.py b/V2/time_tracker/projects/_application/_projects/_get_projects.py index 0909479b..2297609d 100644 --- a/V2/time_tracker/projects/_application/_projects/_get_projects.py +++ b/V2/time_tracker/projects/_application/_projects/_get_projects.py @@ -5,13 +5,12 @@ from ... import _domain from ... import _infrastructure -from time_tracker._infrastructure import DB +from time_tracker._infrastructure import DB as database from time_tracker.utils.enums import ResponseEnums as enums def get_projects(req: func.HttpRequest) -> func.HttpResponse: - database = DB() - project_dao = _infrastructure.ProjectsSQLDao(database) + project_dao = _infrastructure.ProjectsSQLDao(database()) project_service = _domain.ProjectService(project_dao) project_id = req.route_params.get("id") diff --git a/V2/time_tracker/projects/_application/_projects/_update_project.py b/V2/time_tracker/projects/_application/_projects/_update_project.py index ecf064bd..64048140 100644 --- a/V2/time_tracker/projects/_application/_projects/_update_project.py +++ b/V2/time_tracker/projects/_application/_projects/_update_project.py @@ -5,13 +5,12 @@ from ... import _domain from ... import _infrastructure -from time_tracker._infrastructure import DB +from time_tracker._infrastructure import DB as database from time_tracker.utils.enums import ResponseEnums as enums def update_project(req: func.HttpRequest) -> func.HttpResponse: - database = DB() - project_dao = _infrastructure.ProjectsSQLDao(database) + project_dao = _infrastructure.ProjectsSQLDao(database()) project_service = _domain.ProjectService(project_dao) use_case = _domain._use_cases.UpdateProjectUseCase(project_service) @@ -41,6 +40,12 @@ def update_project(req: func.HttpRequest) -> func.HttpResponse: status_code=enums.STATUS_BAD_REQUEST.value, mimetype=enums.MIME_TYPE.value, ) + except Exception as error: + return func.HttpResponse( + body=str(error).encode(), + status_code=enums.STATUS_BAD_REQUEST.value, + mimetype=enums.MIME_TYPE.value, + ) def _validate_project(project_data: dict) -> bool: diff --git a/V2/time_tracker/projects/_infrastructure/_data_persistence/_projects_dao.py b/V2/time_tracker/projects/_infrastructure/_data_persistence/_projects_dao.py index 3f7950ed..2ec61186 100644 --- a/V2/time_tracker/projects/_infrastructure/_data_persistence/_projects_dao.py +++ b/V2/time_tracker/projects/_infrastructure/_data_persistence/_projects_dao.py @@ -65,9 +65,12 @@ def delete(self, id: int) -> domain.Project: return self.get_by_id(id) def update(self, id: int, project_data: dict) -> domain.Project: - query = self.project.update().where(self.project.c.id == id).values(project_data) - self.db.get_session().execute(query) - return self.get_by_id(id) + try: + query = self.project.update().where(self.project.c.id == id).values(project_data) + self.db.get_session().execute(query) + return self.get_by_id(id) + except sq.exc.SQLAlchemyError as error: + raise Exception(error.orig) def __create_project_dto(self, project: dict) -> domain.Project: project = {key: project.get(key) for key in self.project_key} From db9bedc0c25bce47c80c89c9be70562b69fa5e36 Mon Sep 17 00:00:00 2001 From: Jipson Murillo Date: Thu, 25 Nov 2021 15:53:10 -0500 Subject: [PATCH 8/8] refactor: TT-417 add HTTPStatus from http --- .../api/azure/project_azure_endpoints_test.py | 42 +++++++++---------- .../_application/_projects/_create_project.py | 10 ++--- .../_application/_projects/_delete_project.py | 14 +++---- .../_application/_projects/_get_projects.py | 18 ++++---- .../_application/_projects/_update_project.py | 22 +++++----- V2/time_tracker/utils/enums/__init__.py | 2 - V2/time_tracker/utils/enums/response_enums.py | 16 ------- 7 files changed, 53 insertions(+), 71 deletions(-) delete mode 100644 V2/time_tracker/utils/enums/__init__.py delete mode 100644 V2/time_tracker/utils/enums/response_enums.py diff --git a/V2/tests/api/azure/project_azure_endpoints_test.py b/V2/tests/api/azure/project_azure_endpoints_test.py index a0cbc186..790cbfcf 100644 --- a/V2/tests/api/azure/project_azure_endpoints_test.py +++ b/V2/tests/api/azure/project_azure_endpoints_test.py @@ -1,4 +1,5 @@ import json +from http import HTTPStatus import pytest from faker import Faker @@ -7,7 +8,6 @@ from time_tracker.projects._application import _projects as azure_projects from time_tracker.projects import _domain as domain from time_tracker.projects import _infrastructure as infrastructure -from time_tracker.utils.enums import ResponseEnums as enums PROJECT_URL = '/api/projects/' @@ -36,7 +36,7 @@ def test__project_azure_endpoint__returns_all_projects( response = azure_projects._get_projects.get_projects(req) projects_json_data = response.get_body().decode("utf-8") - assert response.status_code == enums.STATUS_OK.value + assert response.status_code == HTTPStatus.OK assert projects_json_data == json.dumps(inserted_projects) @@ -55,7 +55,7 @@ def test__project_azure_endpoint__returns_an_project__when_project_matches_its_i response = azure_projects._get_projects.get_projects(req) activitiy_json_data = response.get_body().decode("utf-8") - assert response.status_code == enums.STATUS_OK.value + assert response.status_code == HTTPStatus.OK assert activitiy_json_data == json.dumps(inserted_project) @@ -70,8 +70,8 @@ def test__projects_azure_endpoint__returns_a_status_code_400__when_project_reciv response = azure_projects._get_projects.get_projects(req) - assert response.status_code == enums.STATUS_BAD_REQUEST.value - assert response.get_body() == enums.INVALID_ID.value.encode() + assert response.status_code == HTTPStatus.BAD_REQUEST + assert response.get_body() == b"Invalid Format ID" def test__project_azure_endpoint__returns_an_project_with_inactive_status__when_a_project_matching_its_id_is_found( @@ -89,7 +89,7 @@ def test__project_azure_endpoint__returns_an_project_with_inactive_status__when_ response = azure_projects._delete_project.delete_project(req) project_json_data = json.loads(response.get_body().decode("utf-8")) - assert response.status_code == enums.STATUS_OK.value + assert response.status_code == HTTPStatus.OK assert project_json_data['status'] == 0 assert project_json_data['deleted'] is True @@ -105,8 +105,8 @@ def test__delete_projects_azure_endpoint__returns_a_status_code_400__when_projec response = azure_projects._delete_project.delete_project(req) - assert response.status_code == enums.STATUS_BAD_REQUEST.value - assert response.get_body() == enums.INVALID_ID.value.encode() + assert response.status_code == HTTPStatus.BAD_REQUEST + assert response.get_body() == b"Invalid Format ID" def test__delete_projects_azure_endpoint__returns_a_status_code_404__when_no_found_a_project_to_delete( @@ -120,8 +120,8 @@ def test__delete_projects_azure_endpoint__returns_a_status_code_404__when_no_fou response = azure_projects._delete_project.delete_project(req) - assert response.status_code == enums.STATUS_NOT_FOUND.value - assert response.get_body() == enums.NOT_FOUND.value.encode() + assert response.status_code == HTTPStatus.NOT_FOUND + assert response.get_body() == b"Not found" def test__update_project_azure_endpoint__returns_an_project__when_found_a_project_to_update( @@ -141,7 +141,7 @@ def test__update_project_azure_endpoint__returns_an_project__when_found_a_projec activitiy_json_data = response.get_body().decode("utf-8") inserted_project.update(project_body) - assert response.status_code == enums.STATUS_OK.value + assert response.status_code == HTTPStatus.OK assert activitiy_json_data == json.dumps(inserted_project) @@ -159,8 +159,8 @@ def test__update_projects_azure_endpoint__returns_a_status_code_404__when_no_fou response = azure_projects._update_project.update_project(req) - assert response.status_code == enums.STATUS_NOT_FOUND.value - assert response.get_body() == enums.NOT_FOUND.value.encode() + assert response.status_code == HTTPStatus.NOT_FOUND + assert response.get_body() == b"Not found" def test__update_projects_azure_endpoint__returns_a_status_code_400__when_recive_an_incorrect_body( @@ -175,8 +175,8 @@ def test__update_projects_azure_endpoint__returns_a_status_code_400__when_recive response = azure_projects._update_project.update_project(req) - assert response.status_code == enums.STATUS_BAD_REQUEST.value - assert response.get_body() == enums.INCORRECT_BODY.value.encode() + assert response.status_code == HTTPStatus.BAD_REQUEST + assert response.get_body() == b"Incorrect body" def test__update_projects_azure_endpoint__returns_a_status_code_400__when_project_recive_invalid_id( @@ -190,8 +190,8 @@ def test__update_projects_azure_endpoint__returns_a_status_code_400__when_projec response = azure_projects._update_project.update_project(req) - assert response.status_code == enums.STATUS_BAD_REQUEST.value - assert response.get_body() == enums.INVALID_ID.value.encode() + assert response.status_code == HTTPStatus.BAD_REQUEST + assert response.get_body() == b"Invalid Format ID" def test__project_azure_endpoint__creates_an_project__when_project_has_all_attributes( @@ -210,7 +210,7 @@ def test__project_azure_endpoint__creates_an_project__when_project_has_all_attri project_json_data = json.loads(response.get_body()) project_body['id'] = project_json_data['id'] - assert response.status_code == enums.STATUS_CREATED.value + assert response.status_code == HTTPStatus.CREATED assert project_json_data == project_body @@ -230,7 +230,7 @@ def test__project_azure_endpoint__returns_a_status_code_400__when_project_does_n response = azure_projects._create_project.create_project(req) - assert response.status_code == enums.STATUS_BAD_REQUEST.value + assert response.status_code == HTTPStatus.BAD_REQUEST assert response.get_body() == json.dumps(['The name key is missing in the input data']).encode() @@ -250,5 +250,5 @@ def test__project_azure_endpoint__returns_a_status_code_500__when_project_recive response = azure_projects._create_project.create_project(req) - assert response.status_code == enums.INTERNAL_SERVER_ERROR.value - assert response.get_body() == enums.NOT_CREATED.value.encode() + assert response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR + assert response.get_body() == b"could not be created" diff --git a/V2/time_tracker/projects/_application/_projects/_create_project.py b/V2/time_tracker/projects/_application/_projects/_create_project.py index 7ed423ce..559ba864 100644 --- a/V2/time_tracker/projects/_application/_projects/_create_project.py +++ b/V2/time_tracker/projects/_application/_projects/_create_project.py @@ -1,13 +1,13 @@ import dataclasses import json import typing +from http import HTTPStatus import azure.functions as func from ... import _domain from ... import _infrastructure from time_tracker._infrastructure import DB as database -from time_tracker.utils.enums import ResponseEnums as enums def create_project(req: func.HttpRequest) -> func.HttpResponse: @@ -20,7 +20,7 @@ def create_project(req: func.HttpRequest) -> func.HttpResponse: validation_errors = _validate_project(project_data) if validation_errors: - status_code = enums.STATUS_BAD_REQUEST.value + status_code = HTTPStatus.BAD_REQUEST response = json.dumps(validation_errors) else: project_to_create = _domain.Project( @@ -37,13 +37,13 @@ def create_project(req: func.HttpRequest) -> func.HttpResponse: created_project = use_case.create_project(project_to_create) status_code, response = [ - enums.INTERNAL_SERVER_ERROR.value, enums.NOT_CREATED.value.encode() - ] if not created_project else [enums.STATUS_CREATED.value, json.dumps(created_project.__dict__)] + HTTPStatus.INTERNAL_SERVER_ERROR, b"could not be created" + ] if not created_project else [HTTPStatus.CREATED, json.dumps(created_project.__dict__)] return func.HttpResponse( body=response, status_code=status_code, - mimetype=enums.MIME_TYPE.value + mimetype="application/json" ) diff --git a/V2/time_tracker/projects/_application/_projects/_delete_project.py b/V2/time_tracker/projects/_application/_projects/_delete_project.py index 9a1275d5..5274b79f 100644 --- a/V2/time_tracker/projects/_application/_projects/_delete_project.py +++ b/V2/time_tracker/projects/_application/_projects/_delete_project.py @@ -1,11 +1,11 @@ import json +from http import HTTPStatus import azure.functions as func from ... import _domain from ... import _infrastructure from time_tracker._infrastructure import DB as database -from time_tracker.utils.enums import ResponseEnums as enums def delete_project(req: func.HttpRequest) -> func.HttpResponse: @@ -18,18 +18,18 @@ def delete_project(req: func.HttpRequest) -> func.HttpResponse: deleted_project = use_case.delete_project(project_id) status_code, response = [ - enums.STATUS_NOT_FOUND.value, enums.NOT_FOUND.value.encode() - ] if not deleted_project else [enums.STATUS_OK.value, json.dumps(deleted_project.__dict__)] + HTTPStatus.NOT_FOUND, b"Not found" + ] if not deleted_project else [HTTPStatus.OK, json.dumps(deleted_project.__dict__)] return func.HttpResponse( body=response, status_code=status_code, - mimetype=enums.MIME_TYPE.value, + mimetype="application/json", ) except ValueError: return func.HttpResponse( - body=enums.INVALID_ID.value.encode(), - status_code=enums.STATUS_BAD_REQUEST.value, - mimetype=enums.MIME_TYPE.value + body=b"Invalid Format ID", + status_code=HTTPStatus.BAD_REQUEST, + mimetype="application/json" ) diff --git a/V2/time_tracker/projects/_application/_projects/_get_projects.py b/V2/time_tracker/projects/_application/_projects/_get_projects.py index 2297609d..c15efa1c 100644 --- a/V2/time_tracker/projects/_application/_projects/_get_projects.py +++ b/V2/time_tracker/projects/_application/_projects/_get_projects.py @@ -1,12 +1,12 @@ import json import typing +from http import HTTPStatus import azure.functions as func from ... import _domain from ... import _infrastructure from time_tracker._infrastructure import DB as database -from time_tracker.utils.enums import ResponseEnums as enums def get_projects(req: func.HttpRequest) -> func.HttpResponse: @@ -20,24 +20,24 @@ def get_projects(req: func.HttpRequest) -> func.HttpResponse: response = _get_by_id(int(project_id), project_service) if not response: return func.HttpResponse( - body=enums.NOT_FOUND.value.encode(), - status_code=enums.STATUS_NOT_FOUND.value, - mimetype=enums.MIME_TYPE.value + body=b"Not found", + status_code=HTTPStatus.NOT_FOUND, + mimetype="application/json" ) else: response = _get_all(project_service) return func.HttpResponse( body=json.dumps(response), - status_code=enums.STATUS_OK.value, - mimetype=enums.MIME_TYPE.value, + status_code=HTTPStatus.OK, + mimetype="application/json", ) except ValueError: return func.HttpResponse( - body=enums.INVALID_ID.value.encode(), - status_code=enums.STATUS_BAD_REQUEST.value, - mimetype=enums.MIME_TYPE.value + body=b"Invalid Format ID", + status_code=HTTPStatus.BAD_REQUEST, + mimetype="application/json" ) diff --git a/V2/time_tracker/projects/_application/_projects/_update_project.py b/V2/time_tracker/projects/_application/_projects/_update_project.py index 64048140..b2cc1e57 100644 --- a/V2/time_tracker/projects/_application/_projects/_update_project.py +++ b/V2/time_tracker/projects/_application/_projects/_update_project.py @@ -1,12 +1,12 @@ import dataclasses import json +from http import HTTPStatus import azure.functions as func from ... import _domain from ... import _infrastructure from time_tracker._infrastructure import DB as database -from time_tracker.utils.enums import ResponseEnums as enums def update_project(req: func.HttpRequest) -> func.HttpResponse: @@ -19,32 +19,32 @@ def update_project(req: func.HttpRequest) -> func.HttpResponse: project_data = req.get_json() if not _validate_project(project_data): - status_code = enums.STATUS_BAD_REQUEST.value - response = enums.INCORRECT_BODY.value.encode() + status_code = HTTPStatus.BAD_REQUEST + response = b"Incorrect body" else: updated_project = use_case.update_project(project_id, project_data) status_code, response = [ - enums.STATUS_NOT_FOUND.value, enums.NOT_FOUND.value.encode() - ] if not updated_project else [enums.STATUS_OK.value, json.dumps(updated_project.__dict__)] + HTTPStatus.NOT_FOUND, b"Not found" + ] if not updated_project else [HTTPStatus.OK, json.dumps(updated_project.__dict__)] return func.HttpResponse( body=response, status_code=status_code, - mimetype=enums.MIME_TYPE.value, + mimetype="application/json", ) except ValueError: return func.HttpResponse( - body=enums.INVALID_ID.value.encode(), - status_code=enums.STATUS_BAD_REQUEST.value, - mimetype=enums.MIME_TYPE.value, + body=b"Invalid Format ID", + status_code=HTTPStatus.BAD_REQUEST, + mimetype="application/json", ) except Exception as error: return func.HttpResponse( body=str(error).encode(), - status_code=enums.STATUS_BAD_REQUEST.value, - mimetype=enums.MIME_TYPE.value, + status_code=HTTPStatus.BAD_REQUEST, + mimetype="application/json", ) diff --git a/V2/time_tracker/utils/enums/__init__.py b/V2/time_tracker/utils/enums/__init__.py deleted file mode 100644 index 317ca876..00000000 --- a/V2/time_tracker/utils/enums/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# flake8: noqa -from .response_enums import ResponseEnums \ No newline at end of file diff --git a/V2/time_tracker/utils/enums/response_enums.py b/V2/time_tracker/utils/enums/response_enums.py deleted file mode 100644 index 781cba86..00000000 --- a/V2/time_tracker/utils/enums/response_enums.py +++ /dev/null @@ -1,16 +0,0 @@ -from enum import Enum - - -class ResponseEnums(Enum): - STATUS_CREATED = 201 - STATUS_OK = 200 - STATUS_BAD_REQUEST = 400 - STATUS_NOT_FOUND = 404 - INTERNAL_SERVER_ERROR = 500 - - INVALID_ID = "Invalid Format ID" - NOT_FOUND = "Not found" - NOT_CREATED = "could not be created" - INCORRECT_BODY = "Incorrect body" - - MIME_TYPE = "application/json"