user creation and deletetion endpoint
Some checks failed
Build and deploy the backend to staging / Build and push image (pull_request) Successful in 1m49s
Run linting on the backend code / Build (pull_request) Successful in 46s
Run testing on the backend code / Build (pull_request) Failing after 48s
Build and release debug APK / Build APK (pull_request) Failing after 3m35s
Build and deploy the backend to staging / Deploy to staging (pull_request) Successful in 26s

This commit is contained in:
Helldragon67 2025-02-14 16:47:37 +01:00
parent 361b2b1f42
commit f81c28f2ac
7 changed files with 237 additions and 74 deletions

File diff suppressed because one or more lines are too long

View File

@ -18,7 +18,6 @@ PAYPAL_API_URL = "https://api-m.sandbox.paypal.com"
SUPABASE_URL = os.getenv("SUPABASE_URL", None)
SUPABASE_KEY = os.getenv("SUPABASE_API_KEY", None)
SUPABASE_TEST_USER_ID = os.getenv("SUPABASE_TEST_USER_ID", None)
cache_dir_string = os.getenv('OSM_CACHE_DIR', './cache')

View File

@ -11,6 +11,7 @@ from .structs.landmark import Landmark, Toilets
from .structs.preferences import Preferences
from .structs.linked_landmarks import LinkedLandmarks
from .structs.trip import Trip
from .structs.requests import UserDeleteRequest
from .utils.landmarks_manager import LandmarkManager
from .utils.toilets_manager import ToiletsManager
from .optimization.optimizer import Optimizer
@ -62,15 +63,16 @@ def new_trip(user_id: str = Body(...),
(uuid) : The uuid of the first landmark in the optimized route
"""
# Check for valid user balance.
logger.debug(f'user id: {user_id}')
try :
if not supabase.check_balance(user_id=user_id) :
try:
if not supabase.check_balance(user_id=user_id):
logger.warning('Insufficient credits to perform this action.')
return None
except Exception as exc :
traceback.print_exc()
raise HTTPException(status_code=500, detail=str(exc)) from exc
return {"error": "Insufficient credits"}, 400 # Return a 400 Bad Request with an appropriate message
except SyntaxError as se :
raise HTTPException(status_code=400, detail=str(se)) from se
except ValueError as ve :
raise HTTPException(status_code=406, detail=str(ve)) from ve
except Exception as exc:
raise HTTPException(status_code=500, detail=f"Internal Server Error: {str(exc)}") from exc
# Check for invalid input.
if preferences is None:
@ -266,3 +268,46 @@ def get_toilets(location: tuple[float, float] = Query(...), radius: int = 500) -
return toilets_list
except KeyError as exc:
raise HTTPException(status_code=404, detail="No toilets found") from exc
@app.post("/user/create")
def register_user(email: str = Body(...), password: str = Body(...)) -> str:
try:
response = supabase.supabase.auth.admin.create_user({
"email": email,
"password": password
})
except Exception as e:
if e.code == 'email_exists' :
logger.error(f"Failed to create user : {str(e.code)}")
raise HTTPException(status_code=422, detail=str(e)) from e
logger.error(f"Failed to create user : {str(e.code)}")
raise HTTPException(status_code=500, detail=str(e)) from e
logger.debug(response)
# Extract the identity_id and user_id
# identity = response.user.id # Accessing the identity
user_id = response.user.id
logger.info(f"User created successfully, ID: {user_id}")
return user_id
@app.post("/user/delete")
def delete_user(request: UserDeleteRequest):
try:
response = supabase.supabase.auth.admin.delete_user(request.user_id)
logger.debug(response)
except Exception as e:
if e.code == 'user_not_found' :
logger.error(f"Failed to delete user : {str(e.code)}")
raise HTTPException(status_code=404, detail=str(e)) from e
logger.error(f"Failed to create user : {str(e.code)}")
raise HTTPException(status_code=500, detail=str(e)) from e
logger.info(f"User with ID {request.user_id} deleted successfully")

View File

@ -21,10 +21,14 @@ class Supabase:
with open(os.path.join(PARAMETERS_DIR, 'secrets.yaml')) as f:
secrets = yaml.safe_load(f)
self.SUPABASE_URL = secrets['SUPABASE_URL']
self.SUPABASE_KEY = secrets['SUPABASE_API_KEY']
self.SUPABASE_ADMIN_KEY = secrets['SUPABASE_ADMIN_KEY']
self.SUPABASE_TEST_USER_ID = secrets['SUPABASE_TEST_USER_ID']
self.supabase: Client = create_client(self.SUPABASE_URL, self.SUPABASE_KEY, options=ClientOptions(schema='public'))
self.supabase = create_client(
self.SUPABASE_URL,
self.SUPABASE_ADMIN_KEY,
options=ClientOptions(schema='public')
)
self.logger.debug('Supabase client initialized.')
@ -47,24 +51,31 @@ class Supabase:
.single()
.execute()
)
# Check if the response was successful and contains data
if response.data :
credits = response.data['credit_amount']
self.logger.debug(f'Credits of user {user_id}: {credits}')
# self.logger.critical(response)
if credits > 0:
self.logger.info(f'Credit balance is positive. Proceeding with trip generation')
return True
except Exception as e:
if e.code == '22P02' :
self.logger.error(f"Failed querying credits : {str(e)}")
raise SyntaxError(f"Failed querying credits : {str(e)}") from e
if e.code == 'PGRST116' :
self.logger.error(f"User not found : {str(e)}")
raise ValueError(f"User not found : {str(e)}") from e
else :
self.logger.error(f"An unexpected error occured while checking user balance : {str(e)}")
raise Exception(f"An unexpected error occured while checking user balance : {str(e)}") from e
self.logger.warning(f'Insufficient balance. Trip generation cannot be granted')
return False
# Proceed to check the user's credit balance
credits = response.data['credit_amount']
self.logger.debug(f'Credits of user {user_id}: {credits}')
# If user or credits not found, raise a 404 error
raise HTTPException(status_code=404, detail="User data not found.")
if credits > 0:
self.logger.info(f'Credit balance is positive for user {user_id}. Proceeding with trip generation.')
return True
except Exception as exc:
raise HTTPException(status_code=500, detail="Error retrieving credit balance") from exc
self.logger.warning(f'Insufficient balance for user {user_id}. Trip generation cannot proceed.')
return False
def decrement_credit_balance(self, user_id: str) -> bool:
"""
@ -82,32 +93,36 @@ class Supabase:
.single()
.execute()
)
# Check if the response was successful and contains data
if response.data :
current_credits = response.data['credit_amount']
updated_credits = current_credits - 1
except Exception as e:
if e.code == '22P02' :
self.logger.error(f"Failed decrementing credits : {str(e)}")
raise SyntaxError(f"Failed decrementing credits : {str(e)}") from e
if e.code == 'PGRST116' :
self.logger.error(f"User not found : {str(e)}")
raise ValueError(f"User not found : {str(e)}") from e
else :
self.logger.error(f"An unexpected error occured while decrementing user balance : {str(e)}")
raise Exception(f"An unexpected error occured while decrementing user balance : {str(e)}") from e
# Update the user's credits in the table
update_response = (
self.supabase.table('credits')
.update({'credit_amount': updated_credits})
.eq('id', user_id)
.execute()
)
# Check if the update was successful
if update_response.data:
self.logger.debug(f'Credit balance successfully decremented.')
return True
else:
raise HTTPException(status_code=500, detail="Error decrementing credit balance.")
current_credits = response.data['credit_amount']
updated_credits = current_credits - 1
# If user or credits not found, raise a 404 error
raise HTTPException(status_code=404, detail="User data not found.")
# Update the user's credits in the table
update_response = (
self.supabase.table('credits')
.update({'credit_amount': updated_credits})
.eq('id', user_id)
.execute()
)
# Check if the update was successful
if update_response.data:
self.logger.debug(f'Credit balance successfully decremented.')
return True
else:
raise Exception("Error decrementing credit balance.")
except Exception:
# Handle exceptions (like if the user ID doesn't exist or there are database issues)
raise HTTPException(status_code=500, detail="Error decrementing credit balance")
def increment_credit_balance(self, user_id: str) -> bool:
"""
@ -125,29 +140,32 @@ class Supabase:
.single()
.execute()
)
# Check if the response was successful and contains data
if response.data :
current_credits = response.data['credit_amount']
updated_credits = current_credits + 1
except Exception as e:
if e.code == '22P02' :
self.logger.error(f"Failed incrementing credits : {str(e)}")
raise SyntaxError(f"Failed incrementing credits : {str(e)}") from e
if e.code == 'PGRST116' :
self.logger.error(f"User not found : {str(e)}")
raise ValueError(f"User not found : {str(e)}") from e
else :
self.logger.error(f"An unexpected error occured while incrementing user balance : {str(e)}")
raise Exception(f"An unexpected error occured while incrementing user balance : {str(e)}") from e
# Update the user's credits in the table
update_response = (
self.supabase.table('credits')
.update({'credit_amount': updated_credits})
.eq('id', user_id)
.execute()
)
# Check if the update was successful
if update_response.data:
self.logger.debug(f'Credit balance successfully incremented.')
return True
else:
raise HTTPException(status_code=500, detail="Error incrementing credit balance.")
current_credits = response.data['credit_amount']
updated_credits = current_credits + 1
# If user or credits not found, raise a 404 error
raise HTTPException(status_code=404, detail="User data not found.")
# Update the user's credits in the table
update_response = (
self.supabase.table('credits')
.update({'credit_amount': updated_credits})
.eq('id', user_id)
.execute()
)
except Exception:
# Handle exceptions (like if the user ID doesn't exist or there are database issues)
raise HTTPException(status_code=500, detail="Error incrementing credit balance")
# Check if the update was successful
if update_response.data:
self.logger.debug(f'Credit balance successfully decremented.')
return True
else:
raise Exception("Error decrementing credit balance.")

View File

@ -0,0 +1,4 @@
from pydantic import BaseModel
class UserDeleteRequest(BaseModel):
user_id: str

View File

@ -62,3 +62,32 @@ def test_input(invalid_client, start, preferences, status_code): # pylint: dis
}
)
assert response.status_code == status_code
@pytest.mark.parametrize(
"user_id,status_code",
[
# No user id :
({}, 422),
("invalid_user_id", 400),
# ("12345678-1234-5678-1234-567812345678", 406)
]
)
def test_input(invalid_client, user_id, status_code): # pylint: disable=redefined-outer-name
"""
Test new trip creation with invalid user ID.
"""
response = invalid_client.post(
"/trip/new",
json={
"user_id": user_id,
"preferences": {"sightseeing": {"type": "sightseeing", "score": 5},
"nature": {"type": "nature", "score": 0},
"shopping": {"type": "shopping", "score": 0},
"max_time_minute": 20,
"detour_tolerance_minute": 0},
"start": [48.084588, 7.280405]
}
)
assert response.status_code == status_code

View File

@ -0,0 +1,68 @@
"""Collection of tests to ensure correct handling of user data."""
from fastapi.testclient import TestClient
import pytest
from ..main import app
TEST_EMAIL = "dummy@example.com"
TEST_PW = "DummyPassword123"
@pytest.fixture(scope="module")
def client():
"""Client used to call the app."""
return TestClient(app)
def test_user_handling(client) :
"""
Test the creation of a new user.
"""
# Create a new user
response = client.post(
"/user/create",
json={
"email": TEST_EMAIL,
"password": TEST_PW
}
)
# Verify user has been created
assert response.status_code == 200
user_id = response.json()
# # Create same user again to raise an error
response = client.post(
"/user/create",
json={
"email": TEST_EMAIL,
"password": TEST_PW
}
)
# Verify user already exists
assert response.status_code == 422
# Delete the user.
response = client.post(
"/user/delete",
json={
"user_id": user_id
}
)
print(response)
# Verify user has been deleted
assert response.status_code == 200, "Failed to delete dummy user."
# Delete the user again to raise an error
response = client.post(
"/user/delete",
json={
"user_id": user_id
}
)
print(response)
# Verify user has been deleted
assert response.status_code == 404, "Failed to delete dummy user."