Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
refactor: TT-356 Solving merge conflicts
  • Loading branch information
Andrés Soto committed Oct 13, 2021
commit 7a8df353187743d0f120259b50a8c20c7fed6871
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

<!--next-version-placeholder-->

## v0.39.1 (2021-10-06)
### Fix
* TT-339 skip users with azureioet.onmicrosoft.com extension from user search ([#322](https://github.com/ioet/time-tracker-backend/issues/322)) ([`8b37d4a`](https://github.com/ioet/time-tracker-backend/commit/8b37d4a7a890b9e4880efedd19dc733e60c5e7cf))

## v0.39.0 (2021-10-04)
### Feature
* TT-353 Create V2 Activities DAO ([#320](https://github.com/ioet/time-tracker-backend/issues/320)) ([`328ad43`](https://github.com/ioet/time-tracker-backend/commit/328ad43e3058de3c824b2feec47530bee5b23823))
Expand Down
30 changes: 30 additions & 0 deletions V2/source/entry_points/flask_api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
from flask import Flask
from flask_wtf.csrf import CSRFProtect
from flask_restplus import Namespace, Resource, Api
from http import HTTPStatus
from . import activities_endpoints

csrf = CSRFProtect()


def create_app(test_config=None):
app = Flask(__name__)
csrf.init_app(app)

api = Api(
app,
version='1.0',
title='Time Tracker API',
description='API for the TimeTracker project',
)

if test_config is not None:
app.config.from_mapping(test_config)

activities_namespace = Namespace('activities', description='Endpoint for activities')
activities_namespace.route('/')(activities_endpoints.Activities)
activities_namespace.route('/<string:activity_id>')(activities_endpoints.Activity)

api.add_namespace(activities_namespace)

return app
31 changes: 31 additions & 0 deletions V2/source/entry_points/flask_api/activities_endpoints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from V2.source.daos.activities_json_dao import ActivitiesJsonDao
from V2.source.services.activity_service import ActivityService
from V2.source import use_cases
from flask_restplus import Resource
from http import HTTPStatus

JSON_PATH = './V2/source/activities_data.json'


class Activities(Resource):
def get(self):
activities = use_cases.GetActivitiesUseCase(
create_activity_service(JSON_PATH)
)
return [activity.__dict__ for activity in activities.get_activities()]


class Activity(Resource):
def get(self, activity_id: str):
try:
activity = use_cases.GetActivityUseCase(
create_activity_service(JSON_PATH)
)
return activity.get_activity_by_id(activity_id).__dict__
except AttributeError:
return {'message': 'Activity not found'}, HTTPStatus.NOT_FOUND


def create_activity_service(path: str):
activity_json = ActivitiesJsonDao(path)
return ActivityService(activity_json)
2 changes: 2 additions & 0 deletions V2/source/use_cases/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from ._get_activities_use_case import GetActivitiesUseCase
from ._get_activity_by_id_use_case import GetActivityUseCase
11 changes: 11 additions & 0 deletions V2/source/use_cases/_get_activities_use_case.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from V2.source.services.activity_service import ActivityService
from V2.source.dtos.activity import Activity
import typing


class GetActivitiesUseCase:
def __init__(self, activity_service: ActivityService):
self.activity_service = activity_service

def get_activities(self) -> typing.List[Activity]:
return self.activity_service.get_all()
10 changes: 10 additions & 0 deletions V2/source/use_cases/_get_activity_by_id_use_case.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
from V2.source.services.activity_service import ActivityService
from V2.source.dtos.activity import Activity


class GetActivityUseCase:
def __init__(self, activity_service: ActivityService):
self.activity_service = activity_service

def get_activity_by_id(self, id: str) -> Activity:
return self.activity_service.get_by_id(id)
86 changes: 86 additions & 0 deletions V2/tests/api/flask/activity_endpoints_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from V2.source.entry_points.flask_api import create_app
import json
import pytest
import typing
from flask.testing import FlaskClient
from http import HTTPStatus
from faker import Faker
import shutil


@pytest.fixture
def client():
app = create_app({'TESTING': True})
with app.test_client() as client:
yield client


@pytest.fixture
def activities_json(tmpdir_factory):
temporary_directory = tmpdir_factory.mktemp("tmp")
json_file = temporary_directory.join("activities.json")
activities = [
{
'id': 'c61a4a49-3364-49a3-a7f7-0c5f2d15072b',
'name': 'Development',
'description': 'Development',
'deleted': 'b4327ba6-9f96-49ee-a9ac-3c1edf525172',
'status': None,
'tenant_id': 'cc925a5d-9644-4a4f-8d99-0bee49aadd05',
},
{
'id': '94ec92e2-a500-4700-a9f6-e41eb7b5507c',
'name': 'Management',
'description': None,
'deleted': '7cf6efe5-a221-4fe4-b94f-8945127a489a',
'status': None,
'tenant_id': 'cc925a5d-9644-4a4f-8d99-0bee49aadd05',
},
{
'id': 'd45c770a-b1a0-4bd8-a713-22c01a23e41b',
'name': 'Operations',
'description': 'Operation activities performed.',
'deleted': '7cf6efe5-a221-4fe4-b94f-8945127a489a',
'status': 'active',
'tenant_id': 'cc925a5d-9644-4a4f-8d99-0bee49aadd05',
},
]

with open(json_file, 'w') as outfile:
json.dump(activities, outfile)

with open(json_file) as outfile:
activities_json = json.load(outfile)

yield activities_json
shutil.rmtree(temporary_directory)


def test_test__activity_endpoint__returns_all_activities(
client: FlaskClient, activities_json: typing.List[dict]
):
response = client.get("/activities/")
json_data = json.loads(response.data)

assert response.status_code == HTTPStatus.OK
assert json_data == activities_json


def test__activity_endpoint__returns_an_activity__when_activity_matches_its_id(
client: FlaskClient, activities_json: typing.List[dict]
):
response = client.get("/activities/%s" % activities_json[0]['id'])
json_data = json.loads(response.data)

assert response.status_code == HTTPStatus.OK
assert json_data == activities_json[0]


def test__activity_endpoint__returns_a_not_found_status__when_no_activity_matches_its_id(
client: FlaskClient,
):
response = client.get("/activities/%s" % Faker().uuid4())
json_data = json.loads(response.data)

assert response.status_code == HTTPStatus.NOT_FOUND
assert json_data['message'] == 'Activity not found'
85 changes: 85 additions & 0 deletions V2/tests/integration/daos/activities_json_dao_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from V2.source.daos.activities_json_dao import ActivitiesJsonDao
from V2.source.dtos.activity import Activity
from faker import Faker
import json
import pytest
import typing


@pytest.fixture(name='create_fake_activities')
def _create_fake_activities(mocker) -> typing.List[Activity]:
def _creator(activities):
read_data = json.dumps(activities)
mocker.patch('builtins.open', mocker.mock_open(read_data=read_data))
return [Activity(**activity) for activity in activities]

return _creator


def test_get_by_id__returns_an_activity_dto__when_found_one_activity_that_matches_its_id(
create_fake_activities,
):
activities_json_dao = ActivitiesJsonDao(Faker().file_path())
activities = create_fake_activities(
[
{
"name": "test_name",
"description": "test_description",
"tenant_id": "test_tenant_id",
"id": "test_id",
"deleted": "test_deleted",
"status": "test_status",
}
]
)
activity_dto = activities.pop()

result = activities_json_dao.get_by_id(activity_dto.id)

assert result == activity_dto


def test__get_by_id__returns_none__when_no_activity_matches_its_id(
create_fake_activities,
):
activities_json_dao = ActivitiesJsonDao(Faker().file_path())
create_fake_activities([])

result = activities_json_dao.get_by_id(Faker().uuid4())

assert result == None


def test__get_all__returns_a_list_of_activity_dto_objects__when_one_or_more_activities_are_found(
create_fake_activities,
):
activities_json_dao = ActivitiesJsonDao(Faker().file_path())
number_of_activities = 3
activities = create_fake_activities(
[
{
"name": "test_name",
"description": "test_description",
"tenant_id": "test_tenant_id",
"id": "test_id",
"deleted": "test_deleted",
"status": "test_status",
}
]
* number_of_activities
)

result = activities_json_dao.get_all()

assert result == activities


def test_get_all__returns_an_empty_list__when_doesnt_found_any_activities(
create_fake_activities,
):
activities_json_dao = ActivitiesJsonDao(Faker().file_path())
activities = create_fake_activities([])

result = activities_json_dao.get_all()

assert result == activities
55 changes: 55 additions & 0 deletions V2/tests/unit/entry_points/flask/activity_class_endpoint_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from V2.source.entry_points.flask_api.activities_endpoints import (
Activities,
Activity,
)
from V2.source import use_cases
from V2.source.dtos.activity import Activity as ActivityDTO
from pytest_mock import MockFixture
from faker import Faker
from werkzeug.exceptions import NotFound

fake = Faker()

valid_id = fake.uuid4()

fake_activity = {
"name": fake.company(),
"description": fake.paragraph(),
"tenant_id": fake.uuid4(),
"id": valid_id,
"deleted": fake.date(),
"status": fake.boolean(),
}
fake_activity_dto = ActivityDTO(**fake_activity)


def test__activities_class__uses_the_get_activities_use_case__to_retrieve_activities(
mocker: MockFixture,
):
mocker.patch.object(
use_cases.GetActivitiesUseCase,
'get_activities',
return_value=[],
)

activities_class_endpoint = Activities()
activities = activities_class_endpoint.get()

assert use_cases.GetActivitiesUseCase.get_activities.called
assert [] == activities


def test__activity_class__uses_the_get_activity_by_id_use_case__to_retrieve__an_activity(
mocker: MockFixture,
):
mocker.patch.object(
use_cases.GetActivityUseCase,
'get_activity_by_id',
return_value=fake_activity_dto,
)

activity_class_endpoint = Activity()
activity = activity_class_endpoint.get(valid_id)

assert use_cases.GetActivityUseCase.get_activity_by_id.called
assert fake_activity == activity
28 changes: 28 additions & 0 deletions V2/tests/unit/services/activity_service_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from V2.source.services.activity_service import ActivityService
from faker import Faker


def test__get_all__uses_the_activity_dao__to_retrieve_activities(mocker):
expected_activities = mocker.Mock()
activity_dao = mocker.Mock(
get_all=mocker.Mock(return_value=expected_activities)
)
activity_service = ActivityService(activity_dao)

actual_activities = activity_service.get_all()

assert activity_dao.get_all.called
assert expected_activities == actual_activities


def test__get_by_id__uses_the_activity_dao__to_retrieve_one_activity(mocker):
expected_activity = mocker.Mock()
activity_dao = mocker.Mock(
get_by_id=mocker.Mock(return_value=expected_activity)
)
activity_service = ActivityService(activity_dao)

actual_activity = activity_service.get_by_id(Faker().uuid4())

assert activity_dao.get_by_id.called
assert expected_activity == actual_activity
36 changes: 36 additions & 0 deletions V2/tests/unit/use_cases/activities_use_case_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from V2.source.services.activity_service import ActivityService
from V2.source import use_cases
from pytest_mock import MockFixture
from faker import Faker

fake = Faker()


def test__get_list_activities_function__uses_the_activities_service__to_retrieve_activities(
mocker: MockFixture,
):
expected_activities = mocker.Mock()
activity_service = mocker.Mock(
get_all=mocker.Mock(return_value=expected_activities)
)

activities_use_case = use_cases.GetActivitiesUseCase(activity_service)
actual_activities = activities_use_case.get_activities()

assert activity_service.get_all.called
assert expected_activities == actual_activities


def test__get_activity_by_id_function__uses_the_activities_service__to_retrieve_activity(
mocker: MockFixture,
):
expected_activity = mocker.Mock()
activity_service = mocker.Mock(
get_by_id=mocker.Mock(return_value=expected_activity)
)

activity_use_case = use_cases.GetActivityUseCase(activity_service)
actual_activity = activity_use_case.get_activity_by_id(fake.uuid4())

assert activity_service.get_by_id.called
assert expected_activity == actual_activity
Loading
You are viewing a condensed version of this merge commit. You can view the full changes here.