Skip to content

Commit 602726e

Browse files
ararcosmandres2015
authored andcommitted
feat: TT-401 validated request create time entry
1 parent a3de1f8 commit 602726e

File tree

23 files changed

+249
-192
lines changed

23 files changed

+249
-192
lines changed

V2/tests/api/azure/time_entry_azure_endpoints_test.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@ def test__time_entry_azure_endpoint__creates_an_time_entry__when_time_entry_has_
1515
time_entries._create_time_entry._JSON_PATH = tmp_directory
1616

1717
time_entry_body = {
18-
"id" : None,
19-
"start_date" : Faker().date(),
20-
"owner_id" : Faker().random_int(),
18+
"id": None,
19+
"start_date": Faker().date(),
20+
"owner_id": Faker().random_int(),
2121
"description": Faker().sentence(),
22-
"activity_id" : Faker().random_int(),
22+
"activity_id": Faker().random_int(),
2323
"uri": "http://timetracker.com",
24-
"technologies" : ["jira","git"],
24+
"technologies": ["jira", "git"],
2525
"end_date": Faker().date(),
26-
"deleted": Faker().random_int(),
26+
"deleted": False,
2727
"timezone_offset": "300",
2828
"project_id": Faker().random_int(),
2929
}

V2/tests/conftest.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
11
# flake8: noqa
2-
from fixtures import _activity_factory, _create_fake_dao, _create_fake_database
2+
<<<<<<< HEAD
3+
from fixtures import _activity_factory, _create_fake_dao, _create_fake_database
4+
=======
5+
from fixtures import create_temp_activities
6+
from fixtures import create_temp_time_entries
7+
from fixtures import _time_entry_factory
8+
>>>>>>> 42cfc46... feat: TT-401 validated request create time entry

V2/tests/fixtures.py

Lines changed: 128 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,150 @@
11
import pytest
2+
import json
3+
import pytest
4+
import shutil
5+
from faker import Faker
26

37
import time_tracker.activities._domain as domain
48
import time_tracker.activities._infrastructure as infrastructure
59
from time_tracker._infrastructure import DB
6-
from faker import Faker
10+
from time_tracker.time_entries._domain import TimeEntry
711

812

9-
@pytest.fixture(name='activity_factory')
13+
@pytest.fixture(name="activity_factory")
1014
def _activity_factory() -> domain.Activity:
1115
def _make_activity(
12-
name: str = Faker().name(), description: str = Faker().sentence(), deleted: bool = False, status: int = 1
16+
name: str = Faker().name(),
17+
description: str = Faker().sentence(),
18+
deleted: bool = False,
19+
status: int = 1,
1320
):
1421
activity = domain.Activity(
15-
id=None,
16-
name=name,
17-
description=description,
18-
deleted=deleted,
19-
status=status
20-
)
22+
id=None, name=name, description=description, deleted=deleted, status=status
23+
)
2124
return activity
25+
2226
return _make_activity
2327

2428

25-
@pytest.fixture(name='create_fake_dao')
29+
@pytest.fixture(name="create_fake_dao")
2630
def _create_fake_dao() -> domain.ActivitiesDao:
27-
db_fake = DB('sqlite:///:memory:')
31+
db_fake = DB("sqlite:///:memory:")
2832
dao = infrastructure.ActivitiesSQLDao(db_fake)
2933
return dao
3034

3135

32-
@pytest.fixture(name='create_fake_database')
36+
@pytest.fixture(name="create_fake_database")
3337
def _create_fake_database() -> domain.ActivitiesDao:
34-
db_fake = DB('sqlite:///:memory:')
38+
db_fake = DB("sqlite:///:memory:")
3539
return db_fake
40+
41+
42+
@pytest.fixture
43+
def create_temp_activities(tmpdir_factory):
44+
temporary_directory = tmpdir_factory.mktemp("tmp")
45+
json_file = temporary_directory.join("activities.json")
46+
activities = [
47+
{
48+
"id": "c61a4a49-3364-49a3-a7f7-0c5f2d15072b",
49+
"name": "Development",
50+
"description": "Development",
51+
"deleted": "b4327ba6-9f96-49ee-a9ac-3c1edf525172",
52+
"status": "active",
53+
"tenant_id": "cc925a5d-9644-4a4f-8d99-0bee49aadd05",
54+
},
55+
{
56+
"id": "94ec92e2-a500-4700-a9f6-e41eb7b5507c",
57+
"name": "Management",
58+
"description": "Description of management",
59+
"deleted": "7cf6efe5-a221-4fe4-b94f-8945127a489a",
60+
"status": "active",
61+
"tenant_id": "cc925a5d-9644-4a4f-8d99-0bee49aadd05",
62+
},
63+
{
64+
"id": "d45c770a-b1a0-4bd8-a713-22c01a23e41b",
65+
"name": "Operations",
66+
"description": "Operation activities performed.",
67+
"deleted": "7cf6efe5-a221-4fe4-b94f-8945127a489a",
68+
"status": "active",
69+
"tenant_id": "cc925a5d-9644-4a4f-8d99-0bee49aadd05",
70+
},
71+
]
72+
73+
with open(json_file, "w") as outfile:
74+
json.dump(activities, outfile)
75+
76+
yield activities, json_file
77+
shutil.rmtree(temporary_directory)
78+
79+
80+
@pytest.fixture
81+
def create_temp_time_entries(tmpdir_factory):
82+
temporary_directory = tmpdir_factory.mktemp("tmp")
83+
json_file = temporary_directory.join("time_entries.json")
84+
time_entries = [
85+
{
86+
"id": Faker().random_int(),
87+
"start_date": Faker().date(),
88+
"owner_id": Faker().random_int(),
89+
"description": Faker().sentence(),
90+
"activity_id": Faker().random_int(),
91+
"uri": "http://time-tracker.com",
92+
"technologies": ["jira", "git"],
93+
"end_date": Faker().date(),
94+
"deleted": Faker().random_int(),
95+
"timezone_offset": "300",
96+
"project_id": Faker().random_int(),
97+
},
98+
{
99+
"id": Faker().random_int(),
100+
"start_date": Faker().date(),
101+
"owner_id": Faker().random_int(),
102+
"description": Faker().sentence(),
103+
"activity_id": Faker().random_int(),
104+
"uri": "http://time-tracker.com",
105+
"technologies": ["jira", "git"],
106+
"end_date": Faker().date(),
107+
"deleted": Faker().random_int(),
108+
"timezone_offset": "300",
109+
"project_id": Faker().random_int(),
110+
},
111+
]
112+
113+
with open(json_file, "w") as outfile:
114+
json.dump(time_entries, outfile)
115+
116+
yield time_entries, json_file
117+
shutil.rmtree(temporary_directory)
118+
119+
120+
@pytest.fixture(name="time_entry_factory")
121+
def _time_entry_factory() -> TimeEntry:
122+
def _make_time_entry(
123+
id=Faker().random_int(),
124+
start_date=Faker().date(),
125+
owner_id=Faker().random_int(),
126+
description=Faker().sentence(),
127+
activity_id=Faker().random_int(),
128+
uri="http://time-tracker.com",
129+
technologies=["jira", "git"],
130+
end_date=Faker().date(),
131+
deleted=Faker().random_int(),
132+
timezone_offset="300",
133+
project_id=Faker().random_int(),
134+
):
135+
time_entry = TimeEntry(
136+
id=id,
137+
start_date=start_date,
138+
owner_id=owner_id,
139+
description=description,
140+
activity_id=activity_id,
141+
uri=uri,
142+
technologies=technologies,
143+
end_date=end_date,
144+
deleted=deleted,
145+
timezone_offset=timezone_offset,
146+
project_id=project_id,
147+
)
148+
return time_entry
149+
150+
return _make_time_entry

V2/tests/integration/daos/time_entries_dao.test.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,21 +25,17 @@ def test_create_time_entry__returns_an_time_entry_dto__when_create_an_time_entry
2525

2626
time_entries_json_dao = TimeEntriesJsonDao(Faker().file_path())
2727
time_entry_data = {
28-
"id" : None,
29-
"start_date" : Faker().date(),
30-
"owner_id" : Faker().random_int(),
28+
"id": None,
29+
"start_date": Faker().date(),
30+
"owner_id": Faker().random_int(),
3131
"description": Faker().sentence(),
32-
"activity_id" : Faker().random_int(),
33-
"uri": "http://hola.com",
34-
"technologies" : ["jira","git"],
32+
"activity_id": Faker().random_int(),
33+
"uri": "http://timetracker.com",
34+
"technologies": ["jira", "git"],
3535
"end_date": Faker().date(),
36-
"deleted": Faker().random_int(),
37-
"timezone_offset": "UTC-5",
36+
"deleted": False,
37+
"timezone_offset": "300",
3838
"project_id": Faker().random_int(),
3939
}
4040
result = time_entries_json_dao.create(time_entry_data)
4141
assert result == TimeEntry(**time_entry_data)
42-
43-
44-
45-

V2/tests/unit/services/time_entry_service_test.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from time_tracker.time_entries._domain import TimeEntryService
44

5+
56
def test__create_time_entries__uses_the_time_entry_dao__to_create_an_time_entry(mocker):
67
expected_time_entry = mocker.Mock()
78
time_entry_dao = mocker.Mock(
@@ -12,4 +13,4 @@ def test__create_time_entries__uses_the_time_entry_dao__to_create_an_time_entry(
1213
actual_time_entry = time_entry_service.create(Faker().pydict())
1314

1415
assert time_entry_dao.create.called
15-
assert expected_time_entry == actual_time_entry
16+
assert expected_time_entry == actual_time_entry
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
1-
from faker import Faker
2-
31
from pytest_mock import MockFixture
42

53
from time_tracker.time_entries._domain import _use_cases
64

5+
76
def test__create_time_entry_function__uses_the_time_entries_service__to_create_time_entry(
8-
mocker: MockFixture,
7+
mocker: MockFixture, time_entry_factory
98
):
109
expected_time_entry = mocker.Mock()
1110
time_entry_service = mocker.Mock(
1211
create=mocker.Mock(return_value=expected_time_entry)
1312
)
1413

1514
time_entry_use_case = _use_cases.CreateTimeEntryUseCase(time_entry_service)
16-
actual_time_entry = time_entry_use_case.create_time_entry(Faker().pydict())
15+
actual_time_entry = time_entry_use_case.create_time_entry(time_entry_factory())
1716

1817
assert time_entry_service.create.called
1918
assert expected_time_entry == actual_time_entry
20-
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
# flake8: noqa
12
from ._time_entries import create_time_entry
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1+
# flake8: noqa
12
from ._create_time_entry import create_time_entry

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

Lines changed: 48 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -8,42 +8,59 @@
88
from ... import _infrastructure
99

1010
_JSON_PATH = (
11-
'time_entries/_infrastructure/_data_persistence/time_entries_data.json'
11+
'time_tracker/time_entries/_infrastructure/_data_persistence/time_entries_data.json'
1212
)
1313

14+
1415
def create_time_entry(req: func.HttpRequest) -> func.HttpResponse:
1516

16-
time_entry_dao = _infrastructure.TimeEntriesJsonDao(_JSON_PATH)
17-
time_entry_service = _domain.TimeEntryService(time_entry_dao)
18-
use_case = _domain._use_cases.CreateTimeEntryUseCase(time_entry_service)
19-
20-
time_entry_data = req.get_json()
21-
22-
time_entry_to_create = _domain.TimeEntry(
23-
id=None,
24-
start_date=time_entry_data["start_date"],
25-
owner_id=time_entry_data["owner_id"],
26-
description=time_entry_data["description"],
27-
activity_id=time_entry_data["activity_id"],
28-
uri=time_entry_data["uri"],
29-
technologies=time_entry_data["technologies"],
30-
end_date=time_entry_data["end_date"],
31-
deleted=time_entry_data["deleted"],
32-
timezone_offset=time_entry_data["timezone_offset"],
33-
project_id=time_entry_data["project_id"]
34-
)
35-
36-
created_time_entry = use_case.create_time_entry(time_entry_to_create.__dict__)
37-
38-
if not created_time_entry:
17+
time_entry_dao = _infrastructure.TimeEntriesJsonDao(_JSON_PATH)
18+
time_entry_service = _domain.TimeEntryService(time_entry_dao)
19+
use_case = _domain._use_cases.CreateTimeEntryUseCase(time_entry_service)
20+
21+
time_entry_data = req.get_json()
22+
23+
validation_errors = _validate_time_entry(time_entry_data)
24+
if validation_errors:
25+
return func.HttpResponse(
26+
body=json.dumps(validation_errors), status_code=400, mimetype="application/json"
27+
)
28+
29+
time_entry_to_create = _domain.TimeEntry(
30+
id=None,
31+
start_date=time_entry_data["start_date"],
32+
owner_id=time_entry_data["owner_id"],
33+
description=time_entry_data["description"],
34+
activity_id=time_entry_data["activity_id"],
35+
uri=time_entry_data["uri"],
36+
technologies=time_entry_data["technologies"],
37+
end_date=time_entry_data["end_date"],
38+
deleted=False,
39+
timezone_offset=time_entry_data["timezone_offset"],
40+
project_id=time_entry_data["project_id"]
41+
)
42+
43+
created_time_entry = use_case.create_time_entry(time_entry_to_create)
44+
45+
if not created_time_entry:
46+
return func.HttpResponse(
47+
body=json.dumps({'error': 'time_entry could not be created'}),
48+
status_code=500,
49+
mimetype="application/json"
50+
)
51+
3952
return func.HttpResponse(
40-
body=json.dumps({'error': 'time_entry could not be created'}),
41-
status_code=500,
53+
body=json.dumps(created_time_entry.__dict__),
54+
status_code=201,
4255
mimetype="application/json"
4356
)
4457

45-
return func.HttpResponse(
46-
body=json.dumps(created_time_entry.__dict__),
47-
status_code=201,
48-
mimetype="application/json"
49-
)
58+
59+
def _validate_time_entry(time_entry_data: dict) -> typing.List[str]:
60+
time_entry_fields = [field.name for field in dataclasses.fields(_domain.TimeEntry)]
61+
time_entry_fields.pop(8)
62+
missing_keys = [field for field in time_entry_fields if field not in time_entry_data]
63+
return [
64+
f'The {missing_key} key is missing in the input data'
65+
for missing_key in missing_keys
66+
]

V2/time_tracker/time_entries/_domain/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
# flake8: noqa
12
from ._entities import TimeEntry
23
from ._persistence_contracts import TimeEntriesDao
34
from ._services import TimeEntryService

0 commit comments

Comments
 (0)