Merge a79c359b05 into fa4d01c23a
This commit is contained in:
commit
5fe5669ee9
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import requests
|
import requests
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import json
|
import json
|
||||||
|
|
@ -14,6 +15,10 @@ def get_api_key() -> str:
|
||||||
raise ValueError("ALPHA_VANTAGE_API_KEY environment variable is not set.")
|
raise ValueError("ALPHA_VANTAGE_API_KEY environment variable is not set.")
|
||||||
return api_key
|
return api_key
|
||||||
|
|
||||||
|
def _sanitize_url(url: str) -> str:
|
||||||
|
"""Remove sensitive API key values from a URL string for safe logging."""
|
||||||
|
return re.sub(r'([\?&]apikey=)[^&]+', r'\1***REDACTED***', url, flags=re.IGNORECASE)
|
||||||
|
|
||||||
def format_datetime_for_api(date_input) -> str:
|
def format_datetime_for_api(date_input) -> str:
|
||||||
"""Convert various date formats to YYYYMMDDTHHMM format required by Alpha Vantage API."""
|
"""Convert various date formats to YYYYMMDDTHHMM format required by Alpha Vantage API."""
|
||||||
if isinstance(date_input, str):
|
if isinstance(date_input, str):
|
||||||
|
|
@ -41,33 +46,53 @@ class AlphaVantageRateLimitError(Exception):
|
||||||
|
|
||||||
def _make_api_request(function_name: str, params: dict) -> dict | str:
|
def _make_api_request(function_name: str, params: dict) -> dict | str:
|
||||||
"""Helper function to make API requests and handle responses.
|
"""Helper function to make API requests and handle responses.
|
||||||
|
|
||||||
|
The Alpha Vantage API requires the key to be supplied as a query parameter.
|
||||||
|
To reduce the risk of accidental key exposure in logs and error messages the
|
||||||
|
key is:
|
||||||
|
- never stored in the intermediate ``api_params`` dict, and
|
||||||
|
- redacted from any URL that appears in exception text via
|
||||||
|
``_sanitize_url()``.
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
AlphaVantageRateLimitError: When API rate limit is exceeded
|
AlphaVantageRateLimitError: When API rate limit is exceeded
|
||||||
"""
|
"""
|
||||||
# Create a copy of params to avoid modifying the original
|
# Create a copy of params to avoid modifying the original.
|
||||||
|
# The API key is intentionally kept out of this dict so it does not appear
|
||||||
|
# in local state that could be logged or serialised for debugging.
|
||||||
api_params = params.copy()
|
api_params = params.copy()
|
||||||
api_params.update({
|
api_params.update({
|
||||||
"function": function_name,
|
"function": function_name,
|
||||||
"apikey": get_api_key(),
|
|
||||||
"source": "trading_agents",
|
"source": "trading_agents",
|
||||||
})
|
})
|
||||||
|
|
||||||
# Handle entitlement parameter if present in params or global variable
|
# Handle entitlement parameter if present in params or global variable
|
||||||
current_entitlement = globals().get('_current_entitlement')
|
current_entitlement = globals().get('_current_entitlement')
|
||||||
entitlement = api_params.get("entitlement") or current_entitlement
|
entitlement = api_params.get("entitlement") or current_entitlement
|
||||||
|
|
||||||
if entitlement:
|
if entitlement:
|
||||||
api_params["entitlement"] = entitlement
|
api_params["entitlement"] = entitlement
|
||||||
elif "entitlement" in api_params:
|
elif "entitlement" in api_params:
|
||||||
# Remove entitlement if it's None or empty
|
# Remove entitlement if it's None or empty
|
||||||
api_params.pop("entitlement", None)
|
api_params.pop("entitlement", None)
|
||||||
|
|
||||||
response = requests.get(API_BASE_URL, params=api_params)
|
# Merge the API key into request_params only at call-time so the key is
|
||||||
response.raise_for_status()
|
# not retained in any longer-lived variable.
|
||||||
|
request_params = {**api_params, "apikey": get_api_key()}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(API_BASE_URL, params=request_params)
|
||||||
|
response.raise_for_status()
|
||||||
|
except requests.exceptions.HTTPError as exc:
|
||||||
|
# Sanitize the request URL before it propagates to callers / logs.
|
||||||
|
safe_url = _sanitize_url(exc.response.url if exc.response is not None else "")
|
||||||
|
raise requests.exceptions.HTTPError(
|
||||||
|
f"HTTP error {exc.response.status_code} for URL: {safe_url}",
|
||||||
|
response=exc.response,
|
||||||
|
) from exc
|
||||||
|
|
||||||
response_text = response.text
|
response_text = response.text
|
||||||
|
|
||||||
# Check if response is JSON (error responses are typically JSON)
|
# Check if response is JSON (error responses are typically JSON)
|
||||||
try:
|
try:
|
||||||
response_json = json.loads(response_text)
|
response_json = json.loads(response_text)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue