Skip to content

Commit c963c08

Browse files
committed
Use ORM for user delete
1 parent f5d16f3 commit c963c08

File tree

3 files changed

+52
-55
lines changed

3 files changed

+52
-55
lines changed

gefapi/routes/api/v1/users.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -639,14 +639,14 @@ def delete_profile():
639639
logger.info("[ROUTER]: Delete me")
640640
identity = current_user
641641
try:
642-
user = UserService.delete_user(str(identity.id))
642+
user_data = UserService.delete_user(str(identity.id))
643643
except UserNotFound as e:
644644
logger.error("[ROUTER]: " + e.message)
645645
return error(status=404, detail=e.message)
646646
except Exception as e:
647647
logger.error("[ROUTER]: " + str(e))
648648
return error(status=500, detail="Generic Error")
649-
return jsonify(data=user.serialize()), 200
649+
return jsonify(data=user_data), 200
650650

651651

652652
@endpoints.route(
@@ -979,14 +979,14 @@ def delete_user(user):
979979
if not can_delete_user(identity):
980980
return error(status=403, detail="Forbidden")
981981
try:
982-
user = UserService.delete_user(user)
982+
user_data = UserService.delete_user(user)
983983
except UserNotFound as e:
984984
logger.error("[ROUTER]: " + e.message)
985985
return error(status=404, detail=e.message)
986986
except Exception as e:
987987
logger.error("[ROUTER]: " + str(e))
988988
return error(status=500, detail="Generic Error")
989-
return jsonify(data=user.serialize()), 200
989+
return jsonify(data=user_data), 200
990990

991991

992992
@endpoints.route(

gefapi/services/user_service.py

Lines changed: 47 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -760,90 +760,86 @@ def delete_user(user_id):
760760
raise UserNotFound(
761761
message="User with ID " + str(user_id) + " does not exist"
762762
)
763+
764+
# Serialize user data before deletion
765+
user_data = user.serialize()
766+
763767
try:
764-
from sqlalchemy import text
768+
from sqlalchemy import String, cast
765769

766-
user_uuid = str(user.id)
770+
from gefapi.models import Execution, ExecutionLog, Script, StatusLog
771+
from gefapi.models.password_reset_token import PasswordResetToken
772+
from gefapi.models.refresh_token import RefreshToken
773+
from gefapi.models.script_log import ScriptLog
767774

768-
# Use PostgreSQL DELETE...USING for efficient JOIN-based deletes
769-
# This is faster than subqueries as PostgreSQL can use indexes
775+
user_uuid = user.id
770776

771-
# Delete status logs via JOIN (StatusLog uses String FK)
772-
logger.info("[DB]: Deleting status logs for user's executions")
773-
db.session.execute(
774-
text("""
775-
DELETE FROM status_log s
776-
USING execution e
777-
WHERE s.execution_id = CAST(e.id AS VARCHAR)
778-
AND e.user_id = :user_id
779-
"""),
780-
{"user_id": user_uuid},
777+
# Get execution IDs for this user (needed for related table deletions)
778+
execution_ids_uuid = db.session.query(Execution.id).filter(
779+
Execution.user_id == user_uuid
780+
)
781+
# Cast to string for StatusLog which uses String(36) for execution_id
782+
execution_ids_str = db.session.query(cast(Execution.id, String)).filter(
783+
Execution.user_id == user_uuid
781784
)
782785

783-
# Delete execution logs via JOIN (ExecutionLog uses UUID FK)
784-
logger.info("[DB]: Deleting execution logs for user's executions")
785-
db.session.execute(
786-
text("""
787-
DELETE FROM execution_log el
788-
USING execution e
789-
WHERE el.execution_id = e.id
790-
AND e.user_id = :user_id
791-
"""),
792-
{"user_id": user_uuid},
786+
# Get script IDs for this user (needed for script log deletions)
787+
script_ids_uuid = db.session.query(Script.id).filter(
788+
Script.user_id == user_uuid
793789
)
794790

791+
# Delete status logs for user's executions
792+
# StatusLog.execution_id is String(36), so use string-cast query
793+
logger.info("[DB]: Deleting status logs for user's executions")
794+
StatusLog.query.filter(
795+
StatusLog.execution_id.in_(execution_ids_str)
796+
).delete(synchronize_session=False)
797+
798+
# Delete execution logs (uses UUID foreign key)
799+
logger.info("[DB]: Deleting execution logs for user's executions")
800+
ExecutionLog.query.filter(
801+
ExecutionLog.execution_id.in_(execution_ids_uuid)
802+
).delete(synchronize_session=False)
803+
795804
# Delete executions
796805
logger.info("[DB]: Deleting executions")
797-
db.session.execute(
798-
text("DELETE FROM execution WHERE user_id = :user_id"),
799-
{"user_id": user_uuid},
806+
Execution.query.filter(Execution.user_id == user_uuid).delete(
807+
synchronize_session=False
800808
)
801809

802-
# Delete script logs via JOIN
810+
# Delete script logs for user's scripts
803811
logger.info("[DB]: Deleting script logs for user's scripts")
804-
db.session.execute(
805-
text("""
806-
DELETE FROM script_log sl
807-
USING script s
808-
WHERE sl.script_id = s.id
809-
AND s.user_id = :user_id
810-
"""),
811-
{"user_id": user_uuid},
812+
ScriptLog.query.filter(ScriptLog.script_id.in_(script_ids_uuid)).delete(
813+
synchronize_session=False
812814
)
813815

814816
# Delete scripts
815817
logger.info("[DB]: Deleting scripts")
816-
db.session.execute(
817-
text("DELETE FROM script WHERE user_id = :user_id"),
818-
{"user_id": user_uuid},
818+
Script.query.filter(Script.user_id == user_uuid).delete(
819+
synchronize_session=False
819820
)
820821

821822
# Delete password reset tokens
822823
logger.info("[DB]: Deleting password reset tokens")
823-
db.session.execute(
824-
text("DELETE FROM password_reset_token WHERE user_id = :user_id"),
825-
{"user_id": user_uuid},
826-
)
824+
PasswordResetToken.query.filter(
825+
PasswordResetToken.user_id == user_uuid
826+
).delete(synchronize_session=False)
827827

828828
# Delete refresh tokens
829829
logger.info("[DB]: Deleting refresh tokens")
830-
db.session.execute(
831-
text("DELETE FROM refresh_tokens WHERE user_id = :user_id"),
832-
{"user_id": user_uuid},
830+
RefreshToken.query.filter(RefreshToken.user_id == user_uuid).delete(
831+
synchronize_session=False
833832
)
834833

835834
# Now delete the user
836835
logger.info("[DB]: DELETE user")
837-
db.session.execute(
838-
text("DELETE FROM public.user WHERE id = :user_id"),
839-
{"user_id": user_uuid},
840-
)
836+
db.session.delete(user)
841837
db.session.commit()
842838
except Exception as error:
843839
db.session.rollback()
844840
rollbar.report_exc_info()
845841
raise error
846-
return user
842+
return user_data
847843

848844
@staticmethod
849845
def authenticate_user(email, password):

pytest.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ markers =
1616
auth: marks tests related to authentication
1717
admin: marks tests requiring admin privileges
1818
celery: marks tests related to Celery tasks
19+
standalone: marks tests as standalone (no database or Flask app needed)
1920
filterwarnings =
2021
ignore::DeprecationWarning
2122
ignore::PendingDeprecationWarning

0 commit comments

Comments
 (0)