Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1065bde
feat: TT-353 Create V2 Activities DAO
Sep 24, 2021
3bb39b0
refactor: TT-353 Solving code smells from SonarCloud
Sep 24, 2021
b15c301
refactor: TT-353 Solving duplicated literal
Sep 24, 2021
7a2565c
refactor: TT-353 Add type of argument and return type to functions
Sep 27, 2021
8bdbdfe
refactor: TT-353 Solving comments from PR
Sep 29, 2021
bc998fe
refactor: TT-353 Solving Sonarcloud code smell
Sep 29, 2021
9d0e21e
refactor: TT-353 Changing variable names and tests
Sep 29, 2021
4c467c7
feat: TT-352 Create entry point and use case to get activities
JosueOb Sep 30, 2021
408c016
feat: TT-352 Create entry point and use case to get activity
JosueOb Sep 30, 2021
a936f0a
refactor: TT-352 use list comprehensions
JosueOb Sep 30, 2021
47bb34b
refactor: TT-352 standarization flask_api directory
JosueOb Sep 30, 2021
1f92569
refactor: TT-353 Solving requested changes on PR
Sep 30, 2021
2ce4987
refactor: TT-352 use_cases and entry_points improvements to read acti…
JosueOb Sep 30, 2021
361a864
Merge branch 'TT-353-Create-V2-Activities-DAO' into TT-352-Create-V2-…
JosueOb Sep 30, 2021
bb87c54
test: TT-352 Unit test of activity use cases
JosueOb Oct 4, 2021
1006410
test: TT-352 entry_points and use_cases for activities complete testing
JosueOb Oct 6, 2021
39f5221
refactor: TT-352 solving merge conflicts
JosueOb Oct 6, 2021
9539885
code-smell: TT-352 fixing code-smell
JosueOb Oct 6, 2021
9e090bc
build: TT-352 implementation of CSRF Protection using Flask-WTF
JosueOb Oct 6, 2021
b0cc467
refactor: TT-352 improving use_cases, endpoitns and test to get activ…
JosueOb Oct 6, 2021
13133d1
test: TT-352 improved testing of activity use cases
JosueOb Oct 7, 2021
7b582b7
refactor: TT-352 improvement of endpoint testing for obtaining activi…
JosueOb Oct 8, 2021
a2d912c
refactor: TT-352 refactoring of the use case and endpoint to obtain a…
JosueOb Oct 12, 2021
b4987e6
refactor: TT-352 refactoring of use cases and enpoint to obtain activ…
JosueOb Oct 12, 2021
4866913
test: TT-352 refactoring of use cases and enpoint to obtain activities
JosueOb Oct 12, 2021
fd84ea1
refactor: TT-352 refactoring of activity endpoints
JosueOb Oct 13, 2021
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
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
26 changes: 26 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,26 @@
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):
activity_json = ActivitiesJsonDao(JSON_PATH)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Este código está duplicado con la línea 21

activity_service = ActivityService(activity_json)
activities = use_cases.GetActivitiesUseCase(activity_service)
return [activity.__dict__ for activity in activities.get_activities()]


class Activity(Resource):
def get(self, activity_id: str):
try:
activity_json = ActivitiesJsonDao(JSON_PATH)
activity_service = ActivityService(activity_json)
activity = use_cases.GetActivityUseCase(activity_service)
return activity.get_activity_by_id(activity_id).__dict__
except AttributeError:
return {'message': 'Activity not found'}, HTTPStatus.NOT_FOUND
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'
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
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
1 change: 1 addition & 0 deletions requirements/time_tracker_api/prod.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#Required by Flask
Flask==1.1.1
Flask-WTF==0.15.1
flake8==3.7.9
WSGIserver==1.3
Werkzeug==0.16.1
Expand Down