diff --git a/api/src/main.py b/api/src/main.py index d719c0e..4a26e5a 100644 --- a/api/src/main.py +++ b/api/src/main.py @@ -1,6 +1,6 @@ import logging -from fastapi import Depends, FastAPI, HTTPException +from fastapi import Body, Depends, FastAPI, HTTPException from src import supabase_client from src.auth_manager import validate_user from src.types.training_week import TrainingWeek @@ -27,3 +27,24 @@ async def training_week(user: UserRow = Depends(validate_user)): except ValueError as e: logger.error(f"Error retrieving training week: {e}", exc_info=True) raise HTTPException(status_code=404, detail=str(e)) + + +@app.post("/device_token/") +async def update_device_token( + device_token: str = Body(..., embed=True), user: UserRow = Depends(validate_user) +) -> dict: + """ + Update device token for push notifications + + :param device_token: The device token to register + :param user: The authenticated user + :return: Success status + """ + try: + supabase_client.update_user_device_token( + athlete_id=user.athlete_id, device_token=device_token + ) + return {"success": True} + except Exception as e: + logger.error(f"Failed to update device token: {e}", exc_info=True) + raise HTTPException(status_code=400, detail=str(e)) diff --git a/api/src/supabase_client.py b/api/src/supabase_client.py index 6e841ab..8cf0aee 100644 --- a/api/src/supabase_client.py +++ b/api/src/supabase_client.py @@ -104,3 +104,15 @@ def upsert_user_auth(user_auth_row: UserAuthRow) -> None: table = client.table("user_auth") table.upsert(user_auth_row, on_conflict="athlete_id").execute() + + +def update_user_device_token(athlete_id: str, device_token: str) -> None: + """ + Update the device token for a user in the database. + + :param athlete_id: The athlete's ID + :param device_token: The device token for push notifications + """ + client.table("user_auth").update({"device_token": device_token}).eq( + "athlete_id", athlete_id + ).execute() diff --git a/api/tests/test_get_training_week.py b/api/tests/test_get_training_week.py index 5a5c2be..1948d97 100644 --- a/api/tests/test_get_training_week.py +++ b/api/tests/test_get_training_week.py @@ -17,3 +17,14 @@ def test_get_training_week_success(): ) assert TrainingWeek(**response.json()) assert response.status_code == 200 + + +def test_update_device_token_success(): + """Test successful update of device token""" + user_auth = get_user_auth(os.environ["JAMIES_ATHLETE_ID"]) + response = client.post( + "/device_token/", + json={"device_token": user_auth.device_token}, + headers={"Authorization": f"Bearer {user_auth.jwt_token}"}, + ) + assert response.status_code == 200 diff --git a/lambda/src/frontend_router.py b/lambda/src/frontend_router.py index 9c04d81..3a956aa 100644 --- a/lambda/src/frontend_router.py +++ b/lambda/src/frontend_router.py @@ -82,7 +82,6 @@ def update_device_token_handler(athlete_id: str, payload: dict) -> dict: "update_preferences": update_preferences_handler, "get_weekly_summaries": get_weekly_summaries_handler, "start_onboarding": start_onboarding, - "update_device_token": update_device_token_handler, } diff --git a/mobile/mobile/APIManager.swift b/mobile/mobile/APIManager.swift index d51081f..0f433fb 100644 --- a/mobile/mobile/APIManager.swift +++ b/mobile/mobile/APIManager.swift @@ -268,32 +268,48 @@ class APIManager { } func updateDeviceToken( - token: String, deviceToken: String, completion: @escaping (Result) -> Void + token: String, + deviceToken: String, + completion: @escaping (Result) -> Void ) { let startTime = CFAbsoluteTimeGetCurrent() - let body: [String: Any] = [ - "jwt_token": token, - "payload": ["device_token": deviceToken], - "method": "update_device_token", - ] + guard let url = URL(string: "\(apiURL)/device_token/") else { + completion( + .failure(NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: "Invalid URL"])) + ) + return + } - performRequest(body: body, responseType: GenericResponse.self) { result in + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + + let payload = ["device_token": deviceToken] + request.httpBody = try? JSONSerialization.data(withJSONObject: payload) + + session.dataTask(with: request) { data, response, error in let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime print("APIManager: updateDeviceToken took \(timeElapsed) seconds") - switch result { - case .success(let response): - if response.success { - completion(.success(())) - } else { - let errorMessage = response.message ?? "Failed to update device token" - completion( - .failure( - NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey: errorMessage]))) - } - case .failure(let error): + if let httpResponse = response as? HTTPURLResponse, + !(200..<300).contains(httpResponse.statusCode) + { + let message = "Failed to update device token" + completion( + .failure( + NSError( + domain: "", code: httpResponse.statusCode, + userInfo: [NSLocalizedDescriptionKey: message]))) + return + } + + if let error = error { completion(.failure(error)) + return } - } + + completion(.success(())) + }.resume() } }