From 60a0dc7015f98b24a3429b1ceabf31e722741649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Rafael=20Arcos=20G=C3=B3mez?= <37599693+ararcos@users.noreply.github.com> Date: Mon, 22 Nov 2021 10:21:34 -0500 Subject: [PATCH 1/8] feat: TT-403 delete v2 time entries (#346) * feat: TT-401 Implemented service, end-point, dao, test- time entries * feat: TT-401 validated request create time entry * fix: TT-401 implemented faker url * feat: TT-403 created end-point to DELETE of time_entries * fix: TT-403 validation of id as integer * fix: TT-403 remove method POST * feat: TT-403 rebase with master * feat: TT-403 tests added * refactor: TT-403 correct flake8 lint syntax * fix: TT-403 comments solved * fix: TT-403 correction of rebase * refactor: TT-403 renamed of delete test Co-authored-by: mandres2015 --- V2/serverless.yml | 10 ++++ .../azure/time_entry_azure_endpoints_test.py | 50 +++++++++++++++++++ V2/tests/fixtures.py | 6 ++- .../integration/daos/time_entries_dao_test.py | 25 +++++++++- .../unit/services/time_entry_service_test.py | 17 +++++++ .../use_cases/time_entries_use_case_test.py | 14 ++++++ .../time_entries/_application/__init__.py | 2 +- .../_application/_time_entries/__init__.py | 3 +- .../_time_entries/_delete_time_entry.py | 36 +++++++++++++ .../time_entries/_domain/__init__.py | 1 + .../_time_entries_dao.py | 4 ++ .../_domain/_services/_time_entry.py | 3 ++ .../_domain/_use_cases/__init__.py | 1 + .../_use_cases/_delete_time_entry_use_case.py | 10 ++++ .../_data_persistence/_time_entries_dao.py | 11 ++++ V2/time_tracker/time_entries/interface.py | 3 +- 16 files changed, 191 insertions(+), 5 deletions(-) create mode 100644 V2/time_tracker/time_entries/_application/_time_entries/_delete_time_entry.py create mode 100644 V2/time_tracker/time_entries/_domain/_use_cases/_delete_time_entry_use_case.py diff --git a/V2/serverless.yml b/V2/serverless.yml index c6c5e34b..fc5942d9 100644 --- a/V2/serverless.yml +++ b/V2/serverless.yml @@ -86,3 +86,13 @@ functions: - POST route: time-entries/ authLevel: anonymous + + delete_time_entry: + handler: time_tracker/time_entries/interface.delete_time_entry + events: + - http: true + x-azure-settings: + methods: + - DELETE + route: time-entries/{id} + authLevel: anonymous 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 f801dad9..8422c4b5 100644 --- a/V2/tests/api/azure/time_entry_azure_endpoints_test.py +++ b/V2/tests/api/azure/time_entry_azure_endpoints_test.py @@ -1,12 +1,26 @@ +import pytest import json import azure.functions as func import time_tracker.time_entries._application._time_entries as azure_time_entries +from time_tracker._infrastructure import DB +from time_tracker.time_entries import _domain as domain_time_entries +from time_tracker.time_entries import _infrastructure as infrastructure_time_entries + TIME_ENTRY_URL = "/api/time-entries/" +@pytest.fixture(name='insert_time_entry') +def _insert_time_entry() -> domain_time_entries.TimeEntry: + def _new_time_entry(time_entry: domain_time_entries.TimeEntry, database: DB): + dao = infrastructure_time_entries.TimeEntriesSQLDao(database) + new_time_entry = dao.create(time_entry) + return new_time_entry + return _new_time_entry + + def test__time_entry_azure_endpoint__creates_an_time_entry__when_time_entry_has_all_attributes( test_db, time_entry_factory, activity_factory, insert_activity ): @@ -26,3 +40,39 @@ def test__time_entry_azure_endpoint__creates_an_time_entry__when_time_entry_has_ assert response.status_code == 201 assert time_entry_json_data == time_entry_body + + +def test__delete_time_entries_azure_endpoint__returns_an_time_entry_with_true_deleted__when_its_id_is_found( + 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]") + inserted_time_entry = insert_time_entry(time_entry_body, test_db) + + req = func.HttpRequest( + method='DELETE', + body=None, + url=TIME_ENTRY_URL, + route_params={"id": inserted_time_entry.id}, + ) + + response = azure_time_entries._delete_time_entry.delete_time_entry(req) + time_entry_json_data = json.loads(response.get_body().decode("utf-8")) + + assert response.status_code == 200 + assert time_entry_json_data['deleted'] is True + + +def test__delete_time_entries_azure_endpoint__returns_a_status_code_400__when_time_entry_recive_invalid_id( +): + req = func.HttpRequest( + method="DELETE", + body=None, + url=TIME_ENTRY_URL, + route_params={"id": "invalid id"}, + ) + + response = azure_time_entries._delete_time_entry.delete_time_entry(req) + + assert response.status_code == 400 + assert response.get_body() == b'Invalid Format ID' diff --git a/V2/tests/fixtures.py b/V2/tests/fixtures.py index 51ee5e5d..8568bdb6 100644 --- a/V2/tests/fixtures.py +++ b/V2/tests/fixtures.py @@ -10,7 +10,10 @@ @pytest.fixture(name='activity_factory') def _activity_factory() -> activities_domain.Activity: def _make_activity( - name: str = Faker().name(), description: str = Faker().sentence(), deleted: bool = False, status: int = 1 + name: str = Faker().name(), + description: str = Faker().sentence(), + deleted: bool = False, + status: int = 1, ): activity = activities_domain.Activity( id=None, @@ -20,6 +23,7 @@ def _make_activity( status=status ) return activity + return _make_activity diff --git a/V2/tests/integration/daos/time_entries_dao_test.py b/V2/tests/integration/daos/time_entries_dao_test.py index 403f80c6..901bce34 100644 --- a/V2/tests/integration/daos/time_entries_dao_test.py +++ b/V2/tests/integration/daos/time_entries_dao_test.py @@ -1,5 +1,5 @@ import pytest - +from faker import Faker import time_tracker.time_entries._domain as domain import time_tracker.time_entries._infrastructure as infrastructure @@ -46,3 +46,26 @@ def test__time_entry__returns_None__when_not_saves_correctly( inserted_time_entry = dao.create(time_entry_to_insert) assert inserted_time_entry is None + + +def test_delete__returns_an_time_entry_with_true_deleted__when_an_time_entry_matching_its_id_is_found( + create_fake_dao, test_db, time_entry_factory, insert_activity, activity_factory +): + 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]") + inserted_time_entry = dao.create(existent_time_entry) + + result = dao.delete(inserted_time_entry.id) + + assert result.deleted is True + + +def test_delete__returns_none__when_no_time_entry_matching_its_id_is_found( + create_fake_dao, test_db +): + dao = create_fake_dao(test_db) + + result = dao.delete(Faker().pyint()) + + assert result 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 bd5ce085..e83b6afb 100644 --- a/V2/tests/unit/services/time_entry_service_test.py +++ b/V2/tests/unit/services/time_entry_service_test.py @@ -1,3 +1,5 @@ +from faker import Faker + from time_tracker.time_entries._domain import TimeEntryService @@ -12,3 +14,18 @@ def test__create_time_entries__uses_the_time_entry_dao__to_create_an_time_entry( assert time_entry_dao.create.called assert expected_time_entry == actual_time_entry + + +def test__delete_time_entry__uses_the_time_entry_dao__to_delete_time_entry_selected( + mocker, +): + expected_time_entry = mocker.Mock() + time_entry_dao = mocker.Mock( + delete=mocker.Mock(return_value=expected_time_entry) + ) + + time_entry_service = TimeEntryService(time_entry_dao) + deleted_time_entry = time_entry_service.delete(Faker().pyint()) + + assert time_entry_dao.delete.called + assert expected_time_entry == deleted_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 d2a31eb7..e0994df4 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 @@ -1,4 +1,5 @@ from pytest_mock import MockFixture +from faker import Faker from time_tracker.time_entries._domain import _use_cases @@ -16,3 +17,16 @@ def test__create_time_entry_function__uses_the_time_entries_service__to_create_t assert time_entry_service.create.called assert expected_time_entry == actual_time_entry + + +def test__delete_time_entry_function__uses_the_time_entry_service__to_delete_time_entry_selected( + mocker: MockFixture, +): + expected_time_entry = mocker.Mock() + time_entry_service = mocker.Mock(delete=mocker.Mock(return_value=expected_time_entry)) + + time_entry_use_case = _use_cases.DeleteTimeEntryUseCase(time_entry_service) + deleted_time_entry = time_entry_use_case.delete_time_entry(Faker().pyint()) + + assert time_entry_service.delete.called + assert expected_time_entry == deleted_time_entry diff --git a/V2/time_tracker/time_entries/_application/__init__.py b/V2/time_tracker/time_entries/_application/__init__.py index 6e4ba9c3..2810c87d 100644 --- a/V2/time_tracker/time_entries/_application/__init__.py +++ b/V2/time_tracker/time_entries/_application/__init__.py @@ -1,2 +1,2 @@ # flake8: noqa -from ._time_entries import create_time_entry \ No newline at end of file +from ._time_entries import create_time_entry, delete_time_entry \ No newline at end of file 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 b46cddce..4cb4d4b0 100644 --- a/V2/time_tracker/time_entries/_application/_time_entries/__init__.py +++ b/V2/time_tracker/time_entries/_application/_time_entries/__init__.py @@ -1,2 +1,3 @@ # flake8: noqa -from ._create_time_entry import create_time_entry \ No newline at end of file +from ._create_time_entry import create_time_entry +from ._delete_time_entry import delete_time_entry \ No newline at end of file diff --git a/V2/time_tracker/time_entries/_application/_time_entries/_delete_time_entry.py b/V2/time_tracker/time_entries/_application/_time_entries/_delete_time_entry.py new file mode 100644 index 00000000..bbf76eab --- /dev/null +++ b/V2/time_tracker/time_entries/_application/_time_entries/_delete_time_entry.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_time_entry(req: func.HttpRequest) -> func.HttpResponse: + time_entry_dao = _infrastructure.TimeEntriesSQLDao(DB()) + time_entry_service = _domain.TimeEntryService(time_entry_dao) + use_case = _domain._use_cases.DeleteTimeEntryUseCase(time_entry_service) + + try: + time_entry_id = int(req.route_params.get("id")) + deleted_time_entry = use_case.delete_time_entry(time_entry_id) + if not deleted_time_entry: + return func.HttpResponse( + body="Not found", + status_code=404, + mimetype="application/json" + ) + + return func.HttpResponse( + body=json.dumps(deleted_time_entry.__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/time_entries/_domain/__init__.py b/V2/time_tracker/time_entries/_domain/__init__.py index a8b2081c..ad927811 100644 --- a/V2/time_tracker/time_entries/_domain/__init__.py +++ b/V2/time_tracker/time_entries/_domain/__init__.py @@ -4,4 +4,5 @@ from ._services import TimeEntryService from ._use_cases import ( CreateTimeEntryUseCase, + DeleteTimeEntryUseCase ) \ No newline at end of file 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 5d04c861..e7d94608 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 @@ -7,3 +7,7 @@ class TimeEntriesDao(abc.ABC): @abc.abstractmethod def create(self, time_entry_data: TimeEntry) -> TimeEntry: pass + + @abc.abstractmethod + def delete(self, id: int) -> TimeEntry: + pass 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 d7aaf3ba..9d47d5e0 100644 --- a/V2/time_tracker/time_entries/_domain/_services/_time_entry.py +++ b/V2/time_tracker/time_entries/_domain/_services/_time_entry.py @@ -8,3 +8,6 @@ def __init__(self, time_entry_dao: TimeEntriesDao): def create(self, time_entry_data: TimeEntry) -> TimeEntry: return self.time_entry_dao.create(time_entry_data) + + def delete(self, id: int) -> TimeEntry: + return self.time_entry_dao.delete(id) 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 41aca738..17b2442a 100644 --- a/V2/time_tracker/time_entries/_domain/_use_cases/__init__.py +++ b/V2/time_tracker/time_entries/_domain/_use_cases/__init__.py @@ -1,2 +1,3 @@ # flake8: noqa from ._create_time_entry_use_case import CreateTimeEntryUseCase +from ._delete_time_entry_use_case import DeleteTimeEntryUseCase diff --git a/V2/time_tracker/time_entries/_domain/_use_cases/_delete_time_entry_use_case.py b/V2/time_tracker/time_entries/_domain/_use_cases/_delete_time_entry_use_case.py new file mode 100644 index 00000000..a195c303 --- /dev/null +++ b/V2/time_tracker/time_entries/_domain/_use_cases/_delete_time_entry_use_case.py @@ -0,0 +1,10 @@ +from time_tracker.time_entries._domain import TimeEntry, TimeEntryService + + +class DeleteTimeEntryUseCase: + + def __init__(self, time_entry_service: TimeEntryService): + self.time_entry_service = time_entry_service + + def delete_time_entry(self, id: int) -> TimeEntry: + return self.time_entry_service.delete(id) 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 d233f3e9..6037af9f 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 @@ -47,3 +47,14 @@ def create(self, time_entry_data: domain.TimeEntry) -> domain.TimeEntry: 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 delete(self, time_entry_id: int) -> domain.TimeEntry: + query = ( + self.time_entry.update() + .where(self.time_entry.c.id == time_entry_id) + .values({"deleted": True}) + ) + self.db.get_session().execute(query) + 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 diff --git a/V2/time_tracker/time_entries/interface.py b/V2/time_tracker/time_entries/interface.py index d0182780..773314bb 100644 --- a/V2/time_tracker/time_entries/interface.py +++ b/V2/time_tracker/time_entries/interface.py @@ -1,2 +1,3 @@ # flake8: noqa -from ._application import create_time_entry \ No newline at end of file +from ._application import create_time_entry +from ._application import delete_time_entry \ No newline at end of file From a6fcb3536a5fa6817a5dc28fab899557e5b9e4d8 Mon Sep 17 00:00:00 2001 From: Cristian Toaquiza Date: Mon, 22 Nov 2021 14:17:50 -0500 Subject: [PATCH 2/8] ci: TT-411 adds pull request (ci) workflow for time-tracker v1 (#356) * ci: [TT-412] adds ci workflow for time-tracker v1 * ci: [TT-412] injects secrets * ci: [TT-412] gets azure vault * ci: [TT-412] logins to azure * ci: [TT-412] adds other attempt to use secrets * ci: [TT-412] adds other attempt to use secrets as env var * ci: [TT-412] injects secrets to run tests * ci: [TT-412] injects USERID to run tests * ci: [TT-412] injects AZURE-APP-CONFIGURATION-CONNECTION-STRING to run tests * ci: [TT-412] injects DATABASE-ACCOUNT-URI to run tests * ci: [TT-412] injects DATABASE-MASTER-KEYO to run tests * ci: [TT-412] injects DATABASE-NAME to run tests * ci: [TT-412] injects AZURE-STORAGE-ACCOUNT-KEY to run tests * ci: [TT-412] adds step to build the app * ci: [TT-412] updates on section values * ci: [TT-412] renames file --- ...me-tracker-v1-on-pull-request-workflow.yml | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 .github/workflows/time-tracker-v1-on-pull-request-workflow.yml diff --git a/.github/workflows/time-tracker-v1-on-pull-request-workflow.yml b/.github/workflows/time-tracker-v1-on-pull-request-workflow.yml new file mode 100644 index 00000000..af4d872a --- /dev/null +++ b/.github/workflows/time-tracker-v1-on-pull-request-workflow.yml @@ -0,0 +1,60 @@ +name: Time Tacker V1 CI + +on: + pull_request: + branches: [master] + +jobs: + time-tracker-ci: + runs-on: ubuntu-latest + + strategy: + matrix: + python-version: [3.9] + + steps: + - name: Checking out code from the repository + uses: actions/checkout@v2 + + - name: Setting up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements/time_tracker_api/dev.txt + pip install -r requirements/time_tracker_events/dev.txt + + - name: Login to azure + uses: Azure/login@v1 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Get vault from azure + uses: Azure/get-keyvault-secrets@v1 + with: + keyvault: "time-tracker-secrets" + secrets: "MS-CLIENT-ID, MS-AUTHORITY, MS-SCOPE, MS-SECRET, MS-ENDPOINT, USERID, AZURE-APP-CONFIGURATION-CONNECTION-STRING, DATABASE-ACCOUNT-URI, DATABASE-MASTER-KEY, DATABASE-NAME, AZURE-STORAGE-ACCOUNT-KEY" + id: timeTrackerAzureVault + + - name: Run tests + env: + MS_AUTHORITY: ${{ steps.timeTrackerAzureVault.outputs.MS-AUTHORITY }} + MS_CLIENT_ID: ${{ steps.timeTrackerAzureVault.outputs.MS-CLIENT-ID }} + MS_SCOPE: ${{ steps.timeTrackerAzureVault.outputs.MS-SCOPE }} + MS_SECRET: ${{ steps.timeTrackerAzureVault.outputs.MS-SECRET }} + MS_ENDPOINT: ${{ steps.timeTrackerAzureVault.outputs.MS-ENDPOINT }} + USERID: ${{ steps.timeTrackerAzureVault.outputs.USERID }} + AZURE_APP_CONFIGURATION_CONNECTION_STRING: ${{ steps.timeTrackerAzureVault.outputs.AZURE-APP-CONFIGURATION-CONNECTION-STRING }} + DATABASE_ACCOUNT_URI: ${{ steps.timeTrackerAzureVault.outputs.DATABASE-ACCOUNT-URI }} + DATABASE_MASTER_KEY: ${{ steps.timeTrackerAzureVault.outputs.DATABASE-MASTER-KEY }} + DATABASE_NAME: ${{ steps.timeTrackerAzureVault.outputs.DATABASE-NAME }} + AZURE_STORAGE_ACCOUNT_KEY: ${{ steps.timeTrackerAzureVault.outputs.AZURE-STORAGE-ACCOUNT-KEY }} + run: | + pytest tests + + - name: Test the build of the app + run: | + docker build . From 08e9ff50850d5b64f02be4f62ccfa4135d399cf7 Mon Sep 17 00:00:00 2001 From: Jobzi Date: Wed, 17 Nov 2021 10:54:02 -0500 Subject: [PATCH 3/8] feat: TT-402 add put time entries --- V2/serverless.yml | 12 ++++- .../azure/time_entry_azure_endpoints_test.py | 42 ++++++++++++++++ .../daos/time_entries_sql_dao_test.py | 41 ++++++++++++++++ .../unit/services/time_entry_service_test.py | 17 +++++++ .../use_cases/time_entries_use_case_test.py | 14 ++++++ V2/time_tracker/_infrastructure/_db.py | 4 ++ .../time_entries/_application/__init__.py | 3 +- .../_application/_time_entries/__init__.py | 3 +- .../_time_entries/_update_time_entry.py | 48 +++++++++++++++++++ .../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 | 19 ++++++-- V2/time_tracker/time_entries/interface.py | 3 +- 19 files changed, 223 insertions(+), 11 deletions(-) create mode 100644 V2/tests/integration/daos/time_entries_sql_dao_test.py 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 fc5942d9..df6272f1 100644 --- a/V2/serverless.yml +++ b/V2/serverless.yml @@ -89,10 +89,20 @@ functions: delete_time_entry: handler: time_tracker/time_entries/interface.delete_time_entry - events: + events: - http: true x-azure-settings: methods: - DELETE 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 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..4ba07730 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,7 @@ import pytest import json +import pytest +from faker import Faker import azure.functions as func @@ -76,3 +78,43 @@ 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_activity_azure_endpoint__returns_an_activity__when_found_an_activity_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, technologies="[jira,sql]") + 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_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": "invalid id"}, + ) + + response = azure_time_entries._update_time_entry.update_time_entry(req) + + assert response.status_code == 400 + assert response.get_body() == b'Invalid ID' diff --git a/V2/tests/integration/daos/time_entries_sql_dao_test.py b/V2/tests/integration/daos/time_entries_sql_dao_test.py new file mode 100644 index 00000000..0db29465 --- /dev/null +++ b/V2/tests/integration/daos/time_entries_sql_dao_test.py @@ -0,0 +1,41 @@ +import pytest + +from time_tracker.time_entries._infrastructure import TimeEntriesSQLDao + +from time_tracker._infrastructure import DB + + +@pytest.fixture(name='create_time_entry_fake_dao') +def _create_fake_dao() -> TimeEntriesSQLDao: + db_fake = DB('sqlite:///:memory:') + dao = TimeEntriesSQLDao(db_fake) + return dao + + +def test_update__returns_an_time_entry_dto__when_found_one_time_entry_to_update( + create_time_entry_fake_dao, time_entry_factory, insert_activity, activity_factory +): + dao = create_time_entry_fake_dao + inserted_activity = insert_activity(activity_factory(), dao.db) + existent_time_entries = time_entry_factory(activity_id=inserted_activity.id, technologies="[jira,sql]") + 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 == "description updated" + + +def test_update__returns_none__when_doesnt_found_one_time_entry_to_update( + create_time_entry_fake_dao, time_entry_factory, insert_activity, activity_factory +): + dao = create_time_entry_fake_dao + inserted_activity = insert_activity(activity_factory(), dao.db) + existent_time_entries = time_entry_factory(activity_id=inserted_activity.id, technologies="[jira,sql]") + 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..e505fe7e 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 @@ -1,3 +1,4 @@ +from faker import Faker from pytest_mock import MockFixture from faker import Faker @@ -30,3 +31,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/_infrastructure/_db.py b/V2/time_tracker/_infrastructure/_db.py index 6f3a9f9a..338c6b00 100644 --- a/V2/time_tracker/_infrastructure/_db.py +++ b/V2/time_tracker/_infrastructure/_db.py @@ -2,6 +2,8 @@ from . import _config +_TEST_DIALECT = "sqlite" + class DB(): config = _config.load_config() @@ -17,4 +19,6 @@ def get_session(self): self.metadata.create_all(self.engine) if self.connection is None: self.connection = self.engine.connect() + if self.engine.dialect.name == _TEST_DIALECT: + self.connection.execute("pragma foreign_keys=ON") return self.connection diff --git a/V2/time_tracker/time_entries/_application/__init__.py b/V2/time_tracker/time_entries/_application/__init__.py index 2810c87d..cedea945 100644 --- a/V2/time_tracker/time_entries/_application/__init__.py +++ b/V2/time_tracker/time_entries/_application/__init__.py @@ -1,2 +1,3 @@ # 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, 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..e9d01b6d --- /dev/null +++ b/V2/time_tracker/time_entries/_application/_time_entries/_update_time_entry.py @@ -0,0 +1,48 @@ +import dataclasses +import json +import logging + +import azure.functions as func + +from time_tracker.time_entries._infrastructure import TimeEntriesSQLDao +from time_tracker.time_entries._domain import TimeEntryService, TimeEntry, _use_cases +from time_tracker._infrastructure import DB + + + +def update_time_entry(req: func.HttpRequest) -> func.HttpResponse: + logging.info( + 'Python HTTP trigger function processed a request to update an time entry.' + ) + time_entry_id = req.route_params.get('id') + time_entry_data = req.get_json() if req.get_body() else {} + time_entry_keys = [field.name for field in dataclasses.fields(TimeEntry)] + + if all(key in time_entry_keys for key in time_entry_data.keys()): + try: + response = _update(int(time_entry_id), time_entry_data) + status_code = 200 + except ValueError: + response = b'Invalid ID' + status_code = 400 + else: + response = b'Incorrect time entry body' + status_code = 400 + + return func.HttpResponse( + body=response, status_code=status_code, mimetype="application/json" + ) + + +def _update(time_entry_id: int, time_entry_data: dict) -> str: + database = DB() + time_entry_use_case = _use_cases.UpdateTimeEntryUseCase( + _create_time_entry_service(database) + ) + time_entry = time_entry_use_case.update_time_entry(time_entry_id, time_entry_data) + return json.dumps(time_entry.__dict__) if time_entry else b'Not Found' + + +def _create_time_entry_service(db: DB): + time_entry_sql = TimeEntriesSQLDao(db) + return TimeEntryService(time_entry_sql) diff --git a/V2/time_tracker/time_entries/_domain/__init__.py b/V2/time_tracker/time_entries/_domain/__init__.py index ad927811..25025657 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..834b48ff 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,14 @@ 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: + + 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_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 delete(self, time_entry_id: int) -> domain.TimeEntry: query = ( @@ -58,3 +63,11 @@ 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 47b3b654d1f9deb614f146863a70972bc2488f6e Mon Sep 17 00:00:00 2001 From: Jipson Murillo Date: Fri, 19 Nov 2021 12:27:01 -0500 Subject: [PATCH 4/8] refactor: TT-402 rebase post time entry --- .../_application/_time_entries/_update_time_entry.py | 1 - 1 file changed, 1 deletion(-) 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 index e9d01b6d..34ffddf3 100644 --- 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 @@ -9,7 +9,6 @@ from time_tracker._infrastructure import DB - def update_time_entry(req: func.HttpRequest) -> func.HttpResponse: logging.info( 'Python HTTP trigger function processed a request to update an time entry.' From 5bc306b549f836b4b5fa58fd02cb64155e07467b Mon Sep 17 00:00:00 2001 From: Jipson Murillo Date: Fri, 19 Nov 2021 14:27:37 -0500 Subject: [PATCH 5/8] test: TT-402 add integration test of UPDATE --- .../integration/daos/time_entries_dao_test.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/V2/tests/integration/daos/time_entries_dao_test.py b/V2/tests/integration/daos/time_entries_dao_test.py index 901bce34..995d4c6d 100644 --- a/V2/tests/integration/daos/time_entries_dao_test.py +++ b/V2/tests/integration/daos/time_entries_dao_test.py @@ -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, technologies="[jira,sql]") + 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 == "description updated" + + +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, technologies="[jira,sql]") + inserted_time_entries = dao.create(existent_time_entries).__dict__ + + time_entry = dao.update(0, inserted_time_entries) + + assert time_entry is None From 94eca997c175f6fa638f06d766f3dc1abd2d7544 Mon Sep 17 00:00:00 2001 From: Jipson Murillo Date: Fri, 19 Nov 2021 14:32:35 -0500 Subject: [PATCH 6/8] refactor: TT-402 delete time_entires_sql_dao_test --- .../daos/time_entries_sql_dao_test.py | 41 ------------------- 1 file changed, 41 deletions(-) delete mode 100644 V2/tests/integration/daos/time_entries_sql_dao_test.py diff --git a/V2/tests/integration/daos/time_entries_sql_dao_test.py b/V2/tests/integration/daos/time_entries_sql_dao_test.py deleted file mode 100644 index 0db29465..00000000 --- a/V2/tests/integration/daos/time_entries_sql_dao_test.py +++ /dev/null @@ -1,41 +0,0 @@ -import pytest - -from time_tracker.time_entries._infrastructure import TimeEntriesSQLDao - -from time_tracker._infrastructure import DB - - -@pytest.fixture(name='create_time_entry_fake_dao') -def _create_fake_dao() -> TimeEntriesSQLDao: - db_fake = DB('sqlite:///:memory:') - dao = TimeEntriesSQLDao(db_fake) - return dao - - -def test_update__returns_an_time_entry_dto__when_found_one_time_entry_to_update( - create_time_entry_fake_dao, time_entry_factory, insert_activity, activity_factory -): - dao = create_time_entry_fake_dao - inserted_activity = insert_activity(activity_factory(), dao.db) - existent_time_entries = time_entry_factory(activity_id=inserted_activity.id, technologies="[jira,sql]") - 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 == "description updated" - - -def test_update__returns_none__when_doesnt_found_one_time_entry_to_update( - create_time_entry_fake_dao, time_entry_factory, insert_activity, activity_factory -): - dao = create_time_entry_fake_dao - inserted_activity = insert_activity(activity_factory(), dao.db) - existent_time_entries = time_entry_factory(activity_id=inserted_activity.id, technologies="[jira,sql]") - inserted_time_entries = dao.create(existent_time_entries).__dict__ - - time_entry = dao.update(0, inserted_time_entries) - - assert time_entry is None From c09437b7c7f7f6a0b529c1f7473f2eec2f81eb54 Mon Sep 17 00:00:00 2001 From: Jipson Murillo Date: Fri, 19 Nov 2021 14:55:37 -0500 Subject: [PATCH 7/8] refactor: TT-404 revert changes _db.py --- V2/time_tracker/_infrastructure/_db.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/V2/time_tracker/_infrastructure/_db.py b/V2/time_tracker/_infrastructure/_db.py index 338c6b00..6f3a9f9a 100644 --- a/V2/time_tracker/_infrastructure/_db.py +++ b/V2/time_tracker/_infrastructure/_db.py @@ -2,8 +2,6 @@ from . import _config -_TEST_DIALECT = "sqlite" - class DB(): config = _config.load_config() @@ -19,6 +17,4 @@ def get_session(self): self.metadata.create_all(self.engine) if self.connection is None: self.connection = self.engine.connect() - if self.engine.dialect.name == _TEST_DIALECT: - self.connection.execute("pragma foreign_keys=ON") return self.connection From 9b0be664c69b9efdc90386915ff127254f8f5e6f Mon Sep 17 00:00:00 2001 From: Jipson Murillo Date: Mon, 22 Nov 2021 15:57:48 -0500 Subject: [PATCH 8/8] fix: TT-402 solve conflict merge --- V2/serverless.yml | 2 +- V2/tests/api/azure/time_entry_azure_endpoints_test.py | 1 - V2/tests/unit/use_cases/time_entries_use_case_test.py | 1 - .../_infrastructure/_data_persistence/_time_entries_dao.py | 1 - 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/V2/serverless.yml b/V2/serverless.yml index df6272f1..12224f2b 100644 --- a/V2/serverless.yml +++ b/V2/serverless.yml @@ -89,7 +89,7 @@ functions: delete_time_entry: handler: time_tracker/time_entries/interface.delete_time_entry - events: + events: - http: true x-azure-settings: methods: 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 4ba07730..85bb6f71 100644 --- a/V2/tests/api/azure/time_entry_azure_endpoints_test.py +++ b/V2/tests/api/azure/time_entry_azure_endpoints_test.py @@ -1,6 +1,5 @@ import pytest import json -import pytest from faker import Faker import azure.functions as func 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 e505fe7e..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 @@ -1,4 +1,3 @@ -from faker import Faker from pytest_mock import MockFixture from faker import Faker 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 834b48ff..03e2c566 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 @@ -64,7 +64,6 @@ def delete(self, time_entry_id: int) -> domain.TimeEntry: 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")),