Merge pull request #711 from christianlouis/performance-optimize-dropbox-token-2748375428782384195
⚡ Optimize Dropbox token check by using async httpx
This commit is contained in:
+49
-46
@@ -6,7 +6,7 @@ import logging
|
|||||||
import os
|
import os
|
||||||
from typing import Annotated, Optional
|
from typing import Annotated, Optional
|
||||||
|
|
||||||
import requests
|
import httpx
|
||||||
from fastapi import APIRouter, Depends, Form, HTTPException, Request, status
|
from fastapi import APIRouter, Depends, Form, HTTPException, Request, status
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
@@ -132,57 +132,60 @@ async def test_dropbox_token(request: Request):
|
|||||||
"message": "Dropbox credentials are not fully configured",
|
"message": "Dropbox credentials are not fully configured",
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check token validity by getting current account info
|
async with httpx.AsyncClient() as client:
|
||||||
headers = {"Authorization": f"Bearer {settings.dropbox_refresh_token}"}
|
# Check token validity by getting current account info
|
||||||
response = requests.post(
|
headers = {"Authorization": f"Bearer {settings.dropbox_refresh_token}"}
|
||||||
"https://api.dropboxapi.com/2/users/get_current_account",
|
response = await client.post(
|
||||||
headers=headers,
|
|
||||||
timeout=settings.http_request_timeout,
|
|
||||||
)
|
|
||||||
|
|
||||||
# If token is invalid, try refreshing it
|
|
||||||
if response.status_code == 401:
|
|
||||||
logger.info("Dropbox access token invalid or expired, trying to refresh")
|
|
||||||
|
|
||||||
# Get a new access token using the refresh token
|
|
||||||
refresh_url = "https://api.dropbox.com/oauth2/token"
|
|
||||||
refresh_data = {
|
|
||||||
"grant_type": "refresh_token",
|
|
||||||
"refresh_token": settings.dropbox_refresh_token,
|
|
||||||
"client_id": settings.dropbox_app_key,
|
|
||||||
"client_secret": settings.dropbox_app_secret,
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh_response = requests.post(refresh_url, data=refresh_data, timeout=settings.http_request_timeout)
|
|
||||||
|
|
||||||
if refresh_response.status_code != 200:
|
|
||||||
logger.error(f"Failed to refresh Dropbox token: {refresh_response.text}")
|
|
||||||
return {
|
|
||||||
"status": "error",
|
|
||||||
"message": "Refresh token has expired or is invalid",
|
|
||||||
"needs_reauth": True,
|
|
||||||
}
|
|
||||||
|
|
||||||
token_info = refresh_response.json()
|
|
||||||
access_token = token_info.get("access_token")
|
|
||||||
|
|
||||||
# Try again with the new access token
|
|
||||||
headers = {"Authorization": f"Bearer {access_token}"}
|
|
||||||
response = requests.post(
|
|
||||||
"https://api.dropboxapi.com/2/users/get_current_account",
|
"https://api.dropboxapi.com/2/users/get_current_account",
|
||||||
headers=headers,
|
headers=headers,
|
||||||
timeout=settings.http_request_timeout,
|
timeout=settings.http_request_timeout,
|
||||||
)
|
)
|
||||||
|
|
||||||
if response.status_code != 200:
|
# If token is invalid, try refreshing it
|
||||||
logger.error(f"Dropbox token test failed: {response.status_code} {response.text}")
|
if response.status_code == 401:
|
||||||
return {
|
logger.info("Dropbox access token invalid or expired, trying to refresh")
|
||||||
"status": "error",
|
|
||||||
"message": f"Token validation failed with status {response.status_code}: {response.text}",
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get account info
|
# Get a new access token using the refresh token
|
||||||
account_info = response.json()
|
refresh_url = "https://api.dropbox.com/oauth2/token"
|
||||||
|
refresh_data = {
|
||||||
|
"grant_type": "refresh_token",
|
||||||
|
"refresh_token": settings.dropbox_refresh_token,
|
||||||
|
"client_id": settings.dropbox_app_key,
|
||||||
|
"client_secret": settings.dropbox_app_secret,
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh_response = await client.post(
|
||||||
|
refresh_url, data=refresh_data, timeout=settings.http_request_timeout
|
||||||
|
)
|
||||||
|
|
||||||
|
if refresh_response.status_code != 200:
|
||||||
|
logger.error(f"Failed to refresh Dropbox token: {refresh_response.text}")
|
||||||
|
return {
|
||||||
|
"status": "error",
|
||||||
|
"message": "Refresh token has expired or is invalid",
|
||||||
|
"needs_reauth": True,
|
||||||
|
}
|
||||||
|
|
||||||
|
token_info = refresh_response.json()
|
||||||
|
access_token = token_info.get("access_token")
|
||||||
|
|
||||||
|
# Try again with the new access token
|
||||||
|
headers = {"Authorization": f"Bearer {access_token}"}
|
||||||
|
response = await client.post(
|
||||||
|
"https://api.dropboxapi.com/2/users/get_current_account",
|
||||||
|
headers=headers,
|
||||||
|
timeout=settings.http_request_timeout,
|
||||||
|
)
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
logger.error(f"Dropbox token test failed: {response.status_code} {response.text}")
|
||||||
|
return {
|
||||||
|
"status": "error",
|
||||||
|
"message": f"Token validation failed with status {response.status_code}: {response.text}",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get account info
|
||||||
|
account_info = response.json()
|
||||||
account_email = account_info.get("email", "Unknown account")
|
account_email = account_info.get("email", "Unknown account")
|
||||||
account_name = account_info.get("name", {}).get("display_name", "Unknown user")
|
account_name = account_info.get("name", {}).get("display_name", "Unknown user")
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ Covers Dropbox OAuth endpoints, settings management, and token testing.
|
|||||||
from unittest.mock import Mock, patch
|
from unittest.mock import Mock, patch
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import requests
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.unit
|
@pytest.mark.unit
|
||||||
@@ -137,7 +136,7 @@ class TestTestDropboxToken:
|
|||||||
assert data["status"] == "error"
|
assert data["status"] == "error"
|
||||||
assert "not fully configured" in data["message"]
|
assert "not fully configured" in data["message"]
|
||||||
|
|
||||||
@patch("app.api.dropbox.requests.post")
|
@patch("app.api.dropbox.httpx.AsyncClient.post")
|
||||||
@patch("app.api.dropbox.settings")
|
@patch("app.api.dropbox.settings")
|
||||||
def test_valid_token(self, mock_settings, mock_post, client):
|
def test_valid_token(self, mock_settings, mock_post, client):
|
||||||
"""Test successful token validation."""
|
"""Test successful token validation."""
|
||||||
@@ -162,7 +161,7 @@ class TestTestDropboxToken:
|
|||||||
assert data["account"] == "user@example.com"
|
assert data["account"] == "user@example.com"
|
||||||
assert data["account_name"] == "Test User"
|
assert data["account_name"] == "Test User"
|
||||||
|
|
||||||
@patch("app.api.dropbox.requests.post")
|
@patch("app.api.dropbox.httpx.AsyncClient.post")
|
||||||
@patch("app.api.dropbox.settings")
|
@patch("app.api.dropbox.settings")
|
||||||
def test_expired_token_refreshed(self, mock_settings, mock_post, client):
|
def test_expired_token_refreshed(self, mock_settings, mock_post, client):
|
||||||
"""Test that expired token triggers refresh and retry."""
|
"""Test that expired token triggers refresh and retry."""
|
||||||
@@ -194,7 +193,7 @@ class TestTestDropboxToken:
|
|||||||
data = response.json()
|
data = response.json()
|
||||||
assert data["status"] == "success"
|
assert data["status"] == "success"
|
||||||
|
|
||||||
@patch("app.api.dropbox.requests.post")
|
@patch("app.api.dropbox.httpx.AsyncClient.post")
|
||||||
@patch("app.api.dropbox.settings")
|
@patch("app.api.dropbox.settings")
|
||||||
def test_refresh_token_expired(self, mock_settings, mock_post, client):
|
def test_refresh_token_expired(self, mock_settings, mock_post, client):
|
||||||
"""Test handling when refresh token itself is expired."""
|
"""Test handling when refresh token itself is expired."""
|
||||||
@@ -220,7 +219,7 @@ class TestTestDropboxToken:
|
|||||||
assert data["status"] == "error"
|
assert data["status"] == "error"
|
||||||
assert data["needs_reauth"] is True
|
assert data["needs_reauth"] is True
|
||||||
|
|
||||||
@patch("app.api.dropbox.requests.post")
|
@patch("app.api.dropbox.httpx.AsyncClient.post")
|
||||||
@patch("app.api.dropbox.settings")
|
@patch("app.api.dropbox.settings")
|
||||||
def test_token_validation_failure(self, mock_settings, mock_post, client):
|
def test_token_validation_failure(self, mock_settings, mock_post, client):
|
||||||
"""Test handling non-401, non-200 response."""
|
"""Test handling non-401, non-200 response."""
|
||||||
@@ -240,16 +239,21 @@ class TestTestDropboxToken:
|
|||||||
data = response.json()
|
data = response.json()
|
||||||
assert data["status"] == "error"
|
assert data["status"] == "error"
|
||||||
|
|
||||||
@patch("app.api.dropbox.requests.post")
|
@patch("app.api.dropbox.httpx.AsyncClient.post")
|
||||||
@patch("app.api.dropbox.settings")
|
@patch("app.api.dropbox.settings")
|
||||||
def test_connection_error(self, mock_settings, mock_post, client):
|
def test_connection_error(self, mock_settings, mock_post, client):
|
||||||
"""Test handling of connection exceptions."""
|
"""Test handling of connection exceptions."""
|
||||||
|
import httpx
|
||||||
|
|
||||||
mock_settings.dropbox_refresh_token = "token"
|
mock_settings.dropbox_refresh_token = "token"
|
||||||
mock_settings.dropbox_app_key = "app-key"
|
mock_settings.dropbox_app_key = "app-key"
|
||||||
mock_settings.dropbox_app_secret = "app-secret"
|
mock_settings.dropbox_app_secret = "app-secret"
|
||||||
mock_settings.http_request_timeout = 30
|
mock_settings.http_request_timeout = 30
|
||||||
|
|
||||||
mock_post.side_effect = requests.exceptions.ConnectionError("Connection refused")
|
mock_post.side_effect = httpx.RequestError(
|
||||||
|
"Connection refused",
|
||||||
|
request=httpx.Request("POST", "https://api.dropboxapi.com/2/users/get_current_account"),
|
||||||
|
)
|
||||||
|
|
||||||
response = client.get("/api/dropbox/test-token")
|
response = client.get("/api/dropbox/test-token")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user