Implement access token
This commit is contained in:
@@ -1,6 +1,10 @@
|
|||||||
import httpx
|
import httpx
|
||||||
|
|
||||||
class BaseAPI():
|
class BaseAPI():
|
||||||
|
def __init__(self):
|
||||||
|
self._client = httpx.AsyncClient()
|
||||||
|
self.access_token = None # Placeholder, will be set by derived class or external config
|
||||||
|
|
||||||
async def __aenter__(self):
|
async def __aenter__(self):
|
||||||
"""Allows using the client with async with."""
|
"""Allows using the client with async with."""
|
||||||
return self
|
return self
|
||||||
@@ -11,68 +15,29 @@ class BaseAPI():
|
|||||||
|
|
||||||
async def close(self):
|
async def close(self):
|
||||||
"""Closes the underlying asynchronous HTTP client."""
|
"""Closes the underlying asynchronous HTTP client."""
|
||||||
await self._client.close()
|
if self._client: # Ensure _client exists before trying to close
|
||||||
|
await self._client.close()
|
||||||
|
|
||||||
async def _post(self, endpoint: str, data: dict = None):
|
async def _post(self, endpoint: str, data: dict = None):
|
||||||
"""
|
"""
|
||||||
Asynchronous helper method for making POST requests.
|
Asynchronous helper method for making POST requests.
|
||||||
|
This method will now implicitly use the _request method,
|
||||||
Args:
|
so authentication logic is centralized.
|
||||||
endpoint: The API endpoint (e.g., "/op25/start").
|
|
||||||
data: The data to send in the request body (as a dictionary).
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The JSON response from the API.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
httpx.RequestError: If the request fails.
|
|
||||||
httpx.HTTPStatusError: If the API returns an error status code (4xx or 5xx).
|
|
||||||
"""
|
"""
|
||||||
url = f"{self.base_url}{endpoint}"
|
return await self._request("POST", endpoint, json=data)
|
||||||
try:
|
|
||||||
# Use await with the asynchronous httpx client
|
|
||||||
response = await self._client.post(url, json=data)
|
|
||||||
response.raise_for_status() # Raise HTTPStatusError for bad responses (4xx or 5xx)
|
|
||||||
return response.json()
|
|
||||||
except httpx.HTTPStatusError as e:
|
|
||||||
print(f"HTTP error occurred: {e}")
|
|
||||||
print(f"Response body: {e.response.text}") # Access response text from the exception
|
|
||||||
raise
|
|
||||||
except httpx.RequestError as e:
|
|
||||||
print(f"Request to '{url}' failed: {e}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
async def _get(self, endpoint: str):
|
async def _get(self, endpoint: str):
|
||||||
"""
|
"""
|
||||||
Asynchronous helper method for making GET requests.
|
Asynchronous helper method for making GET requests.
|
||||||
|
This method will now implicitly use the _request method,
|
||||||
Args:
|
so authentication logic is centralized.
|
||||||
endpoint: The API endpoint (e.g., "/op25/status").
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
The JSON response from the API.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
httpx.RequestError: If the request fails.
|
|
||||||
httpx.HTTPStatusError: If the API returns an error status code (4xx or 5xx).
|
|
||||||
"""
|
"""
|
||||||
url = f"{self.base_url}{endpoint}"
|
return await self._request("GET", endpoint)
|
||||||
try:
|
|
||||||
# Use await with the asynchronous httpx client
|
|
||||||
response = await self._client.get(url)
|
|
||||||
response.raise_for_status() # Raise HTTPStatusError for bad responses (4xx or 5xx)
|
|
||||||
return response.json()
|
|
||||||
except httpx.HTTPStatusError as e:
|
|
||||||
print(f"HTTP error occurred: {e}")
|
|
||||||
print(f"Response body: {e.response.text}") # Access response text from the exception
|
|
||||||
raise
|
|
||||||
except httpx.RequestError as e:
|
|
||||||
print(f"Request to '{url}' failed: {e}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
async def _request(self, method, endpoint, **kwargs):
|
async def _request(self, method, endpoint, **kwargs):
|
||||||
"""
|
"""
|
||||||
Helper method to make an asynchronous HTTP request.
|
Helper method to make an asynchronous HTTP request.
|
||||||
|
This is where the access_token will be injected.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
method (str): The HTTP method (e.g., 'GET', 'POST').
|
method (str): The HTTP method (e.g., 'GET', 'POST').
|
||||||
@@ -86,15 +51,20 @@ class BaseAPI():
|
|||||||
httpx.HTTPStatusError: If the request returns a non-2xx status code.
|
httpx.HTTPStatusError: If the request returns a non-2xx status code.
|
||||||
httpx.RequestError: For other request-related errors.
|
httpx.RequestError: For other request-related errors.
|
||||||
"""
|
"""
|
||||||
url = f"{self.base_url}{endpoint}"
|
url = f"{self.base_url}{endpoint}" # base_url must be defined in derived class or passed to BaseAPI
|
||||||
|
|
||||||
|
headers = kwargs.pop("headers", {})
|
||||||
|
if self.access_token: # Check if access_token is set
|
||||||
|
headers["Authorization"] = f"Bearer {self.access_token}"
|
||||||
|
kwargs["headers"] = headers
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = await self._client.request(method, url, **kwargs)
|
response = await self._client.request(method, url, **kwargs)
|
||||||
response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx)
|
response.raise_for_status() # Raise an exception for bad status codes (4xx or 5xx)
|
||||||
return response.json()
|
return response.json()
|
||||||
except httpx.HTTPStatusError as e:
|
except httpx.HTTPStatusError as e:
|
||||||
print(f"HTTP error occurred: {e}")
|
print(f"HTTP error occurred: {e}")
|
||||||
# You might want to return the error response body or raise the exception
|
|
||||||
raise
|
raise
|
||||||
except httpx.RequestError as e:
|
except httpx.RequestError as e:
|
||||||
print(f"An error occurred while requesting {e.request.url!r}: {e}")
|
print(f"An error occurred while requesting {e.request.url!r}: {e}")
|
||||||
raise
|
raise
|
||||||
@@ -20,6 +20,9 @@ CLIENT_API_URL = os.getenv("CLIENT_API_URL", "http://localhost:8001")
|
|||||||
if not app_conf.get("client_id"):
|
if not app_conf.get("client_id"):
|
||||||
app_conf.set("client_id", f"client-{uuid.uuid4().hex[:8]}")
|
app_conf.set("client_id", f"client-{uuid.uuid4().hex[:8]}")
|
||||||
CLIENT_ID = app_conf.client_id
|
CLIENT_ID = app_conf.client_id
|
||||||
|
|
||||||
|
# Get the nickname (even if it's empty)
|
||||||
|
NICKNAME = app_conf.get("nickname")
|
||||||
# ----------------------------
|
# ----------------------------
|
||||||
|
|
||||||
# Dictionary mapping command names (strings) to local client functions
|
# Dictionary mapping command names (strings) to local client functions
|
||||||
@@ -222,6 +225,9 @@ async def receive_commands(websocket):
|
|||||||
else:
|
else:
|
||||||
print(f"Received unknown command: {command_name}")
|
print(f"Received unknown command: {command_name}")
|
||||||
elif data.get("type") == "handshake_ack":
|
elif data.get("type") == "handshake_ack":
|
||||||
|
# Set the session token
|
||||||
|
app_conf.set(data.get("access_token"))
|
||||||
|
|
||||||
print(f"Server acknowledged handshake.")
|
print(f"Server acknowledged handshake.")
|
||||||
else:
|
else:
|
||||||
print(f"Received unknown message type: {data.get('type')}")
|
print(f"Received unknown message type: {data.get('type')}")
|
||||||
@@ -242,7 +248,7 @@ async def main_client():
|
|||||||
print("Connection established.")
|
print("Connection established.")
|
||||||
|
|
||||||
# Handshake: Send client ID immediately after connecting
|
# Handshake: Send client ID immediately after connecting
|
||||||
handshake_message = json.dumps({"type": "handshake", "id": CLIENT_ID})
|
handshake_message = json.dumps({"type": "handshake", "id": CLIENT_ID, "nickname": NICKNAME})
|
||||||
await websocket.send(handshake_message)
|
await websocket.send(handshake_message)
|
||||||
print(f"Sent handshake with ID: {CLIENT_ID}")
|
print(f"Sent handshake with ID: {CLIENT_ID}")
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import httpx
|
|||||||
import json
|
import json
|
||||||
from base_api import BaseAPI
|
from base_api import BaseAPI
|
||||||
from config import Config
|
from config import Config
|
||||||
|
import asyncio # Add this import for the example usage
|
||||||
|
|
||||||
app_config = Config()
|
app_config = Config()
|
||||||
|
|
||||||
@@ -19,8 +20,9 @@ class RadioAPIClient(BaseAPI):
|
|||||||
"""
|
"""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.base_url = base_url
|
self.base_url = base_url
|
||||||
# Use an AsyncClient for making asynchronous requests
|
|
||||||
self._client = httpx.AsyncClient()
|
self._client = httpx.AsyncClient()
|
||||||
|
# Set the access token on the BaseAPI instance
|
||||||
|
self.access_token = app_config.get("access_token") # Or app_config.access_token
|
||||||
|
|
||||||
async def get_systems(self):
|
async def get_systems(self):
|
||||||
"""
|
"""
|
||||||
@@ -161,5 +163,4 @@ if __name__ == "__main__":
|
|||||||
# 1. Ensure the server (server.py) is running.
|
# 1. Ensure the server (server.py) is running.
|
||||||
# 2. Ensure at least one client (client.py) is running.
|
# 2. Ensure at least one client (client.py) is running.
|
||||||
# 3. Run this script: python api_client.py
|
# 3. Run this script: python api_client.py
|
||||||
asyncio.run(example_api_usage())
|
asyncio.run(example_api_usage())
|
||||||
|
|
||||||
Reference in New Issue
Block a user