Skip to content

Commit 6ba8320

Browse files
JosueObAndrés Soto
andauthored
feat: TT-367 V2 - Delete Activity (#330)
* feat: TT-356 Read activities with an azure endpoint * refactor: TT-356 Solving code smells from Sonarcloud * refactor: TT-356 change directory from files in source to azure_time_tracker * test: TT-356 Adding azure endpoint api test * feat: TT-358 Use serverless to create Azure endpoint * refactor: TT-358 Changing time tracker backend app skeleton * refactor: TT-358 Change name to the domain partitioning * refactor: TT-358 Change route of activities data json file for azure functions * refactor: TT-358 Change folder structure according to new app skeleton * feat: TT-358 Add Makefile to install time tracker backend * refactor: TT-358 Change api test to use create temp activities fixture * feat: TT-367 creation of the functionality to change the status of an activity * test: TT-367 unit test for activity service * test: TT-367 unit test for delete activity use case * test: TT-367 integration test for activities json dao * test: TT-367 api test for endpoint to delete an activity Co-authored-by: Andrés Soto <[email protected]>
1 parent 464f281 commit 6ba8320

File tree

14 files changed

+186
-8
lines changed

14 files changed

+186
-8
lines changed

V2/serverless.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,3 +46,13 @@ functions:
4646
- GET
4747
route: activities/{id:?}
4848
authLevel: anonymous
49+
50+
delete_activity:
51+
handler: time_entries/interface.delete_activity
52+
events:
53+
- http: true
54+
x-azure-settings:
55+
methods:
56+
- DELETE
57+
route: activities/{id}
58+
authLevel: anonymous
Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
from time_entries._application._activities import _get_activities as activities
1+
from time_entries._application._activities import (
2+
_get_activities,
3+
_delete_activity,
4+
)
25
import azure.functions as func
36
import json
47
import typing
@@ -8,10 +11,10 @@ def test__activity_azure_endpoint__returns_all_activities(
811
create_temp_activities,
912
):
1013
activities_json, tmp_directory = create_temp_activities
11-
activities.JSON_PATH = tmp_directory
14+
_get_activities.JSON_PATH = tmp_directory
1215
req = func.HttpRequest(method='GET', body=None, url='/api/activities')
1316

14-
response = activities.get_activities(req)
17+
response = _get_activities.get_activities(req)
1518
activities_json_data = response.get_body().decode("utf-8")
1619

1720
assert response.status_code == 200
@@ -22,16 +25,35 @@ def test__activity_azure_endpoint__returns_an_activity__when_activity_matches_it
2225
create_temp_activities,
2326
):
2427
activities_json, tmp_directory = create_temp_activities
25-
activities.JSON_PATH = tmp_directory
28+
_get_activities.JSON_PATH = tmp_directory
2629
req = func.HttpRequest(
2730
method='GET',
2831
body=None,
2932
url='/api/activities/',
3033
route_params={"id": activities_json[0]['id']},
3134
)
3235

33-
response = activities.get_activities(req)
36+
response = _get_activities.get_activities(req)
3437
activitiy_json_data = response.get_body().decode("utf-8")
3538

3639
assert response.status_code == 200
3740
assert activitiy_json_data == json.dumps(activities_json[0])
41+
42+
43+
def test__activity_azure_endpoint__returns_an_activity_with_inactive_status__when_an_activity_matching_its_id_is_found(
44+
create_temp_activities,
45+
):
46+
activities_json, tmp_directory = create_temp_activities
47+
_delete_activity.JSON_PATH = tmp_directory
48+
req = func.HttpRequest(
49+
method='DELETE',
50+
body=None,
51+
url='/api/activities/',
52+
route_params={"id": activities_json[0]['id']},
53+
)
54+
55+
response = _delete_activity.delete_activity(req)
56+
activity_json_data = json.loads(response.get_body().decode("utf-8"))
57+
58+
assert response.status_code == 200
59+
assert activity_json_data['status'] == 'inactive'

V2/tests/integration/daos/activities_json_dao_test.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def test__get_by_id__returns_none__when_no_activity_matches_its_id(
4747

4848
result = activities_json_dao.get_by_id(Faker().uuid4())
4949

50-
assert result == None
50+
assert result is None
5151

5252

5353
def test__get_all__returns_a_list_of_activity_dto_objects__when_one_or_more_activities_are_found(
@@ -83,3 +83,37 @@ def test_get_all__returns_an_empty_list__when_doesnt_found_any_activities(
8383
result = activities_json_dao.get_all()
8484

8585
assert result == activities
86+
87+
88+
def test_delete__returns_an_activity_with_inactive_status__when_an_activity_matching_its_id_is_found(
89+
create_fake_activities,
90+
):
91+
activities_json_dao = ActivitiesJsonDao(Faker().file_path())
92+
activities = create_fake_activities(
93+
[
94+
{
95+
"name": "test_name",
96+
"description": "test_description",
97+
"tenant_id": "test_tenant_id",
98+
"id": "test_id",
99+
"deleted": "test_deleted",
100+
"status": "test_status",
101+
}
102+
]
103+
)
104+
105+
activity_dto = activities.pop()
106+
result = activities_json_dao.delete(activity_dto.id)
107+
108+
assert result.status == 'inactive'
109+
110+
111+
def test_delete__returns_none__when_no_activity_matching_its_id_is_found(
112+
create_fake_activities,
113+
):
114+
activities_json_dao = ActivitiesJsonDao(Faker().file_path())
115+
create_fake_activities([])
116+
117+
result = activities_json_dao.delete(Faker().uuid4())
118+
119+
assert result is None

V2/tests/unit/services/activity_service_test.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,18 @@ def test__get_by_id__uses_the_activity_dao__to_retrieve_one_activity(mocker):
2626

2727
assert activity_dao.get_by_id.called
2828
assert expected_activity == actual_activity
29+
30+
31+
def test__delete_activity__uses_the_activity_dao__to_change_activity_status(
32+
mocker,
33+
):
34+
expected_activity = mocker.Mock()
35+
activity_dao = mocker.Mock(
36+
delete=mocker.Mock(return_value=expected_activity)
37+
)
38+
39+
activity_service = ActivityService(activity_dao)
40+
deleted_activity = activity_service.delete(Faker().uuid4())
41+
42+
assert activity_dao.delete.called
43+
assert expected_activity == deleted_activity

V2/tests/unit/use_cases/activities_use_case_test.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
fake = Faker()
77

88

9-
def test__get_list_activities_function__uses_the_activities_service__to_retrieve_activities(
9+
def test__get_list_activities_function__uses_the_activity_service__to_retrieve_activities(
1010
mocker: MockFixture,
1111
):
1212
expected_activities = mocker.Mock()
@@ -21,7 +21,7 @@ def test__get_list_activities_function__uses_the_activities_service__to_retrieve
2121
assert expected_activities == actual_activities
2222

2323

24-
def test__get_activity_by_id_function__uses_the_activities_service__to_retrieve_activity(
24+
def test__get_activity_by_id_function__uses_the_activity_service__to_retrieve_activity(
2525
mocker: MockFixture,
2626
):
2727
expected_activity = mocker.Mock()
@@ -34,3 +34,18 @@ def test__get_activity_by_id_function__uses_the_activities_service__to_retrieve_
3434

3535
assert activity_service.get_by_id.called
3636
assert expected_activity == actual_activity
37+
38+
39+
def test__delete_activity_function__uses_the_activity_service__to_change_activity_status(
40+
mocker: MockFixture,
41+
):
42+
expected_activity = mocker.Mock()
43+
activity_service = mocker.Mock(
44+
delete=mocker.Mock(return_value=expected_activity)
45+
)
46+
47+
activity_use_case = _use_cases.DeleteActivityUseCase(activity_service)
48+
deleted_activity = activity_use_case.delete_activity(fake.uuid4())
49+
50+
assert activity_service.delete.called
51+
assert expected_activity == deleted_activity
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
from ._activities import get_activities
2+
from ._activities import delete_activity
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
from ._get_activities import get_activities
2+
from ._delete_activity import delete_activity
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from time_entries._infrastructure import ActivitiesJsonDao
2+
from time_entries._domain import ActivityService, _use_cases
3+
4+
import azure.functions as func
5+
import json
6+
import logging
7+
8+
JSON_PATH = (
9+
'time_entries/_infrastructure/_data_persistence/activities_data.json'
10+
)
11+
12+
13+
def delete_activity(req: func.HttpRequest) -> func.HttpResponse:
14+
logging.info(
15+
'Python HTTP trigger function processed a request to delete an activity.'
16+
)
17+
activity_id = req.route_params.get('id')
18+
response = _delete(activity_id)
19+
status_code = 200 if response != b'Not found' else 404
20+
21+
return func.HttpResponse(
22+
body=response, status_code=status_code, mimetype="application/json"
23+
)
24+
25+
26+
def _delete(activity_id: str) -> str:
27+
activity_use_case = _use_cases.DeleteActivityUseCase(
28+
_create_activity_service(JSON_PATH)
29+
)
30+
activity = activity_use_case.delete_activity(activity_id)
31+
return json.dumps(activity.__dict__) if activity else b'Not found'
32+
33+
34+
def _create_activity_service(path: str):
35+
activity_json = ActivitiesJsonDao(path)
36+
return ActivityService(activity_json)

V2/time_entries/_domain/_persistence_contracts/_activities_dao.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ def get_by_id(self, id: str) -> Activity:
1111
@abc.abstractmethod
1212
def get_all(self) -> typing.List[Activity]:
1313
pass
14+
15+
@abc.abstractmethod
16+
def delete(self, id: str) -> Activity:
17+
pass

V2/time_entries/_domain/_services/_activity.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@ def get_by_id(self, activity_id: str) -> Activity:
1111

1212
def get_all(self) -> typing.List[Activity]:
1313
return self.activities_dao.get_all()
14+
15+
def delete(self, activity_id: str) -> Activity:
16+
return self.activities_dao.delete(activity_id)

0 commit comments

Comments
 (0)