Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
9711ceb
feat: TT-356 Read activities with an azure endpoint
Oct 11, 2021
193261f
refactor: TT-356 Solving code smells from Sonarcloud
Oct 11, 2021
7a8df35
refactor: TT-356 Solving merge conflicts
Oct 13, 2021
11831bd
refactor: TT-356 change directory from files in source to azure_time_…
Oct 13, 2021
6967d42
test: TT-356 Adding azure endpoint api test
Oct 13, 2021
d9ab6a4
feat: TT-358 Use serverless to create Azure endpoint
Oct 14, 2021
c878eb3
refactor: TT-358 Changing time tracker backend app skeleton
Oct 15, 2021
1c28197
refactor: TT-358 Change name to the domain partitioning
Oct 18, 2021
2eb86cd
refactor: TT-358 Change route of activities data json file for azure …
Oct 18, 2021
8a073c3
refactor: TT-358 Change folder structure according to new app skeleton
Oct 19, 2021
f176589
feat: TT-358 Add Makefile to install time tracker backend
Oct 20, 2021
7325a84
refactor: TT-367 merge branch TT-358
JosueOb Oct 21, 2021
936c71e
refactor: TT-358 Change api test to use create temp activities fixture
Oct 22, 2021
b823e87
refactor: TT-367 solving merge conflicts
JosueOb Oct 25, 2021
f13bc69
feat: TT-367 creation of the functionality to change the status of an…
JosueOb Oct 25, 2021
687387f
test: TT-367 unit test for activity service
JosueOb Oct 26, 2021
54ba9cd
test: TT-367 unit test for delete activity use case
JosueOb Oct 26, 2021
2954640
test: TT-367 integration test for activities json dao
JosueOb Oct 26, 2021
d3629b9
test: TT-367 api test for endpoint to delete an activity
JosueOb Oct 26, 2021
e2284c9
refactor: TT-367 solving merge conflicts
JosueOb Oct 26, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions V2/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,13 @@ functions:
- GET
route: activities/{id:?}
authLevel: anonymous

delete_activity:
handler: time_entries/interface.delete_activity
events:
- http: true
x-azure-settings:
methods:
- DELETE
route: activities/{id}
authLevel: anonymous
32 changes: 27 additions & 5 deletions V2/tests/api/azure/activity_azure_endpoints_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from time_entries._application._activities import _get_activities as activities
from time_entries._application._activities import (
_get_activities,
_delete_activity,
)
import azure.functions as func
import json
import typing
Expand All @@ -8,10 +11,10 @@ def test__activity_azure_endpoint__returns_all_activities(
create_temp_activities,
):
activities_json, tmp_directory = create_temp_activities
activities.JSON_PATH = tmp_directory
_get_activities.JSON_PATH = tmp_directory
req = func.HttpRequest(method='GET', body=None, url='/api/activities')

response = activities.get_activities(req)
response = _get_activities.get_activities(req)
activities_json_data = response.get_body().decode("utf-8")

assert response.status_code == 200
Expand All @@ -22,16 +25,35 @@ def test__activity_azure_endpoint__returns_an_activity__when_activity_matches_it
create_temp_activities,
):
activities_json, tmp_directory = create_temp_activities
activities.JSON_PATH = tmp_directory
_get_activities.JSON_PATH = tmp_directory
req = func.HttpRequest(
method='GET',
body=None,
url='/api/activities/',
route_params={"id": activities_json[0]['id']},
)

response = activities.get_activities(req)
response = _get_activities.get_activities(req)
activitiy_json_data = response.get_body().decode("utf-8")

assert response.status_code == 200
assert activitiy_json_data == json.dumps(activities_json[0])


def test__activity_azure_endpoint__returns_an_activity_with_inactive_status__when_an_activity_matching_its_id_is_found(
create_temp_activities,
):
activities_json, tmp_directory = create_temp_activities
_delete_activity.JSON_PATH = tmp_directory
req = func.HttpRequest(
method='DELETE',
body=None,
url='/api/activities/',
route_params={"id": activities_json[0]['id']},
)

response = _delete_activity.delete_activity(req)
activity_json_data = json.loads(response.get_body().decode("utf-8"))

assert response.status_code == 200
assert activity_json_data['status'] == 'inactive'
36 changes: 35 additions & 1 deletion V2/tests/integration/daos/activities_json_dao_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def test__get_by_id__returns_none__when_no_activity_matches_its_id(

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

assert result == None
assert result is None


def test__get_all__returns_a_list_of_activity_dto_objects__when_one_or_more_activities_are_found(
Expand Down Expand Up @@ -83,3 +83,37 @@ def test_get_all__returns_an_empty_list__when_doesnt_found_any_activities(
result = activities_json_dao.get_all()

assert result == activities


def test_delete__returns_an_activity_with_inactive_status__when_an_activity_matching_its_id_is_found(
create_fake_activities,
):
activities_json_dao = ActivitiesJsonDao(Faker().file_path())
activities = create_fake_activities(
[
{
"name": "test_name",
"description": "test_description",
"tenant_id": "test_tenant_id",
"id": "test_id",
"deleted": "test_deleted",
"status": "test_status",
}
]
)

activity_dto = activities.pop()
result = activities_json_dao.delete(activity_dto.id)

assert result.status == 'inactive'


def test_delete__returns_none__when_no_activity_matching_its_id_is_found(
create_fake_activities,
):
activities_json_dao = ActivitiesJsonDao(Faker().file_path())
create_fake_activities([])

result = activities_json_dao.delete(Faker().uuid4())

assert result is None
15 changes: 15 additions & 0 deletions V2/tests/unit/services/activity_service_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,18 @@ def test__get_by_id__uses_the_activity_dao__to_retrieve_one_activity(mocker):

assert activity_dao.get_by_id.called
assert expected_activity == actual_activity


def test__delete_activity__uses_the_activity_dao__to_change_activity_status(
mocker,
):
expected_activity = mocker.Mock()
activity_dao = mocker.Mock(
delete=mocker.Mock(return_value=expected_activity)
)

activity_service = ActivityService(activity_dao)
deleted_activity = activity_service.delete(Faker().uuid4())

assert activity_dao.delete.called
assert expected_activity == deleted_activity
19 changes: 17 additions & 2 deletions V2/tests/unit/use_cases/activities_use_case_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
fake = Faker()


def test__get_list_activities_function__uses_the_activities_service__to_retrieve_activities(
def test__get_list_activities_function__uses_the_activity_service__to_retrieve_activities(
mocker: MockFixture,
):
expected_activities = mocker.Mock()
Expand All @@ -21,7 +21,7 @@ def test__get_list_activities_function__uses_the_activities_service__to_retrieve
assert expected_activities == actual_activities


def test__get_activity_by_id_function__uses_the_activities_service__to_retrieve_activity(
def test__get_activity_by_id_function__uses_the_activity_service__to_retrieve_activity(
mocker: MockFixture,
):
expected_activity = mocker.Mock()
Expand All @@ -34,3 +34,18 @@ def test__get_activity_by_id_function__uses_the_activities_service__to_retrieve_

assert activity_service.get_by_id.called
assert expected_activity == actual_activity


def test__delete_activity_function__uses_the_activity_service__to_change_activity_status(
mocker: MockFixture,
):
expected_activity = mocker.Mock()
activity_service = mocker.Mock(
delete=mocker.Mock(return_value=expected_activity)
)

activity_use_case = _use_cases.DeleteActivityUseCase(activity_service)
deleted_activity = activity_use_case.delete_activity(fake.uuid4())

assert activity_service.delete.called
assert expected_activity == deleted_activity
1 change: 1 addition & 0 deletions V2/time_entries/_application/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from ._activities import get_activities
from ._activities import delete_activity
1 change: 1 addition & 0 deletions V2/time_entries/_application/_activities/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from ._get_activities import get_activities
from ._delete_activity import delete_activity
36 changes: 36 additions & 0 deletions V2/time_entries/_application/_activities/_delete_activity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from time_entries._infrastructure import ActivitiesJsonDao
from time_entries._domain import ActivityService, _use_cases

import azure.functions as func
import json
import logging

JSON_PATH = (
'time_entries/_infrastructure/_data_persistence/activities_data.json'
)


def delete_activity(req: func.HttpRequest) -> func.HttpResponse:
logging.info(
'Python HTTP trigger function processed a request to delete an activity.'
)
activity_id = req.route_params.get('id')
response = _delete(activity_id)
status_code = 200 if response != b'Not found' else 404

return func.HttpResponse(
body=response, status_code=status_code, mimetype="application/json"
)


def _delete(activity_id: str) -> str:
activity_use_case = _use_cases.DeleteActivityUseCase(
_create_activity_service(JSON_PATH)
)
activity = activity_use_case.delete_activity(activity_id)
return json.dumps(activity.__dict__) if activity else b'Not found'


def _create_activity_service(path: str):
activity_json = ActivitiesJsonDao(path)
return ActivityService(activity_json)
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@ def get_by_id(self, id: str) -> Activity:
@abc.abstractmethod
def get_all(self) -> typing.List[Activity]:
pass

@abc.abstractmethod
def delete(self, id: str) -> Activity:
pass
3 changes: 3 additions & 0 deletions V2/time_entries/_domain/_services/_activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,6 @@ def get_by_id(self, activity_id: str) -> Activity:

def get_all(self) -> typing.List[Activity]:
return self.activities_dao.get_all()

def delete(self, activity_id: str) -> Activity:
return self.activities_dao.delete(activity_id)
1 change: 1 addition & 0 deletions V2/time_entries/_domain/_use_cases/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from ._get_activities_use_case import GetActivitiesUseCase
from ._get_activity_by_id_use_case import GetActivityUseCase
from ._delete_activity_use_case import DeleteActivityUseCase
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from time_entries._domain import ActivityService, Activity


class DeleteActivityUseCase:
def __init__(self, activity_service: ActivityService):
self.activity_service = activity_service

def delete_activity(self, id: str) -> Activity:
return self.activity_service.delete(id)
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,32 @@ def get_all(self) -> typing.List[Activity]:
for activity in self.__get_activities_from_file()
]

def delete(self, activity_id: str) -> Activity:
activity = self.get_by_id(activity_id)
if activity:
activity_deleted = {**activity.__dict__, 'status': 'inactive'}
activities_updated = list(
map(
lambda activity: activity
if activity.get('id') != activity_id
else activity_deleted,
self.__get_activities_from_file(),
)
)

try:
file = open(self.json_data_file_path, 'w')
json.dump(activities_updated, file)
file.close()

return self.__create_activity_dto(activity_deleted)

except FileNotFoundError:
return None

else:
return None

def __get_activities_from_file(self) -> typing.List[dict]:
try:
file = open(self.json_data_file_path)
Expand Down
1 change: 1 addition & 0 deletions V2/time_entries/interface.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from ._application import get_activities
from ._application import delete_activity