Skip to content
Next Next commit
feat: TT-401 Implemented service, end-point, dao, test- time entries
  • Loading branch information
ararcos committed Nov 17, 2021
commit 2637fd1fefc9644c0590d59c9c868389be5c8ae1
10 changes: 10 additions & 0 deletions V2/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,13 @@ functions:
- POST
route: activities/
authLevel: anonymous

create_time_entry:
handler: time_tracker/time_entries/interface.create_time_entry
events:
- http: true
x-azure-settings:
methods:
- POST
route: time-entries/
authLevel: anonymous
40 changes: 40 additions & 0 deletions V2/tests/api/azure/time_entry_azure_endpoints_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from time_tracker.time_entries._application import _time_entries as time_entries
from faker import Faker

import azure.functions as func
import json


ACTIVITY_URL = "/api/time-entries/"


def test__time_entry_azure_endpoint__creates_an_time_entry__when_time_entry_has_all_attributes(
create_temp_time_entries,
):
time_entries_json, tmp_directory = create_temp_time_entries
time_entries._create_time_entry._JSON_PATH = tmp_directory

time_entry_body = {
"id" : None,
"start_date" : Faker().date(),
"owner_id" : Faker().random_int(),
"description": Faker().sentence(),
"activity_id" : Faker().random_int(),
"uri": "http://timetracker.com",
"technologies" : ["jira","git"],
"end_date": Faker().date(),
"deleted": Faker().random_int(),
"timezone_offset": "300",
"project_id": Faker().random_int(),
}
body = json.dumps(time_entry_body).encode("utf-8")
req = func.HttpRequest(
method="POST",
body=body,
url=ACTIVITY_URL,
)

response = time_entries.create_time_entry(req)
time_entry_json_data = response.get_body()
assert response.status_code == 201
assert time_entry_json_data == body
2 changes: 1 addition & 1 deletion V2/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
# flake8: noqa
from fixtures import _activity_factory, _create_fake_dao, _create_fake_database
from fixtures import _activity_factory, _create_fake_dao, _create_fake_database
45 changes: 45 additions & 0 deletions V2/tests/integration/daos/time_entries_dao.test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import json
import pytest
import typing

from faker import Faker

from time_tracker.time_entries._infrastructure import TimeEntriesJsonDao
from time_tracker.time_entries._domain import TimeEntry


@pytest.fixture(name="create_fake_time_entries")
def _create_fake_time_entries(mocker) -> typing.List[TimeEntry]:
def _creator(time_entries):
read_data = json.dumps(time_entries)
mocker.patch("builtins.open", mocker.mock_open(read_data=read_data))
return [TimeEntry(**time_entry) for time_entry in time_entries]

return _creator


def test_create_time_entry__returns_an_time_entry_dto__when_create_an_time_entry_that_matches_attributes(
create_fake_time_entries,
):
create_fake_time_entries([])

time_entries_json_dao = TimeEntriesJsonDao(Faker().file_path())
time_entry_data = {
"id" : None,
"start_date" : Faker().date(),
"owner_id" : Faker().random_int(),
"description": Faker().sentence(),
"activity_id" : Faker().random_int(),
"uri": "http://hola.com",
"technologies" : ["jira","git"],
"end_date": Faker().date(),
"deleted": Faker().random_int(),
"timezone_offset": "UTC-5",
"project_id": Faker().random_int(),
}
result = time_entries_json_dao.create(time_entry_data)
assert result == TimeEntry(**time_entry_data)




15 changes: 15 additions & 0 deletions V2/tests/unit/services/time_entry_service_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from faker import Faker

from time_tracker.time_entries._domain import TimeEntryService

def test__create_time_entries__uses_the_time_entry_dao__to_create_an_time_entry(mocker):
expected_time_entry = mocker.Mock()
time_entry_dao = mocker.Mock(
create=mocker.Mock(return_value=expected_time_entry)
)
time_entry_service = TimeEntryService(time_entry_dao)

actual_time_entry = time_entry_service.create(Faker().pydict())

assert time_entry_dao.create.called
assert expected_time_entry == actual_time_entry
20 changes: 20 additions & 0 deletions V2/tests/unit/use_cases/time_entries_use_case_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from faker import Faker

from pytest_mock import MockFixture

from time_tracker.time_entries._domain import _use_cases

def test__create_time_entry_function__uses_the_time_entries_service__to_create_time_entry(
mocker: MockFixture,
):
expected_time_entry = mocker.Mock()
time_entry_service = mocker.Mock(
create=mocker.Mock(return_value=expected_time_entry)
)

time_entry_use_case = _use_cases.CreateTimeEntryUseCase(time_entry_service)
actual_time_entry = time_entry_use_case.create_time_entry(Faker().pydict())

assert time_entry_service.create.called
assert expected_time_entry == 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
@@ -0,0 +1 @@
from ._time_entries import create_time_entry
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from ._create_time_entry import create_time_entry
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import dataclasses
import json
import typing

import azure.functions as func

from ... import _domain
from ... import _infrastructure

_JSON_PATH = (
'time_entries/_infrastructure/_data_persistence/time_entries_data.json'
)

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

time_entry_dao = _infrastructure.TimeEntriesJsonDao(_JSON_PATH)
time_entry_service = _domain.TimeEntryService(time_entry_dao)
use_case = _domain._use_cases.CreateTimeEntryUseCase(time_entry_service)

time_entry_data = req.get_json()

time_entry_to_create = _domain.TimeEntry(
id=None,
start_date=time_entry_data["start_date"],
owner_id=time_entry_data["owner_id"],
description=time_entry_data["description"],
activity_id=time_entry_data["activity_id"],
uri=time_entry_data["uri"],
technologies=time_entry_data["technologies"],
end_date=time_entry_data["end_date"],
deleted=time_entry_data["deleted"],
timezone_offset=time_entry_data["timezone_offset"],
project_id=time_entry_data["project_id"]
)

created_time_entry = use_case.create_time_entry(time_entry_to_create.__dict__)

if not created_time_entry:
return func.HttpResponse(
body=json.dumps({'error': 'time_entry could not be created'}),
status_code=500,
mimetype="application/json"
)

return func.HttpResponse(
body=json.dumps(created_time_entry.__dict__),
status_code=201,
mimetype="application/json"
)
6 changes: 6 additions & 0 deletions V2/time_tracker/time_entries/_domain/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from ._entities import TimeEntry
from ._persistence_contracts import TimeEntriesDao
from ._services import TimeEntryService
from ._use_cases import (
CreateTimeEntryUseCase,
)
1 change: 1 addition & 0 deletions V2/time_tracker/time_entries/_domain/_entities/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from ._time_entry import TimeEntry
16 changes: 16 additions & 0 deletions V2/time_tracker/time_entries/_domain/_entities/_time_entry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from dataclasses import dataclass
from typing import List

@dataclass(frozen=True)
class TimeEntry:
id: int
start_date: str
owner_id: int
description: str
activity_id: int
uri: str
technologies: List[str]
end_date: str
deleted: str
timezone_offset: str
project_id: int
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from ._time_entries_dao import TimeEntriesDao
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import abc

from time_tracker.time_entries._domain import TimeEntry

class TimeEntriesDao(abc.ABC):
def create(self, time_entry_data: dict) -> TimeEntry:
pass
1 change: 1 addition & 0 deletions V2/time_tracker/time_entries/_domain/_services/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from ._time_entry import TimeEntryService
9 changes: 9 additions & 0 deletions V2/time_tracker/time_entries/_domain/_services/_time_entry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
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

def create(self, time_entry_data: dict) -> TimeEntry:
return self.time_entry_dao.create(time_entry_data)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from ._create_time_entry_use_case import CreateTimeEntryUseCase
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from time_tracker.time_entries._domain import TimeEntry, TimeEntryService

class CreateTimeEntryUseCase:

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

def create_time_entry(self, time_entry_data: dict) -> TimeEntry:
return self.time_entry_service.create(time_entry_data)
1 change: 1 addition & 0 deletions V2/time_tracker/time_entries/_infrastructure/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from ._data_persistence import TimeEntriesJsonDao
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from ._time_entries_dao import TimeEntriesJsonDao
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import json
import dataclasses
import typing

from time_tracker.time_entries._domain import TimeEntriesDao, TimeEntry

class TimeEntriesJsonDao(TimeEntriesDao):

def __init__(self, json_data_file_path: str):
self.json_data_file_path = json_data_file_path
self.time_entry_key = [field.name for field in dataclasses.fields(TimeEntry)]

def create(self, time_entry_data: dict) -> TimeEntry:
time_entries = self.__get_time_entries_from_file()
time_entries.append(time_entry_data)

try:
with open(self.json_data_file_path, 'w') as outfile:
json.dump(time_entries, outfile)

return self.__create_time_entry_dto(time_entry_data)
except FileNotFoundError:
print("Can not create activity")

def __get_time_entries_from_file(self) -> typing.List[dict]:
try:
file = open(self.json_data_file_path)
time_entries = json.load(file)
file.close()

return time_entries

except FileNotFoundError:
return []

def __create_time_entry_dto(self, time_entry: dict) -> TimeEntry:
time_entry = {key: time_entry.get(key) for key in self.time_entry_key}
return TimeEntry(**time_entry)
Loading