Files
lab_ai/apps/update_delete.py

197 lines
6.3 KiB
Python
Raw Normal View History

2026-01-26 11:55:42 +00:00
from flask import current_app, jsonify, request
from helpers import CORS_HEADERS, db_row_to_dict, init_db_connection
from psycopg2 import IntegrityError
from pydantic_core import ValidationError
from schemas import AiUserUpdate
def main():
"""
```fission
{
"name": "ai-admin-update-delete-user",
"fntimeout": 300,
"http_triggers": {
"ai-admin-update-delete-user-http": {
2026-01-27 01:23:56 +07:00
"url": "/ailbl/ai/admin/users/{UserID}",
2026-01-26 11:55:42 +00:00
"methods": ["DELETE", "PUT"]
}
}
}
```
"""
try:
if request.method == "DELETE":
return make_delete_request()
elif request.method == "PUT":
return make_update_request()
else:
return {"error": "Method not allow"}, 405, CORS_HEADERS
except Exception as err:
print(f"ErrorType={type(err)}")
return {"error": str(err)}, 500, CORS_HEADERS
def make_update_request():
2026-01-27 01:23:56 +07:00
r"""make_update_request() -> tuple[Response, int, dict]
Update an existing user by ID.
Retrieves the user ID from ``X-Fission-Params-UserID`` header, validates
the request body using :class:`AiUserUpdate` schema, and performs a
partial update on the user record.
Uses row-level locking (``SELECT ... FOR UPDATE``) to prevent concurrent
modification conflicts.
Returns:
tuple: A tuple containing:
- JSON response with updated user data or error details
- HTTP status code (200 on success, 400/404/409 on error)
- CORS headers dict
Raises:
ValidationError: If request body fails Pydantic validation (returns 400).
IntegrityError: If email conflicts with another user (returns 409).
Example::
>>> # PUT /ai/admin/users/550e8400-e29b-41d4-a716-446655440000
>>> # Header: X-Fission-Params-UserID: 550e8400-e29b-41d4-a716-446655440000
>>> # Body: {"name": "Jane Doe"}
>>> # Response: 200 OK
>>> {
... "id": "550e8400-e29b-41d4-a716-446655440000",
... "name": "Jane Doe",
... "email": "john@example.com",
... "modified": "2024-01-02T10:00:00"
... }
"""
2026-01-26 11:55:42 +00:00
user_id = request.headers.get("X-Fission-Params-UserID")
if not user_id:
return jsonify({"errorCode": "MISSING_USER_ID"}), 400, CORS_HEADERS
try:
body = AiUserUpdate(**(request.get_json(silent=True) or {}))
except ValidationError as e:
return (
jsonify({"error": "Validation failed", "details": e.errors()}),
400,
CORS_HEADERS,
)
conn = None
try:
conn = init_db_connection()
with conn:
with conn.cursor() as cur:
cur.execute(
"SELECT * FROM ai_user WHERE id=%s FOR UPDATE", (user_id,))
row = cur.fetchone()
if not row:
return jsonify({"errorCode": "USER_NOT_FOUND"}), 404, CORS_HEADERS
sets, params = [], {"id": user_id}
if body.name is not None:
sets.append("name=%(name)s")
params["name"] = body.name
if body.email is not None:
sets.append("email=%(email)s")
params["email"] = body.email
if body.dob is not None:
sets.append("dob=%(dob)s")
params["dob"] = body.dob
if body.gender is not None:
sets.append("gender=%(gender)s")
params["gender"] = body.gender
sets.append("modified=CURRENT_TIMESTAMP")
cur.execute(
f"UPDATE ai_user SET {', '.join(sets)} WHERE id=%(id)s RETURNING *",
params,
)
updated = db_row_to_dict(cur, cur.fetchone())
return jsonify(updated), 200, CORS_HEADERS
except IntegrityError as e:
return (
jsonify({"errorCode": "DUPLICATE_USER", "details": str(e)}),
409,
CORS_HEADERS,
)
finally:
if conn:
conn.close()
def __delete_user(cursor, id: str):
2026-01-27 01:23:56 +07:00
r"""Delete a user from the database by ID.
Args:
cursor: Database cursor object for executing queries.
id (str): UUID of the user to delete.
Returns:
dict | str: User data dict if deleted successfully,
or ``"USER_NOT_FOUND"`` string if user doesn't exist.
Note:
This is a private function. Use :func:`make_delete_request` instead.
"""
2026-01-26 11:55:42 +00:00
cursor.execute("SELECT 1 FROM ai_user WHERE id = %(id)s", {"id": id})
if not cursor.fetchone():
return "USER_NOT_FOUND"
cursor.execute("DELETE FROM ai_user WHERE id = %(id)s RETURNING *", {"id": id})
row = cursor.fetchone()
return db_row_to_dict(cursor, row)
def make_delete_request():
2026-01-27 01:23:56 +07:00
r"""make_delete_request() -> tuple[Response, int, dict]
Delete a user by ID.
Retrieves the user ID from ``X-Fission-Params-UserID`` header and
deletes the user from the database if found.
2026-01-26 11:55:42 +00:00
2026-01-27 01:23:56 +07:00
Returns:
tuple: A tuple containing:
- JSON response with deleted user data or error details
- HTTP status code (200 on success, 400/404/500 on error)
- CORS headers dict (may be omitted on some error responses)
Example::
>>> # DELETE /ai/admin/users/550e8400-e29b-41d4-a716-446655440000
>>> # Header: X-Fission-Params-UserID: 550e8400-e29b-41d4-a716-446655440000
>>> # Response: 200 OK
>>> {
... "id": "550e8400-e29b-41d4-a716-446655440000",
... "name": "John Doe",
... "email": "john@example.com"
... }
"""
2026-01-26 11:55:42 +00:00
user_id = request.headers.get("X-Fission-Params-UserID")
if not user_id:
return jsonify({"errorCode": "MISSING_USER_ID"}), 400, CORS_HEADERS
conn = None
try:
conn = init_db_connection()
with conn.cursor() as cursor:
result = __delete_user(cursor, id=user_id)
if result == "USER_NOT_FOUND":
return jsonify({"errorCode": "USER_NOT_FOUND"}), 404
conn.commit()
return jsonify(result), 200
except Exception as ex:
return jsonify({"error": str(ex)}), 500
finally:
if conn is not None:
conn.close()
current_app.logger.info("Close DB connection")