Skip to content

Commit 56e4ced

Browse files
committed
reactor: TT-407 rebase with master - DELETE
1 parent a6fcb35 commit 56e4ced

File tree

15 files changed

+211
-20
lines changed

15 files changed

+211
-20
lines changed

V2/serverless.yml

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
service: azure-time-tracker
22

3-
frameworkVersion: "2"
3+
frameworkVersion: '2'
44

55
provider:
66
name: azure
@@ -23,18 +23,18 @@ plugins:
2323

2424
package:
2525
patterns:
26-
- "!env/**"
27-
- "!.env/**"
28-
- "!local.settings.json"
29-
- "!.vscode/**"
30-
- "!__pycache__/**"
31-
- "!node_modules/**"
32-
- "!.python_packages/**"
33-
- "!.funcignore"
34-
- "!package.json"
35-
- "!package-lock.json"
36-
- "!.gitignore"
37-
- "!.git/**"
26+
- '!env/**'
27+
- '!.env/**'
28+
- '!local.settings.json'
29+
- '!.vscode/**'
30+
- '!__pycache__/**'
31+
- '!node_modules/**'
32+
- '!.python_packages/**'
33+
- '!.funcignore'
34+
- '!package.json'
35+
- '!package-lock.json'
36+
- '!.gitignore'
37+
- '!.git/**'
3838

3939
functions:
4040
get_activities:
@@ -63,9 +63,9 @@ functions:
6363
- http: true
6464
x-azure-settings:
6565
methods:
66-
- PUT
66+
- PUT
6767
route: activities/{id}
68-
authLevel: anonymous
68+
authLevel: anonymous
6969

7070
create_activity:
7171
handler: time_tracker/activities/interface.create_activity
@@ -96,3 +96,13 @@ functions:
9696
- DELETE
9797
route: time-entries/{id}
9898
authLevel: anonymous
99+
100+
get_latest_time_entry:
101+
handler: time_tracker/time_entries/interface.get_latest_entries
102+
events:
103+
- http: true
104+
x-azure-settings:
105+
methods:
106+
- GET
107+
route: time-entries/latest
108+
authLevel: anonymous

V2/tests/api/azure/time_entry_azure_endpoints_test.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import pytest
22
import json
3+
import pytest
4+
from faker import Faker
35

46
import azure.functions as func
57

@@ -76,3 +78,42 @@ def test__delete_time_entries_azure_endpoint__returns_a_status_code_400__when_ti
7678

7779
assert response.status_code == 400
7880
assert response.get_body() == b'Invalid Format ID'
81+
82+
83+
def test__get_latest_entries_azure_endpoint__returns_a_list_of_latest_time_entries__when_an_owner_id_match(
84+
test_db, time_entry_factory, insert_time_entry, insert_activity, activity_factory,
85+
):
86+
inserted_activity = insert_activity(activity_factory(), test_db).__dict__
87+
time_entry_body = time_entry_factory(activity_id=inserted_activity["id"], technologies="[jira,sql]")
88+
inserted_time_entry = insert_time_entry(time_entry_body, test_db)
89+
90+
req = func.HttpRequest(
91+
method='GET',
92+
body=None,
93+
url=TIME_ENTRY_URL+"latest/",
94+
params={"owner_id": inserted_time_entry["owner_id"]},
95+
)
96+
97+
response = azure_time_entries._get_latest_entries.get_latest_entries(req)
98+
time_entry_json_data = json.loads(response.get_body().decode("utf-8"))
99+
100+
assert response.status_code == 200
101+
assert time_entry_json_data == [inserted_time_entry]
102+
103+
104+
def test__get_latest_entries_azure_endpoint__returns_No_time_entries_found__when_recieve_an_invalid_owner_id(
105+
test_db, insert_activity, activity_factory,
106+
):
107+
insert_activity(activity_factory(), test_db)
108+
109+
req = func.HttpRequest(
110+
method='GET',
111+
body=None,
112+
url=TIME_ENTRY_URL+"latest/",
113+
params={"owner_id": Faker().pyint()},
114+
)
115+
116+
response = azure_time_entries._get_latest_entries.get_latest_entries(req)
117+
118+
assert response.status_code == 404
119+
assert response.get_body() == b'No time entries found'

V2/tests/integration/daos/time_entries_dao_test.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,29 @@ def test_delete__returns_none__when_no_time_entry_matching_its_id_is_found(
6969
result = dao.delete(Faker().pyint())
7070

7171
assert result is None
72+
73+
74+
def test_get_latest_entries__returns_a_list_of_latest_time_entries__when_an_owner_id_match(
75+
create_fake_dao, time_entry_factory, insert_activity, activity_factory, test_db
76+
):
77+
dao = create_fake_dao(test_db)
78+
inserted_activity = insert_activity(activity_factory(), dao.db)
79+
time_entry_to_insert = time_entry_factory(
80+
activity_id=inserted_activity.id,
81+
technologies="[jira,sql]")
82+
inserted_time_entry = dao.create(time_entry_to_insert)
83+
84+
result = dao.get_latest_entries(int(inserted_time_entry.owner_id))
85+
86+
assert result == [inserted_time_entry.__dict__]
87+
88+
89+
def test_get_latest_entries__returns_None__when_an_owner_id_is_not_found(
90+
create_fake_dao, test_db, insert_activity, activity_factory
91+
):
92+
dao = create_fake_dao(test_db)
93+
insert_activity(activity_factory(), dao.db)
94+
95+
result = dao.get_latest_entries(Faker().pyint())
96+
97+
assert result is None

V2/tests/unit/services/time_entry_service_test.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from faker import Faker
22

33
from time_tracker.time_entries._domain import TimeEntryService
4+
from faker import Faker
45

56

67
def test__create_time_entries__uses_the_time_entry_dao__to_create_an_time_entry(mocker, time_entry_factory):
@@ -29,3 +30,18 @@ def test__delete_time_entry__uses_the_time_entry_dao__to_delete_time_entry_selec
2930

3031
assert time_entry_dao.delete.called
3132
assert expected_time_entry == deleted_time_entry
33+
34+
35+
def test__get_latest_entries__uses_the_time_entry_dao__to_get_last_entries(
36+
mocker,
37+
):
38+
expected_latest_time_entries = mocker.Mock()
39+
time_entry_dao = mocker.Mock(
40+
get_latest_entries=mocker.Mock(return_value=expected_latest_time_entries)
41+
)
42+
43+
time_entry_service = TimeEntryService(time_entry_dao)
44+
latest_time_entries = time_entry_service.get_latest_entries(Faker().pyint(), Faker().pyint())
45+
46+
assert expected_latest_time_entries == latest_time_entries
47+
assert time_entry_dao.get_latest_entries.called

V2/tests/unit/use_cases/time_entries_use_case_test.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,16 @@ def test__delete_time_entry_function__uses_the_time_entry_service__to_delete_tim
3030

3131
assert time_entry_service.delete.called
3232
assert expected_time_entry == deleted_time_entry
33+
34+
35+
def test__get_latest_entries_function__uses_the_time_entry_service__to_get_last_entries(
36+
mocker: MockFixture,
37+
):
38+
expected_latest_time_entries = mocker.Mock()
39+
time_entry_service = mocker.Mock(get_latest_entries=mocker.Mock(return_value=expected_latest_time_entries))
40+
41+
time_entry_use_case = _use_cases.GetLastestTimeEntryUseCase(time_entry_service)
42+
latest_time_entries = time_entry_use_case.get_latest_entries(Faker().pyint(), Faker().pyint())
43+
44+
assert time_entry_service.get_latest_entries.called
45+
assert expected_latest_time_entries == latest_time_entries
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# flake8: noqa
2-
from ._time_entries import create_time_entry, delete_time_entry
2+
from ._time_entries import create_time_entry, delete_time_entry, get_latest_entries
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# flake8: noqa
22
from ._create_time_entry import create_time_entry
3-
from ._delete_time_entry import delete_time_entry
3+
from ._delete_time_entry import delete_time_entry
4+
from ._get_latest_entries import get_latest_entries
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import json
2+
3+
import azure.functions as func
4+
5+
from ... import _domain
6+
from ... import _infrastructure
7+
from time_tracker._infrastructure import DB
8+
9+
10+
def get_latest_entries(req: func.HttpRequest) -> func.HttpResponse:
11+
database = DB()
12+
time_entry_dao = _infrastructure.TimeEntriesSQLDao(database)
13+
time_entry_service = _domain.TimeEntryService(time_entry_dao)
14+
use_case = _domain._use_cases.GetLastestTimeEntryUseCase(time_entry_service)
15+
16+
try:
17+
owner_id = req.params.get("owner_id")
18+
limit = req.params.get("limit")
19+
20+
if not owner_id:
21+
return func.HttpResponse(
22+
body="No owner id found",
23+
status_code=404,
24+
mimetype="application/json"
25+
)
26+
27+
time_entries = use_case.get_latest_entries(int(owner_id), int(limit) if limit and int(limit) > 0 else None)
28+
29+
if not time_entries or len(time_entries) == 0:
30+
return func.HttpResponse(
31+
body="No time entries found",
32+
status_code=404,
33+
mimetype="application/json"
34+
)
35+
36+
return func.HttpResponse(
37+
body=json.dumps(time_entries, default=str),
38+
status_code=200,
39+
mimetype="application/json",
40+
)
41+
42+
except ValueError:
43+
return func.HttpResponse(
44+
body=b"Invalid Format ID",
45+
status_code=400,
46+
mimetype="application/json"
47+
)

V2/time_tracker/time_entries/_domain/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
from ._services import TimeEntryService
55
from ._use_cases import (
66
CreateTimeEntryUseCase,
7-
DeleteTimeEntryUseCase
7+
DeleteTimeEntryUseCase,
8+
GetLastestTimeEntryUseCase,
89
)

V2/time_tracker/time_entries/_domain/_persistence_contracts/_time_entries_dao.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import abc
2+
import typing
23

34
from time_tracker.time_entries._domain import TimeEntry
45

@@ -11,3 +12,7 @@ def create(self, time_entry_data: TimeEntry) -> TimeEntry:
1112
@abc.abstractmethod
1213
def delete(self, id: int) -> TimeEntry:
1314
pass
15+
16+
@abc.abstractmethod
17+
def get_latest_entries(self, owner_id: int, limit: int) -> typing.List[TimeEntry]:
18+
pass

0 commit comments

Comments
 (0)