Skip to content

Commit 1928714

Browse files
author
Pablo Solorzano
committed
fix:TT-210 Dont allow activities deleting
1 parent c133c5d commit 1928714

File tree

5 files changed

+49
-15
lines changed

5 files changed

+49
-15
lines changed

commons/data_access_layer/cosmos_db.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ def create_sql_condition_for_visibility(
133133
return 'AND NOT IS_DEFINED(%s.deleted)' % container_name
134134
return ''
135135

136+
@staticmethod
137+
def create_sql_condition_for_active( only_active: bool,
138+
container_name='c' ) -> str:
139+
if only_active:
140+
return 'AND NOT IS_DEFINED(%s.status) OR (IS_DEFINED(%s.status) AND %s.status = \'active\')' % (container_name, container_name, container_name)
141+
return ''
142+
136143
@staticmethod
137144
def create_sql_where_conditions(
138145
conditions: dict, container_name='c'
@@ -225,6 +232,7 @@ def find(
225232
def find_all(
226233
self,
227234
event_context: EventContext,
235+
only_active = True,
228236
conditions: dict = None,
229237
custom_sql_conditions: List[str] = None,
230238
custom_params: dict = None,
@@ -253,6 +261,7 @@ def find_all(
253261
WHERE c.{partition_key_attribute}=@partition_key_value
254262
{conditions_clause}
255263
{visibility_condition}
264+
{only_active_condition}
256265
{custom_sql_conditions_clause}
257266
{order_clause}
258267
OFFSET @offset LIMIT @max_count
@@ -261,6 +270,7 @@ def find_all(
261270
visibility_condition=self.create_sql_condition_for_visibility(
262271
visible_only
263272
),
273+
only_active_condition=self.create_sql_condition_for_active(only_active),
264274
conditions_clause=self.create_sql_where_conditions(conditions),
265275
custom_sql_conditions_clause=self.create_custom_sql_conditions(
266276
custom_sql_conditions
@@ -348,11 +358,11 @@ class CosmosDBDao(CRUDDao):
348358
def __init__(self, repository: CosmosDBRepository):
349359
self.repository = repository
350360

351-
def get_all(self, conditions: dict = None, **kwargs) -> list:
361+
def get_all(self, only_active = True, conditions: dict = None, **kwargs) -> list:
352362
conditions = conditions if conditions else {}
353363
event_ctx = self.create_event_context("read-many")
354364
return self.repository.find_all(
355-
event_ctx, conditions=conditions, **kwargs
365+
event_ctx, only_active, conditions=conditions, **kwargs
356366
)
357367

358368
def get(self, id):

tests/time_tracker_api/activities/activities_namespace_test.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,15 @@ def test_list_all_activities(client: FlaskClient,
6161
'find_all',
6262
return_value=[])
6363

64-
response = client.get("/activities",
64+
response = client.get("/activities?only_active=1",
6565
headers=valid_header,
6666
follow_redirects=True)
6767

6868
assert HTTPStatus.OK == response.status_code
6969
json_data = json.loads(response.data)
7070
assert [] == json_data
7171

72-
repository_find_all_mock.assert_called_once_with(ANY, conditions={})
72+
repository_find_all_mock.assert_called_once_with(ANY, ANY, conditions={})
7373

7474

7575
def test_get_activity_should_succeed_with_valid_id(client: FlaskClient,
@@ -195,7 +195,7 @@ def test_delete_activity_should_succeed_with_valid_id(client: FlaskClient,
195195
valid_id = fake.random_int(1, 9999)
196196

197197
repository_remove_mock = mocker.patch.object(activity_dao.repository,
198-
'delete',
198+
'partial_update',
199199
return_value=None)
200200

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

205205
assert HTTPStatus.NO_CONTENT == response.status_code
206206
assert b'' == response.data
207-
repository_remove_mock.assert_called_once_with(str(valid_id), ANY)
207+
repository_remove_mock.assert_called_once_with(str(valid_id), {'status': 'inactive'}, ANY)
208208

209209

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

218218
repository_remove_mock = mocker.patch.object(activity_dao.repository,
219-
'delete',
219+
'partial_update',
220220
side_effect=NotFound)
221221

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

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

229229

230230
def test_delete_activity_should_return_422_for_invalid_id_format(client: FlaskClient,
@@ -236,12 +236,12 @@ def test_delete_activity_should_return_422_for_invalid_id_format(client: FlaskCl
236236
invalid_id = fake.company()
237237

238238
repository_remove_mock = mocker.patch.object(activity_dao.repository,
239-
'delete',
239+
'partial_update',
240240
side_effect=UnprocessableEntity)
241241

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

246246
assert HTTPStatus.UNPROCESSABLE_ENTITY == response.status_code
247-
repository_remove_mock.assert_called_once_with(str(invalid_id), ANY)
247+
repository_remove_mock.assert_called_once_with(str(invalid_id), {'status': 'inactive'}, ANY)

time_tracker_api/activities/activities_model.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class ActivityCosmosDBModel(CosmosDBModel):
3434
name: str
3535
description: str
3636
deleted: str
37+
status: str
3738
tenant_id: str
3839

3940
def __init__(self, data):

time_tracker_api/activities/activities_namespace.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,15 @@
2323
required=False,
2424
description='Comments about the activity',
2525
example=faker.paragraph(),
26-
)
26+
),
27+
'status': fields.String(
28+
required=False,
29+
title='Status',
30+
description='Status active or inactive activities',
31+
example=Faker().words(
32+
2, ['active', 'inactive',], unique=True
33+
),
34+
),
2735
})
2836

2937
activity_response_fields = {}
@@ -37,14 +45,28 @@
3745

3846
activity_dao = create_dao()
3947

48+
list_activities_attribs_parser = ns.parser()
49+
list_activities_attribs_parser.add_argument(
50+
'only_active',
51+
required=False,
52+
type=int,
53+
store_missing=False,
54+
help="(Filter) Permits to get a list of active or inactive activities.",
55+
location='args',
56+
)
57+
58+
ONLY_ACTIVE_TRUE_VALUE = 1
4059

4160
@ns.route('')
4261
class Activities(Resource):
4362
@ns.doc('list_activities')
4463
@ns.marshal_list_with(activity)
64+
@ns.expect(list_activities_attribs_parser)
4565
def get(self):
4666
"""List all activities"""
47-
return activity_dao.get_all()
67+
conditions = list_activities_attribs_parser.parse_args()
68+
only_active = conditions.get('only_active') == ONLY_ACTIVE_TRUE_VALUE
69+
return activity_dao.get_all(only_active)
4870

4971
@ns.doc('create_activity')
5072
@ns.response(HTTPStatus.CONFLICT, 'This activity already exists')
@@ -80,5 +102,5 @@ def put(self, id):
80102
@ns.response(HTTPStatus.NO_CONTENT, 'Activity deleted successfully')
81103
def delete(self, id):
82104
"""Delete an activity"""
83-
activity_dao.delete(id)
105+
activity_dao.update(id, {'status': 'inactive'})
84106
return None, HTTPStatus.NO_CONTENT

time_tracker_api/api.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,9 @@ def default_error_handler(error):
186186
HTTPStatus.INTERNAL_SERVER_ERROR,
187187
)
188188

189-
190189
@api.errorhandler(StopIteration)
191190
def handle_no_content(error):
192191
app.logger.error(error)
193-
return {'message': 'No Content'}, HTTPStatus.NO_CONTENT
192+
return {'message': 'No Content'}, HTTPStatus.NO_CONTENT
193+
194+

0 commit comments

Comments
 (0)