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
14 changes: 12 additions & 2 deletions commons/data_access_layer/cosmos_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@ def create_sql_condition_for_visibility(
return 'AND NOT IS_DEFINED(%s.deleted)' % container_name
return ''

@staticmethod
def create_sql_condition_for_active( only_active: bool,
container_name='c' ) -> str:
if only_active:
return 'AND NOT IS_DEFINED(%s.status) OR (IS_DEFINED(%s.status) AND %s.status = \'active\')' % (container_name, container_name, container_name)
return ''

@staticmethod
def create_sql_where_conditions(
conditions: dict, container_name='c'
Expand Down Expand Up @@ -225,6 +232,7 @@ def find(
def find_all(
self,
event_context: EventContext,
only_active = True,
conditions: dict = None,
custom_sql_conditions: List[str] = None,
custom_params: dict = None,
Expand Down Expand Up @@ -253,6 +261,7 @@ def find_all(
WHERE c.{partition_key_attribute}=@partition_key_value
{conditions_clause}
{visibility_condition}
{only_active_condition}
{custom_sql_conditions_clause}
{order_clause}
OFFSET @offset LIMIT @max_count
Expand All @@ -261,6 +270,7 @@ def find_all(
visibility_condition=self.create_sql_condition_for_visibility(
visible_only
),
only_active_condition=self.create_sql_condition_for_active(only_active),
conditions_clause=self.create_sql_where_conditions(conditions),
custom_sql_conditions_clause=self.create_custom_sql_conditions(
custom_sql_conditions
Expand Down Expand Up @@ -348,11 +358,11 @@ class CosmosDBDao(CRUDDao):
def __init__(self, repository: CosmosDBRepository):
self.repository = repository

def get_all(self, conditions: dict = None, **kwargs) -> list:
def get_all(self, only_active = True, conditions: dict = None, **kwargs) -> list:
conditions = conditions if conditions else {}
event_ctx = self.create_event_context("read-many")
return self.repository.find_all(
event_ctx, conditions=conditions, **kwargs
event_ctx, only_active, conditions=conditions, **kwargs
)

def get(self, id):
Expand Down
16 changes: 8 additions & 8 deletions tests/time_tracker_api/activities/activities_namespace_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,15 @@ def test_list_all_activities(client: FlaskClient,
'find_all',
return_value=[])

response = client.get("/activities",
response = client.get("/activities?only_active=1",
headers=valid_header,
follow_redirects=True)

assert HTTPStatus.OK == response.status_code
json_data = json.loads(response.data)
assert [] == json_data

repository_find_all_mock.assert_called_once_with(ANY, conditions={})
repository_find_all_mock.assert_called_once_with(ANY, ANY, conditions={})


def test_get_activity_should_succeed_with_valid_id(client: FlaskClient,
Expand Down Expand Up @@ -195,7 +195,7 @@ def test_delete_activity_should_succeed_with_valid_id(client: FlaskClient,
valid_id = fake.random_int(1, 9999)

repository_remove_mock = mocker.patch.object(activity_dao.repository,
'delete',
'partial_update',
return_value=None)

response = client.delete("/activities/%s" % valid_id,
Expand All @@ -204,7 +204,7 @@ def test_delete_activity_should_succeed_with_valid_id(client: FlaskClient,

assert HTTPStatus.NO_CONTENT == response.status_code
assert b'' == response.data
repository_remove_mock.assert_called_once_with(str(valid_id), ANY)
repository_remove_mock.assert_called_once_with(str(valid_id), {'status': 'inactive'}, ANY)


def test_delete_activity_should_return_not_found_with_invalid_id(client: FlaskClient,
Expand All @@ -216,15 +216,15 @@ def test_delete_activity_should_return_not_found_with_invalid_id(client: FlaskCl
invalid_id = fake.random_int(1, 9999)

repository_remove_mock = mocker.patch.object(activity_dao.repository,
'delete',
'partial_update',
side_effect=NotFound)

response = client.delete("/activities/%s" % invalid_id,
headers=valid_header,
follow_redirects=True)

assert HTTPStatus.NOT_FOUND == response.status_code
repository_remove_mock.assert_called_once_with(str(invalid_id), ANY)
repository_remove_mock.assert_called_once_with(str(invalid_id), {'status': 'inactive'}, ANY)


def test_delete_activity_should_return_422_for_invalid_id_format(client: FlaskClient,
Expand All @@ -236,12 +236,12 @@ def test_delete_activity_should_return_422_for_invalid_id_format(client: FlaskCl
invalid_id = fake.company()

repository_remove_mock = mocker.patch.object(activity_dao.repository,
'delete',
'partial_update',
side_effect=UnprocessableEntity)

response = client.delete("/activities/%s" % invalid_id,
headers=valid_header,
follow_redirects=True)

assert HTTPStatus.UNPROCESSABLE_ENTITY == response.status_code
repository_remove_mock.assert_called_once_with(str(invalid_id), ANY)
repository_remove_mock.assert_called_once_with(str(invalid_id), {'status': 'inactive'}, ANY)
1 change: 1 addition & 0 deletions time_tracker_api/activities/activities_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class ActivityCosmosDBModel(CosmosDBModel):
name: str
description: str
deleted: str
status: str
tenant_id: str

def __init__(self, data):
Expand Down
28 changes: 25 additions & 3 deletions time_tracker_api/activities/activities_namespace.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,15 @@
required=False,
description='Comments about the activity',
example=faker.paragraph(),
)
),
'status': fields.String(
required=False,
title='Status',
description='Status active or inactive activities',
example=Faker().words(
2, ['active', 'inactive',], unique=True
),
),
})

activity_response_fields = {}
Expand All @@ -37,14 +45,28 @@

activity_dao = create_dao()

list_activities_attribs_parser = ns.parser()
list_activities_attribs_parser.add_argument(
'only_active',
required=False,
type=int,
store_missing=False,
help="(Filter) Permits to get a list of active or inactive activities.",
location='args',
)

ONLY_ACTIVE_TRUE_VALUE = 1

@ns.route('')
class Activities(Resource):
@ns.doc('list_activities')
@ns.marshal_list_with(activity)
@ns.expect(list_activities_attribs_parser)
def get(self):
"""List all activities"""
return activity_dao.get_all()
conditions = list_activities_attribs_parser.parse_args()
only_active = conditions.get('only_active') == ONLY_ACTIVE_TRUE_VALUE
return activity_dao.get_all(only_active)

@ns.doc('create_activity')
@ns.response(HTTPStatus.CONFLICT, 'This activity already exists')
Expand Down Expand Up @@ -80,5 +102,5 @@ def put(self, id):
@ns.response(HTTPStatus.NO_CONTENT, 'Activity deleted successfully')
def delete(self, id):
"""Delete an activity"""
activity_dao.delete(id)
activity_dao.update(id, {'status': 'inactive'})
return None, HTTPStatus.NO_CONTENT
5 changes: 3 additions & 2 deletions time_tracker_api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,8 +186,9 @@ def default_error_handler(error):
HTTPStatus.INTERNAL_SERVER_ERROR,
)


@api.errorhandler(StopIteration)
def handle_no_content(error):
app.logger.error(error)
return {'message': 'No Content'}, HTTPStatus.NO_CONTENT
return {'message': 'No Content'}, HTTPStatus.NO_CONTENT