import requests
import time
[docs]
class FatsecretBase:
"""
A client for interacting with the Fatsecret API using OAuth 2.0 authentication.
This class handles the initialization of the Fatsecret API client with the provided
client credentials, fetching new access tokens, and sending requests to the API
with the necessary authorization headers.
"""
TOKEN_URL = "https://oauth.fatsecret.com/connect/token"
API_URL = "https://platform.fatsecret.com/rest/server.api"
def __init__(self, **kwargs) -> None:
"""
Initializes the Fatsecret API client with the provided client credentials.
Parameters:
client_id (str): Your FatSecret application client ID.
client_secret (str): Your FatSecret application client secret key.
"""
self.client_id = kwargs['client_id']
self.client_secret = kwargs['client_secret']
self._time_token_was_requested = time.time()
self._access_token_data = self.get_new_access_token()
[docs]
def get_new_access_token(self) -> dict:
"""
Requests a new access token from the FatSecret OAuth 2.0 endpoint using client credentials.
Returns:
dict: A dictionary containing the access token and other data.
"""
data = {'grant_type': 'client_credentials'}
self._time_token_was_requested = time.time()
response = requests.post(self.TOKEN_URL, data=data,
auth=(self.client_id, self.client_secret))
try:
payload = response.json()
except Exception as exc: # pragma: no cover - network dependent
raise RuntimeError(
f"Failed to parse token response (status {response.status_code})"
) from exc
if not response.ok or 'access_token' not in payload:
raise RuntimeError(
f"Failed to obtain access token (status {response.status_code}): {payload}"
)
return payload
@property
def access_token(self):
"""
Provides the current access token, refreshing it if it's close to expiring.
Returns:
str: The access token.
"""
expires_in = self.access_token_expires_in
if expires_in is None or expires_in < 600:
self._access_token_data = self.get_new_access_token()
token = self._access_token_data.get('access_token')
if not token:
raise RuntimeError(
f"Token response is missing 'access_token': {self._access_token_data}"
)
return token
@property
def access_token_expires_in(self) -> float:
"""
Calculates the time in seconds until the current access token expires.
Returns:
float: The number of seconds until the access token expires.
"""
expires_in = self._access_token_data.get('expires_in')
if not isinstance(expires_in, (int, float)):
return None
return expires_in - (time.time() - self._time_token_was_requested)
[docs]
def get_params(self, **kwargs):
params = {}
for key, value in kwargs.items():
if value is not None:
params[key] = value
return params
[docs]
def make_request(self, method: str, params: dict = None) -> dict:
"""
Makes a request to the FatSecret API using the obtained access token.
Parameters:
method (str): The API method to call.
params (dict, optional): Additional parameters for the API request.
Returns:
dict: The JSON response from the API.
"""
if params is None:
params = {}
headers = {'Authorization': f'Bearer {self.access_token}'}
params['method'] = method
params['format'] = 'json'
response = requests.post(self.API_URL, headers=headers, params=params)
return response.json()