Skip to content

Commit 5fd0c39

Browse files
Merge pull request #560 from ayobi/master-overhaul
added admin delete reason
2 parents 1d4bb5d + cf5f46c commit 5fd0c39

9 files changed

+109
-33
lines changed

microsetta_private_api/admin/admin_impl.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -886,15 +886,16 @@ def ignore_removal_request(account_id, token_info):
886886
try:
887887
# remove the user from the queue, noting the admin who allowed it
888888
# and the time the action was performed.
889-
rq_repo.update_queue(account_id, token_info['email'], 'ignored')
889+
rq_repo.update_queue(account_id, token_info['email'],
890+
'ignored', None)
890891
t.commit()
891892
except RepoException as e:
892893
raise e
893894

894895
return None, 204
895896

896897

897-
def allow_removal_request(account_id, token_info):
898+
def allow_removal_request(account_id, token_info, delete_reason):
898899
validate_admin_access(token_info)
899900

900901
with Transaction() as t:
@@ -903,7 +904,8 @@ def allow_removal_request(account_id, token_info):
903904
try:
904905
# remove the user from the queue, noting the admin who allowed it
905906
# and the time the action was performed.
906-
rq_repo.update_queue(account_id, token_info['email'], 'deleted')
907+
rq_repo.update_queue(account_id, token_info['email'],
908+
'deleted', delete_reason)
907909
t.commit()
908910
except RepoException as e:
909911
raise e

microsetta_private_api/api/_removal_queue.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ def check_request_remove_account(account_id, token_info):
1515
return jsonify(result), 200
1616

1717

18-
def request_remove_account(account_id, token_info):
18+
def request_remove_account(account_id, token_info, user_delete_reason=None):
1919
# raises 401 if method fails
2020
_validate_account_access(token_info, account_id)
2121

2222
with Transaction() as t:
2323
rq_repo = RemovalQueueRepo(t)
24-
rq_repo.request_remove_account(account_id)
24+
rq_repo.request_remove_account(account_id, user_delete_reason)
2525
t.commit()
2626

2727
return jsonify(code=200, message="Request Accepted"), 200

microsetta_private_api/api/microsetta_private_api.yaml

+17
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,12 @@ paths:
184184
description: Request account to be removed
185185
parameters:
186186
- $ref: '#/components/parameters/account_id'
187+
- name: user_delete_reason
188+
in: query
189+
description: Reason for account deletion
190+
required: false
191+
schema:
192+
type: string
187193
responses:
188194
'200':
189195
description: Successfully requested for account to be removed
@@ -199,6 +205,12 @@ paths:
199205
description: Cancel request for account to be removed
200206
parameters:
201207
- $ref: '#/components/parameters/account_id'
208+
- name: user_delete_reason
209+
in: query
210+
description: Reason for account deletion
211+
required: false
212+
schema:
213+
type: string
202214
responses:
203215
'200':
204216
description: Successfully canceled request
@@ -2504,6 +2516,11 @@ paths:
25042516
- Admin
25052517
parameters:
25062518
- $ref: '#/components/parameters/account_id'
2519+
- name: delete_reason
2520+
in: query
2521+
required: true
2522+
schema:
2523+
type: string
25072524
responses:
25082525
'200':
25092526
description: Updates queue, log before calling delete_account()

microsetta_private_api/api/tests/test_api.py

+8-4
Original file line numberDiff line numberDiff line change
@@ -1425,6 +1425,7 @@ def test_request_account_removal(self):
14251425
self.assertFalse(json.loads(response.data)['status'])
14261426

14271427
# submit a request for this account to be removed.
1428+
delete_reason = "User requested account removal"
14281429
response = self.client.put(
14291430
f'/api/accounts/{dummy_acct_id}/removal_queue',
14301431
headers=self.dummy_auth)
@@ -1440,8 +1441,9 @@ def test_request_account_removal(self):
14401441
headers=self.dummy_auth)
14411442

14421443
self.assertEqual(200, response.status_code)
1443-
1444-
self.assertTrue(json.loads(response.data)['status'])
1444+
removal_info = json.loads(response.data)
1445+
self.assertTrue(removal_info['status'])
1446+
# self.assertEqual(removal_info['delete_reason'], delete_reason)
14451447

14461448
# try to request a second time. Verify that an error is returned
14471449
# instead.
@@ -1491,7 +1493,8 @@ def test_request_account_removal(self):
14911493
"Request Accepted")
14921494

14931495
response = self.client.put(
1494-
f'/api/admin/account_removal/{dummy_acct_id}',
1496+
f'/api/admin/account_removal/{dummy_acct_id}'
1497+
f'?delete_reason={delete_reason}',
14951498
headers=make_headers(FAKE_TOKEN_ADMIN))
14961499

14971500
self.assertEqual(204, response.status_code)
@@ -1557,7 +1560,8 @@ def test_request_account_removal(self):
15571560
# functionality; it deletes the id from the delete-queue and logs the
15581561
# deletion in a separate 'log' table, before calling account_delete().
15591562
response = self.client.delete(
1560-
f'/api/admin/account_removal/{dummy_acct_id}',
1563+
f'/api/admin/account_removal/{dummy_acct_id}'
1564+
f'?delete_reason={delete_reason}',
15611565
headers=make_headers(FAKE_TOKEN_ADMIN))
15621566

15631567
# confirm that the operation was a success.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- Feb 5, 2024
2+
-- Add delete_reason to ag.account_removal_log
3+
ALTER TABLE ag.account_removal_log
4+
ADD COLUMN delete_reason VARCHAR;
5+
6+
COMMENT ON COLUMN ag.account_removal_log.delete_reason
7+
IS 'Reason the admin gave for deleting the account.';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- Feb 12, 2024
2+
-- Add user_delete_reason to ag.delete_account_queue
3+
ALTER TABLE ag.delete_account_queue
4+
ADD COLUMN user_delete_reason VARCHAR;
5+
6+
COMMENT ON COLUMN ag.delete_account_queue.user_delete_reason
7+
IS 'Reason the user gave for deleting the account.';

microsetta_private_api/model/removal_queue_requests.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
class RemovalQueueRequest(ModelBase):
55
def __init__(self, id, account_id, email, first_name, last_name,
6-
requested_on):
6+
requested_on, user_delete_reason):
77
self.id = id
88
self.account_id = account_id
99
self.email = email
@@ -12,6 +12,7 @@ def __init__(self, id, account_id, email, first_name, last_name,
1212

1313
# 2022-07-27 17:15:33.937458-07:00 -> 2022-07-27 17:15:33
1414
self.requested_on = str(requested_on).split('.')[0]
15+
self.user_delete_reason = user_delete_reason
1516

1617
def to_api(self):
1718
return self.__dict__.copy()

microsetta_private_api/repo/removal_queue_repo.py

+42-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
from microsetta_private_api.repo.base_repo import BaseRepo
22
from microsetta_private_api.exceptions import RepoException
3+
from microsetta_private_api.model.removal_queue_requests \
4+
import RemovalQueueRequest
35

46

57
class RemovalQueueRepo(BaseRepo):
@@ -14,6 +16,32 @@ def _check_account_is_admin(self, admin_email):
1416

1517
return False if count == 0 else True
1618

19+
def _row_to_removal(self, r):
20+
return RemovalQueueRequest(r['id'], r['account_id'], r['email'],
21+
r['first_name'], r['last_name'],
22+
r['requested_on'], r['user_delete_reason'])
23+
24+
def get_all_account_removal_requests(self):
25+
with self._transaction.dict_cursor() as cur:
26+
cur.execute("""
27+
SELECT
28+
ag.delete_account_queue.id,
29+
ag.delete_account_queue.account_id,
30+
ag.delete_account_queue.requested_on,
31+
ag.delete_account_queue.user_delete_reason,
32+
ag.account.first_name,
33+
ag.account.last_name,
34+
ag.account.email
35+
FROM
36+
ag.account
37+
JOIN
38+
ag.delete_account_queue ON ag.account.id
39+
= ag.delete_account_queue.account_id
40+
""")
41+
rows = cur.fetchall()
42+
43+
return [self._row_to_removal(r) for r in rows]
44+
1745
def check_request_remove_account(self, account_id):
1846
with self._transaction.cursor() as cur:
1947
cur.execute("SELECT count(id) FROM delete_account_queue WHERE "
@@ -22,7 +50,7 @@ def check_request_remove_account(self, account_id):
2250

2351
return False if count == 0 else True
2452

25-
def request_remove_account(self, account_id):
53+
def request_remove_account(self, account_id, user_delete_reason):
2654
with self._transaction.cursor() as cur:
2755
cur.execute("SELECT account_id from delete_account_queue where "
2856
"account_id = %s", (account_id,))
@@ -31,9 +59,13 @@ def request_remove_account(self, account_id):
3159
if result is not None:
3260
raise RepoException("Account is already in removal queue")
3361

62+
user_delete_reason_value = user_delete_reason \
63+
if user_delete_reason else None
64+
3465
cur.execute(
35-
"INSERT INTO delete_account_queue (account_id) VALUES (%s)",
36-
(account_id,))
66+
"INSERT INTO delete_account_queue (account_id, "
67+
"user_delete_reason) VALUES (%s, %s)",
68+
(account_id, user_delete_reason_value))
3769

3870
def cancel_request_remove_account(self, account_id):
3971
if not self.check_request_remove_account(account_id):
@@ -43,7 +75,8 @@ def cancel_request_remove_account(self, account_id):
4375
cur.execute("DELETE FROM delete_account_queue WHERE account_id ="
4476
" %s", (account_id,))
4577

46-
def update_queue(self, account_id, admin_email, disposition):
78+
def update_queue(self, account_id, admin_email,
79+
disposition, delete_reason):
4780
if not self.check_request_remove_account(account_id):
4881
raise RepoException("Account is not in removal queue")
4982

@@ -69,9 +102,11 @@ def update_queue(self, account_id, admin_email, disposition):
69102
# add an entry to the log detailing who reviewed the account
70103
# and when.
71104
cur.execute("INSERT INTO account_removal_log (account_id, "
72-
"admin_id, disposition, requested_on) VALUES (%s,"
73-
" %s, %s, %s)", (account_id, admin_id, disposition,
74-
requested_on))
105+
"admin_id, disposition, requested_on, delete_reason) "
106+
"VALUES (%s, %s, %s, %s, %s)", (account_id,
107+
admin_id, disposition,
108+
requested_on,
109+
delete_reason))
75110

76111
# delete the entry from queue. Note that reviewed entries are
77112
# deleted from the queue whether or not they were approved

microsetta_private_api/repo/tests/test_removal_queue_repo.py

+19-16
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ def test_check_request_remove_account(self):
9595

9696
# use request_remove_account() to push the valid account onto
9797
# the queue.
98-
rqr.request_remove_account(self.acc.id)
98+
rqr.request_remove_account(self.acc.id, 'delete reason')
9999

100100
# assume request_remove_account() succeeded.
101101
# check_request_remove_account() should return True.
@@ -120,7 +120,7 @@ def test_request_remove_account(self):
120120
rqr = RemovalQueueRepo(t)
121121
# use request_remove_account() to push the valid account onto
122122
# the queue.
123-
rqr.request_remove_account(self.acc.id)
123+
rqr.request_remove_account(self.acc.id, 'delete reason')
124124

125125
# assume check_request_remove_account() works correctly.
126126
# verify account is now in the queue.
@@ -131,20 +131,21 @@ def test_request_remove_account_failure(self):
131131
rqr = RemovalQueueRepo(t)
132132

133133
# remove a valid account twice
134-
rqr.request_remove_account(self.acc.id)
134+
rqr.request_remove_account(self.acc.id, 'delete reason')
135135
with self.assertRaises(RepoException):
136-
rqr.request_remove_account(self.acc.id)
136+
rqr.request_remove_account(self.acc.id, 'delete reason')
137137

138138
# remove a non-existant id.
139139
with self.assertRaises(ForeignKeyViolation):
140-
rqr.request_remove_account(RemovalQueueTests.bad_id)
140+
rqr.request_remove_account(RemovalQueueTests.bad_id,
141+
'delete reason')
141142

142143
def test_cancel_request_remove_account(self):
143144
with Transaction() as t:
144145
rqr = RemovalQueueRepo(t)
145146
# use request_remove_account() to push the valid account onto
146147
# the queue.
147-
rqr.request_remove_account(self.acc.id)
148+
rqr.request_remove_account(self.acc.id, 'delete reason')
148149

149150
# assume check_request_remove_account() works correctly.
150151
# verify account is now in the queue.
@@ -177,7 +178,7 @@ def test_cancel_request_remove_account_failure(self):
177178

178179
# use request_remove_account() to push the valid account onto
179180
# the queue.
180-
rqr.request_remove_account(self.acc.id)
181+
rqr.request_remove_account(self.acc.id, 'delete reason')
181182

182183
# cancel the request to delete the account twice.
183184
rqr.cancel_request_remove_account(self.acc.id)
@@ -189,12 +190,13 @@ def test_update_queue_success(self):
189190
rqr = RemovalQueueRepo(t)
190191

191192
# push the standard account onto the queue.
192-
rqr.request_remove_account(self.acc.id)
193+
rqr.request_remove_account(self.acc.id, 'delete reason')
193194

194195
# update_queue should migrate the relevant information to the
195196
# ag.account_removal_log table and delete the entry from the
196197
# queue table.
197-
rqr.update_queue(self.acc.id, self.adm.email, 'deleted')
198+
rqr.update_queue(self.acc.id, self.adm.email, 'deleted',
199+
'delete reason')
198200

199201
# confirm that the account id is no longer in the queue table.
200202
self.assertFalse(rqr.check_request_remove_account(self.acc.id))
@@ -205,16 +207,17 @@ def test_update_queue_success(self):
205207
with Transaction() as t:
206208
with t.cursor() as cur:
207209
cur.execute("SELECT account_id, admin_id, disposition, "
208-
"requested_on, reviewed_on FROM "
210+
"requested_on, reviewed_on, delete_reason FROM "
209211
"ag.account_removal_log")
210212
rows = cur.fetchall()
211213
self.assertEqual(len(rows), 1)
212214
for account_id, admin_id, disposition, requested_on,\
213-
reviewed_on in rows:
215+
reviewed_on, delete_reason in rows:
214216
# note this loop should only execute once.
215217
self.assertEqual(account_id, self.acc.id)
216218
self.assertEqual(admin_id, self.adm.id)
217219
self.assertEqual(disposition, 'deleted')
220+
self.assertEqual(delete_reason, 'delete reason')
218221
now = datetime.datetime.now().timestamp()
219222
# the requested_on time should be not far in the past.
220223
# assume it is not NULL and is less than a minute ago.
@@ -227,23 +230,23 @@ def test_update_queue_failure(self):
227230
rqr = RemovalQueueRepo(t)
228231

229232
with self.assertRaises(InvalidTextRepresentation):
230-
rqr.update_queue('XXXX', self.adm.email, 'ignored')
233+
rqr.update_queue('XXXX', self.adm.email, 'ignored', None)
231234

232235
with Transaction() as t:
233236
rqr = RemovalQueueRepo(t)
234237

235238
# push the standard account onto the queue.
236-
rqr.request_remove_account(self.acc.id)
239+
rqr.request_remove_account(self.acc.id, 'delete reason')
237240

238241
# ensure that an Error is raised when an invalid admin
239242
# email address is passed.
240243
with self.assertRaises(RepoException):
241-
rqr.update_queue(self.acc.id, 'XXXX', 'ignored')
244+
rqr.update_queue(self.acc.id, 'XXXX', 'ignored', None)
242245

243246
# ensure that an Error is raised when disposition is None or
244247
# emptry string.
245248
with self.assertRaises(RepoException):
246-
rqr.update_queue(self.acc.id, self.adm.email, None)
249+
rqr.update_queue(self.acc.id, self.adm.email, None, None)
247250

248251
with self.assertRaises(RepoException):
249-
rqr.update_queue(self.acc.id, self.adm.email, '')
252+
rqr.update_queue(self.acc.id, self.adm.email, '', None)

0 commit comments

Comments
 (0)