Skip to content

Commit 68e934f

Browse files
committed
Merge remote-tracking branch 'origin/master' into TT-418-CRUD-CUSTOMER-V2
2 parents dbada7a + f9e1403 commit 68e934f

File tree

15 files changed

+207
-15
lines changed

15 files changed

+207
-15
lines changed

V2/serverless.yml

Lines changed: 24 additions & 13 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
#region start Functions
4040

@@ -126,6 +126,16 @@ functions:
126126
route: time-entries/{id}
127127
authLevel: anonymous
128128

129+
get_latest_time_entry:
130+
handler: time_tracker/time_entries/interface.get_latest_entries
131+
events:
132+
- http: true
133+
x-azure-settings:
134+
methods:
135+
- GET
136+
route: time-entries/latest/
137+
authLevel: anonymous
138+
129139
#endregion End Functions Time-Entries
130140

131141
#region Start Functions Customers
@@ -212,6 +222,7 @@ functions:
212222
methods:
213223
- POST
214224
route: projects/
225+
215226
authLevel: anonymous
216227

217228
#endregion End Functions Projects

V2/tests/api/azure/time_entry_azure_endpoints_test.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from time_tracker._infrastructure import DB
1010
from time_tracker.time_entries import _domain as domain_time_entries
1111
from time_tracker.time_entries import _infrastructure as infrastructure_time_entries
12+
from time_tracker.utils.enums import ResponseEnums
1213

1314

1415
TIME_ENTRY_URL = "/api/time-entries/"
@@ -208,3 +209,42 @@ def test__update_time_entries_azure_endpoint__returns_a_status_code_400__when_ti
208209

209210
assert response.status_code == 400
210211
assert response.get_body() == b'Incorrect time entry body'
212+
213+
214+
def test__get_latest_entries_azure_endpoint__returns_a_list_of_latest_time_entries__when_an_owner_id_match(
215+
test_db, time_entry_factory, insert_time_entry, insert_activity, activity_factory,
216+
):
217+
inserted_activity = insert_activity(activity_factory(), test_db).__dict__
218+
time_entry_body = time_entry_factory(activity_id=inserted_activity["id"], technologies="[jira,sql]")
219+
inserted_time_entry = insert_time_entry(time_entry_body, test_db).__dict__
220+
221+
req = func.HttpRequest(
222+
method='GET',
223+
body=None,
224+
url=TIME_ENTRY_URL+"latest/",
225+
params={"owner_id": inserted_time_entry["owner_id"]},
226+
)
227+
228+
response = azure_time_entries._get_latest_entries.get_latest_entries(req)
229+
time_entry_json_data = json.loads(response.get_body().decode("utf-8"))
230+
231+
assert response.status_code == HTTPStatus.OK
232+
assert time_entry_json_data == [inserted_time_entry]
233+
234+
235+
def test__get_latest_entries_azure_endpoint__returns_not_found__when_recieve_an_invalid_owner_id(
236+
test_db, insert_activity, activity_factory,
237+
):
238+
insert_activity(activity_factory(), test_db)
239+
240+
req = func.HttpRequest(
241+
method='GET',
242+
body=None,
243+
url=TIME_ENTRY_URL+"latest/",
244+
params={"owner_id": Faker().pyint()},
245+
)
246+
247+
response = azure_time_entries._get_latest_entries.get_latest_entries(req)
248+
249+
assert response.status_code == HTTPStatus.NOT_FOUND
250+
assert response.get_body().decode("utf-8") == ResponseEnums.NOT_FOUND.value

V2/tests/integration/daos/time_entries_dao_test.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ def test_update__returns_an_time_entry_dto__when_found_one_time_entry_to_update(
9090

9191

9292
def test_update__returns_none__when_doesnt_found_one_time_entry_to_update(
93-
test_db, create_fake_dao, time_entry_factory, insert_activity, activity_factory
93+
test_db, create_fake_dao, time_entry_factory, insert_activity, activity_factory
9494
):
9595
dao = create_fake_dao(test_db)
9696
inserted_activity = insert_activity(activity_factory(), dao.db)
@@ -153,3 +153,29 @@ def test__get_by_id__returns_none__when_no_time_entry_matches_by_id(
153153
time_entry = dao.get_by_id(Faker().pyint())
154154

155155
assert time_entry is None
156+
157+
158+
def test_get_latest_entries__returns_a_list_of_latest_time_entries__when_an_owner_id_match(
159+
create_fake_dao, time_entry_factory, insert_activity, activity_factory, test_db
160+
):
161+
dao = create_fake_dao(test_db)
162+
inserted_activity = insert_activity(activity_factory(), dao.db)
163+
time_entry_to_insert = time_entry_factory(
164+
activity_id=inserted_activity.id,
165+
technologies="[jira,sql]")
166+
inserted_time_entry = dao.create(time_entry_to_insert)
167+
168+
result = dao.get_latest_entries(int(inserted_time_entry.owner_id))
169+
170+
assert result == [inserted_time_entry.__dict__]
171+
172+
173+
def test_get_latest_entries__returns_none__when_an_owner_id_is_not_found(
174+
create_fake_dao, test_db, insert_activity, activity_factory
175+
):
176+
dao = create_fake_dao(test_db)
177+
insert_activity(activity_factory(), dao.db)
178+
179+
result = dao.get_latest_entries(Faker().pyint())
180+
181+
assert result is None

V2/tests/unit/services/time_entry_service_test.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,18 @@ def test__get_by_id__uses_the_time_entry_dao__to_retrieve_one_time_entry(mocker)
7272

7373
assert time_entry_dao.get_by_id.called
7474
assert expected_time_entry == actual_time_entry
75+
76+
77+
def test__get_latest_entries__uses_the_time_entry_dao__to_get_last_entries(
78+
mocker,
79+
):
80+
expected_latest_time_entries = mocker.Mock()
81+
time_entry_dao = mocker.Mock(
82+
get_latest_entries=mocker.Mock(return_value=expected_latest_time_entries)
83+
)
84+
85+
time_entry_service = TimeEntryService(time_entry_dao)
86+
latest_time_entries = time_entry_service.get_latest_entries(Faker().pyint(), Faker().pyint())
87+
88+
assert expected_latest_time_entries == latest_time_entries
89+
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
@@ -75,3 +75,16 @@ def test__get_time_entry_by_id_function__uses_the_time_entry_service__to_retriev
7575

7676
assert time_entry_service.get_by_id.called
7777
assert expected_time_entries == actual_time_entry
78+
79+
80+
def test__get_latest_entries_function__uses_the_time_entry_service__to_get_last_entries(
81+
mocker: MockFixture,
82+
):
83+
expected_latest_time_entries = mocker.Mock()
84+
time_entry_service = mocker.Mock(get_latest_entries=mocker.Mock(return_value=expected_latest_time_entries))
85+
86+
time_entry_use_case = _use_cases.GetLastestTimeEntryUseCase(time_entry_service)
87+
latest_time_entries = time_entry_use_case.get_latest_entries(Faker().pyint(), Faker().pyint())
88+
89+
assert time_entry_service.get_latest_entries.called
90+
assert expected_latest_time_entries == latest_time_entries

V2/time_tracker/time_entries/_application/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
from ._time_entries import delete_time_entry
44
from ._time_entries import update_time_entry
55
from ._time_entries import get_time_entries
6+
from ._time_entries import get_latest_entries

V2/time_tracker/time_entries/_application/_time_entries/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
from ._delete_time_entry import delete_time_entry
44
from ._update_time_entry import update_time_entry
55
from ._get_time_entries import get_time_entries
6+
from ._get_latest_entries import get_latest_entries
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import json
2+
from http import HTTPStatus
3+
4+
import azure.functions as func
5+
6+
from ... import _domain
7+
from ... import _infrastructure
8+
from time_tracker._infrastructure import DB
9+
from time_tracker.utils.enums import ResponseEnums
10+
11+
12+
def get_latest_entries(req: func.HttpRequest) -> func.HttpResponse:
13+
database = DB()
14+
time_entry_dao = _infrastructure.TimeEntriesSQLDao(database)
15+
time_entry_service = _domain.TimeEntryService(time_entry_dao)
16+
use_case = _domain._use_cases.GetLastestTimeEntryUseCase(time_entry_service)
17+
18+
try:
19+
owner_id = req.params.get("owner_id")
20+
limit = req.params.get("limit")
21+
22+
if not owner_id:
23+
return func.HttpResponse(
24+
body=ResponseEnums.NOT_FOUND.value,
25+
status_code=HTTPStatus.NOT_FOUND,
26+
mimetype=ResponseEnums.MIME_TYPE.value,
27+
)
28+
29+
time_entries = use_case.get_latest_entries(int(owner_id), int(limit) if limit and int(limit) > 0 else None)
30+
31+
if not time_entries or len(time_entries) == 0:
32+
return func.HttpResponse(
33+
body=ResponseEnums.NOT_FOUND.value,
34+
status_code=HTTPStatus.NOT_FOUND,
35+
mimetype=ResponseEnums.MIME_TYPE.value,
36+
)
37+
38+
return func.HttpResponse(
39+
body=json.dumps(time_entries, default=str),
40+
status_code=HTTPStatus.OK,
41+
mimetype=ResponseEnums.MIME_TYPE.value,
42+
)
43+
44+
except ValueError:
45+
return func.HttpResponse(
46+
body=ResponseEnums.INVALID_ID.value,
47+
status_code=HTTPStatus.BAD_REQUEST,
48+
mimetype=ResponseEnums.MIME_TYPE.value,
49+
)

V2/time_tracker/time_entries/_domain/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,6 @@
77
DeleteTimeEntryUseCase,
88
UpdateTimeEntryUseCase,
99
GetTimeEntriesUseCase,
10-
GetTimeEntryUseCase
10+
GetTimeEntryUseCase,
11+
GetLastestTimeEntryUseCase,
1112
)

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
@@ -17,9 +17,14 @@ def delete(self, id: int) -> TimeEntry:
1717
def update(self, id: int, new_time_entry: dict) -> TimeEntry:
1818
pass
1919

20+
@abc.abstractmethod
2021
def get_by_id(self, id: int) -> TimeEntry:
2122
pass
2223

2324
@abc.abstractmethod
2425
def get_all(self) -> typing.List[TimeEntry]:
2526
pass
27+
28+
@abc.abstractmethod
29+
def get_latest_entries(self, owner_id: int, limit: int) -> typing.List[TimeEntry]:
30+
pass

0 commit comments

Comments
 (0)