Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 12 additions & 2 deletions V2/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ functions:
- http: true
x-azure-settings:
methods:
- PUT
- PUT
route: activities/{id}
authLevel: anonymous
authLevel: anonymous

create_activity:
handler: time_tracker/activities/interface.create_activity
Expand All @@ -87,6 +87,16 @@ functions:
route: time-entries/
authLevel: anonymous

get_time_entries:
handler: time_tracker/time_entries/interface.get_time_entries
events:
- http: true
x-azure-settings:
methods:
- GET
route: time-entries/{id:?}
authLevel: anonymous

delete_time_entry:
handler: time_tracker/time_entries/interface.delete_time_entry
events:
Expand Down
65 changes: 62 additions & 3 deletions V2/tests/api/azure/time_entry_azure_endpoints_test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import pytest
import json
from faker import Faker
from http import HTTPStatus

import azure.functions as func

Expand Down Expand Up @@ -39,7 +40,7 @@ def test__time_entry_azure_endpoint__creates_an_time_entry__when_time_entry_has_
time_entry_json_data = json.loads(response.get_body())
time_entry_body['id'] = time_entry_json_data['id']

assert response.status_code == 201
assert response.status_code == HTTPStatus.CREATED
assert time_entry_json_data == time_entry_body


Expand All @@ -60,7 +61,7 @@ def test__delete_time_entries_azure_endpoint__returns_an_time_entry_with_true_de
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 response.status_code == HTTPStatus.OK
assert time_entry_json_data['deleted'] is True


Expand All @@ -75,7 +76,65 @@ def test__delete_time_entries_azure_endpoint__returns_a_status_code_400__when_ti

response = azure_time_entries._delete_time_entry.delete_time_entry(req)

assert response.status_code == 400
assert response.status_code == HTTPStatus.BAD_REQUEST
assert response.get_body() == b'Invalid Format ID'


def test__time_entry_azure_endpoint__returns_all_time_entries(
test_db, time_entry_factory, insert_time_entry, activity_factory, insert_activity
):
inserted_activity = insert_activity(activity_factory(), test_db)
time_entries_to_insert = time_entry_factory(activity_id=inserted_activity.id)
inserted_time_entries = insert_time_entry(time_entries_to_insert, test_db).__dict__

req = func.HttpRequest(method="GET", body=None, url=TIME_ENTRY_URL)

response = azure_time_entries.get_time_entries(req)
time_entries_json_data = response.get_body().decode("utf-8")
time_entry_list = json.loads(time_entries_json_data)

assert response.status_code == HTTPStatus.OK
assert time_entry_list.pop() == inserted_time_entries


def test__time_entry_azure_endpoint__returns_an_time_entry__when_time_entry_matches_its_id(
test_db, time_entry_factory, insert_time_entry, activity_factory, insert_activity
):
inserted_activity = insert_activity(activity_factory(), test_db)
time_entries_to_insert = time_entry_factory(activity_id=inserted_activity.id)
inserted_time_entries = insert_time_entry(time_entries_to_insert, test_db).__dict__

req = func.HttpRequest(
method="GET",
body=None,
url=TIME_ENTRY_URL,
route_params={"id": inserted_time_entries["id"]},
)

response = azure_time_entries.get_time_entries(req)
time_entry_json_data = response.get_body().decode("utf-8")

assert response.status_code == HTTPStatus.OK
assert time_entry_json_data == json.dumps(inserted_time_entries)


def test__get_time_entries_azure_endpoint__returns_a_status_code_400__when_time_entry_recive_invalid_id(
test_db, time_entry_factory, insert_time_entry, activity_factory, insert_activity
):
inserted_activity = insert_activity(activity_factory(), test_db)
time_entries_to_insert = time_entry_factory(activity_id=inserted_activity.id)
insert_time_entry(time_entries_to_insert, test_db).__dict__

req = func.HttpRequest(
method="GET",
body=None,
url=TIME_ENTRY_URL,
route_params={"id": "invalid id"},
)

response = azure_time_entries.get_time_entries(req)

assert response.status_code == HTTPStatus.BAD_REQUEST
assert response.get_body() == b'Invalid Format ID'


Expand Down
55 changes: 55 additions & 0 deletions V2/tests/integration/daos/time_entries_dao_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import pytest
import typing

from faker import Faker

import time_tracker.time_entries._domain as domain
Expand Down Expand Up @@ -98,3 +100,56 @@ def test_update__returns_none__when_doesnt_found_one_time_entry_to_update(
time_entry = dao.update(0, inserted_time_entries)

assert time_entry is None


def test__get_all__returns_a_list_of_time_entries_dto_objects__when_one_or_more_time_entries_are_found_in_sql_database(
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)
time_entries_to_insert = time_entry_factory(activity_id=inserted_activity.id)
inserted_time_entries = [dao.create(time_entries_to_insert)]

time_entry = dao.get_all()

assert isinstance(time_entry, typing.List)
assert time_entry == inserted_time_entries


def test__get_all__returns_an_empty_list__when_doesnt_found_any_time_entries_in_sql_database(
test_db, create_fake_dao, insert_activity, activity_factory
):
dao = create_fake_dao(test_db)
insert_activity(activity_factory(), dao.db)

time_entry = dao.get_all()
assert time_entry == []


def test__get_by_id__returns_a_time_entry_dto__when_found_one_time_entry_that_match_id_with_sql_database(
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)
time_entries_to_insert = time_entry_factory(activity_id=inserted_activity.id)
inserted_time_entries = dao.create(time_entries_to_insert)

time_entry = dao.get_by_id(time_entries_to_insert.id)

assert isinstance(time_entry, domain.TimeEntry)
assert time_entry.id == inserted_time_entries.id
assert time_entry == inserted_time_entries


def test__get_by_id__returns_none__when_no_time_entry_matches_by_id(
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)
time_entries_to_insert = time_entry_factory(activity_id=inserted_activity.id)
dao.create(time_entries_to_insert)

time_entry = dao.get_by_id(Faker().pyint())

assert time_entry is None
26 changes: 26 additions & 0 deletions V2/tests/unit/services/time_entry_service_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,29 @@ def test__update_time_entry__uses_the_time_entry_dao__to_update_one_time_entry(

assert time_entry_dao.update.called
assert expected_time_entry == updated_time_entry


def test__get_all__uses_the_time_entry_dao__to_retrieve_time_entries(mocker):
expected_time_entries = mocker.Mock()
time_entry_dao = mocker.Mock(
get_all=mocker.Mock(return_value=expected_time_entries)
)
time_activity_service = TimeEntryService(time_entry_dao)

actual_activities = time_activity_service.get_all()

assert time_entry_dao.get_all.called
assert expected_time_entries == actual_activities


def test__get_by_id__uses_the_time_entry_dao__to_retrieve_one_time_entry(mocker):
expected_time_entry = mocker.Mock()
time_entry_dao = mocker.Mock(
get_by_id=mocker.Mock(return_value=expected_time_entry)
)
time_entry_service = TimeEntryService(time_entry_dao)

actual_time_entry = time_entry_service.get_by_id(Faker().uuid4())

assert time_entry_dao.get_by_id.called
assert expected_time_entry == actual_time_entry
32 changes: 32 additions & 0 deletions V2/tests/unit/use_cases/time_entries_use_case_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

from time_tracker.time_entries._domain import _use_cases

fake = Faker()


def test__create_time_entry_function__uses_the_time_entries_service__to_create_time_entry(
mocker: MockFixture, time_entry_factory
Expand Down Expand Up @@ -43,3 +45,33 @@ def test__update_time_entries_function__uses_the_time_entry_service__to_update_a

assert time_entry_service.update.called
assert expected_time_entry == updated_time_entry


def test__get_all_time_entries_function__using_the_use_case_get_time_entries__to_get_all_time_entries(
mocker: MockFixture,
):
expected_time_entries = mocker.Mock()
time_entry_service = mocker.Mock(
get_all=mocker.Mock(return_value=expected_time_entries)
)

time_entries_use_case = _use_cases.GetTimeEntriesUseCase(time_entry_service)
actual_time_entries = time_entries_use_case.get_time_entries()

assert time_entry_service.get_all.called
assert expected_time_entries == actual_time_entries


def test__get_time_entry_by_id_function__uses_the_time_entry_service__to_retrieve_time_entry(
mocker: MockFixture,
):
expected_time_entries = mocker.Mock()
time_entry_service = mocker.Mock(
get_by_id=mocker.Mock(return_value=expected_time_entries)
)

time_entry_use_case = _use_cases.GetTimeEntryUseCase(time_entry_service)
actual_time_entry = time_entry_use_case.get_time_entry_by_id(fake.uuid4())

assert time_entry_service.get_by_id.called
assert expected_time_entries == actual_time_entry
1 change: 1 addition & 0 deletions V2/time_tracker/time_entries/_application/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
from ._time_entries import create_time_entry
from ._time_entries import delete_time_entry
from ._time_entries import update_time_entry
from ._time_entries import get_time_entries
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
from ._create_time_entry import create_time_entry
from ._delete_time_entry import delete_time_entry
from ._update_time_entry import update_time_entry
from ._get_time_entries import get_time_entries
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import json
from http import HTTPStatus

import azure.functions as func

from time_tracker.time_entries._infrastructure import TimeEntriesSQLDao
from time_tracker.time_entries._domain import TimeEntryService, _use_cases
from time_tracker._infrastructure import DB


NOT_FOUND = b'Not Found'
INVALID_FORMAT_ID = b'Invalid Format ID'


def get_time_entries(req: func.HttpRequest) -> func.HttpResponse:

time_entry_id = req.route_params.get('id')
status_code = HTTPStatus.OK

if time_entry_id:
try:
response = _get_by_id(int(time_entry_id))
if response == NOT_FOUND:
status_code = HTTPStatus.NOT_FOUND
except ValueError:
response = INVALID_FORMAT_ID
status_code = HTTPStatus.BAD_REQUEST
else:
response = _get_all()

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


def _get_by_id(id: int) -> str:
database = DB()
time_entry_use_case = _use_cases.GetTimeEntryUseCase(
_create_time_entry_service(database)
)
time_entry = time_entry_use_case.get_time_entry_by_id(id)

return json.dumps(time_entry.__dict__) if time_entry else NOT_FOUND


def _get_all() -> str:
database = DB()
time_entries_use_case = _use_cases.GetTimeEntriesUseCase(
_create_time_entry_service(database)
)
return json.dumps(
[
time_entry.__dict__
for time_entry in time_entries_use_case.get_time_entries()
]
)


def _create_time_entry_service(db: DB):
time_entry_sql = TimeEntriesSQLDao(db)
return TimeEntryService(time_entry_sql)
4 changes: 3 additions & 1 deletion V2/time_tracker/time_entries/_domain/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@
CreateTimeEntryUseCase,
DeleteTimeEntryUseCase,
UpdateTimeEntryUseCase,
)
GetTimeEntriesUseCase,
GetTimeEntryUseCase
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import abc
import typing

from time_tracker.time_entries._domain import TimeEntry

Expand All @@ -15,3 +16,10 @@ def delete(self, id: int) -> TimeEntry:
@abc.abstractmethod
def update(self, id: int, new_time_entry: dict) -> TimeEntry:
pass

def get_by_id(self, id: int) -> TimeEntry:
pass

@abc.abstractmethod
def get_all(self) -> typing.List[TimeEntry]:
pass
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import typing

from time_tracker.time_entries._domain import TimeEntry, TimeEntriesDao


class TimeEntryService:

def __init__(self, time_entry_dao: TimeEntriesDao):
self.time_entry_dao = time_entry_dao

Expand All @@ -14,3 +15,9 @@ def delete(self, id: int) -> TimeEntry:

def update(self, time_entry_id: int, new_time_entry: dict) -> TimeEntry:
return self.time_entry_dao.update(time_entry_id, new_time_entry)

def get_by_id(self, id: int) -> TimeEntry:
return self.time_entry_dao.get_by_id(id)

def get_all(self) -> typing.List[TimeEntry]:
return self.time_entry_dao.get_all()
2 changes: 2 additions & 0 deletions V2/time_tracker/time_entries/_domain/_use_cases/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@
from ._create_time_entry_use_case import CreateTimeEntryUseCase
from ._delete_time_entry_use_case import DeleteTimeEntryUseCase
from ._update_time_entry_use_case import UpdateTimeEntryUseCase
from ._get_time_entry_use_case import GetTimeEntriesUseCase
from ._get_time_entry_by_id_use_case import GetTimeEntryUseCase
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from time_tracker.time_entries._domain import TimeEntryService, TimeEntry


class GetTimeEntryUseCase:
def __init__(self, time_entry_service: TimeEntryService):
self.time_entry_service = time_entry_service

def get_time_entry_by_id(self, id: int) -> TimeEntry:
return self.time_entry_service.get_by_id(id)
Loading