Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
225 changes: 214 additions & 11 deletions fastfuels_sdk/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,82 @@
from typing import Optional

from fastfuels_sdk.client_library.api_client import ApiClient
from fastfuels_sdk.client_library.api import (
DomainsApi,
InventoriesApi,
TreeInventoryApi,
FeaturesApi,
RoadFeatureApi,
WaterFeatureApi,
GridsApi,
TreeGridApi,
SurfaceGridApi,
TopographyGridApi,
FeatureGridApi,
)

_client: Optional[ApiClient] = None
_domains_api: Optional[DomainsApi] = None
_inventories_api: Optional[InventoriesApi] = None
_tree_inventory_api: Optional[TreeInventoryApi] = None
_features_api: Optional[FeaturesApi] = None
_road_feature_api: Optional[RoadFeatureApi] = None
_water_feature_api: Optional[WaterFeatureApi] = None
_grids_api: Optional[GridsApi] = None
_tree_grid_api: Optional[TreeGridApi] = None
_surface_grid_api: Optional[SurfaceGridApi] = None
_topography_grid_api: Optional[TopographyGridApi] = None
_feature_grid_api: Optional[FeatureGridApi] = None


def set_api_key(api_key: str) -> None:
global _client
"""Set the API key for the FastFuels SDK.

config = {
"header_name": "api-key",
"header_value": api_key,
}
This will invalidate the cached API client and all API instances,
ensuring that subsequent API calls use the new credentials.

_client = ApiClient(**config)
Args:
api_key: The API key to use for authentication
"""
global _client, _domains_api, _inventories_api, _tree_inventory_api
global _features_api, _road_feature_api, _water_feature_api
global _grids_api, _tree_grid_api, _surface_grid_api, _topography_grid_api, _feature_grid_api

_client = None
_domains_api = None
_inventories_api = None
_tree_inventory_api = None
_features_api = None
_road_feature_api = None
_water_feature_api = None
_grids_api = None
_tree_grid_api = None
_surface_grid_api = None
_topography_grid_api = None
_feature_grid_api = None

os.environ["FASTFUELS_API_KEY"] = api_key

def get_client() -> ApiClient:

def get_client() -> Optional[ApiClient]:
"""Get the current API client, creating one if necessary.

This function will attempt to get the API client from:
1. The existing _client instance if set_api_key() was called
2. The FASTFUELS_API_KEY environment variable

Returns:
The ApiClient instance, or None if no API key is configured
"""
global _client

if _client is not None:
return _client

api_key = os.getenv("FASTFUELS_API_KEY")
if not api_key:
raise RuntimeError(
"FASTFUELS_API_KEY environment variable not set. "
"Please set this variable with your API key."
)
return None

config = {
"header_name": "api-key",
"header_value": api_key,
Expand All @@ -41,3 +90,157 @@ def get_client() -> ApiClient:
_client = ApiClient(**config)

return _client


def ensure_client() -> ApiClient:
"""Ensure an API client is configured and return it.

This function will raise a RuntimeError with a helpful message if no
API key has been configured.

Returns:
The ApiClient instance

Raises:
RuntimeError: If no API key is configured
"""
client = get_client()
if client is None:
raise RuntimeError(
"FastFuels API key not configured. Please either:\n"
" 1. Set the FASTFUELS_API_KEY environment variable, or\n"
" 2. Call fastfuels_sdk.api.set_api_key('your-api-key') before making API calls"
)
return client


def get_domains_api() -> DomainsApi:
"""Get the cached DomainsApi instance, creating it if necessary.

Returns:
The DomainsApi instance
"""
global _domains_api
if _domains_api is None:
_domains_api = DomainsApi(ensure_client())
return _domains_api


def get_inventories_api() -> InventoriesApi:
"""Get the cached InventoriesApi instance, creating it if necessary.

Returns:
The InventoriesApi instance
"""
global _inventories_api
if _inventories_api is None:
_inventories_api = InventoriesApi(ensure_client())
return _inventories_api


def get_tree_inventory_api() -> TreeInventoryApi:
"""Get the cached TreeInventoryApi instance, creating it if necessary.

Returns:
The TreeInventoryApi instance
"""
global _tree_inventory_api
if _tree_inventory_api is None:
_tree_inventory_api = TreeInventoryApi(ensure_client())
return _tree_inventory_api


def get_features_api() -> FeaturesApi:
"""Get the cached FeaturesApi instance, creating it if necessary.

Returns:
The FeaturesApi instance
"""
global _features_api
if _features_api is None:
_features_api = FeaturesApi(ensure_client())
return _features_api


def get_road_feature_api() -> RoadFeatureApi:
"""Get the cached RoadFeatureApi instance, creating it if necessary.

Returns:
The RoadFeatureApi instance
"""
global _road_feature_api
if _road_feature_api is None:
_road_feature_api = RoadFeatureApi(ensure_client())
return _road_feature_api


def get_water_feature_api() -> WaterFeatureApi:
"""Get the cached WaterFeatureApi instance, creating it if necessary.

Returns:
The WaterFeatureApi instance
"""
global _water_feature_api
if _water_feature_api is None:
_water_feature_api = WaterFeatureApi(ensure_client())
return _water_feature_api


def get_grids_api() -> GridsApi:
"""Get the cached GridsApi instance, creating it if necessary.

Returns:
The GridsApi instance
"""
global _grids_api
if _grids_api is None:
_grids_api = GridsApi(ensure_client())
return _grids_api


def get_tree_grid_api() -> TreeGridApi:
"""Get the cached TreeGridApi instance, creating it if necessary.

Returns:
The TreeGridApi instance
"""
global _tree_grid_api
if _tree_grid_api is None:
_tree_grid_api = TreeGridApi(ensure_client())
return _tree_grid_api


def get_surface_grid_api() -> SurfaceGridApi:
"""Get the cached SurfaceGridApi instance, creating it if necessary.

Returns:
The SurfaceGridApi instance
"""
global _surface_grid_api
if _surface_grid_api is None:
_surface_grid_api = SurfaceGridApi(ensure_client())
return _surface_grid_api


def get_topography_grid_api() -> TopographyGridApi:
"""Get the cached TopographyGridApi instance, creating it if necessary.

Returns:
The TopographyGridApi instance
"""
global _topography_grid_api
if _topography_grid_api is None:
_topography_grid_api = TopographyGridApi(ensure_client())
return _topography_grid_api


def get_feature_grid_api() -> FeatureGridApi:
"""Get the cached FeatureGridApi instance, creating it if necessary.

Returns:
The FeatureGridApi instance
"""
global _feature_grid_api
if _feature_grid_api is None:
_feature_grid_api = FeatureGridApi(ensure_client())
return _feature_grid_api
19 changes: 8 additions & 11 deletions fastfuels_sdk/domains.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@
from typing import Optional, List

# Internal imports
from fastfuels_sdk.api import get_client
from fastfuels_sdk.client_library.api import DomainsApi
from fastfuels_sdk.api import get_domains_api
from fastfuels_sdk.client_library.models import (
Domain as DomainModel,
CreateDomainRequest,
Expand All @@ -21,8 +20,6 @@
# External imports
import geopandas as gpd

_DOMAIN_API = DomainsApi(get_client())


class Domain(DomainModel):
"""Domain resource for the FastFuels API.
Expand Down Expand Up @@ -110,7 +107,7 @@ def from_id(cls, domain_id: str) -> "Domain":
>>> domain.id
'abc123'
"""
get_domain_response = _DOMAIN_API.get_domain(domain_id)
get_domain_response = get_domains_api().get_domain(domain_id)
return cls(**get_domain_response.model_dump())

@classmethod
Expand Down Expand Up @@ -178,7 +175,7 @@ def from_geojson(
}

request = CreateDomainRequest.from_dict(feature_data)
response = _DOMAIN_API.create_domain(
response = get_domains_api().create_domain(
create_domain_request=request.model_dump() # noqa
)
return cls(**response.model_dump()) if response else None
Expand Down Expand Up @@ -297,7 +294,7 @@ def get(self, in_place: bool = False) -> "Domain":
ensure all references to this Domain instance see the updated data.
"""
# Fetch latest data from API
response = _DOMAIN_API.get_domain(self.id)
response = get_domains_api().get_domain(self.id)

if in_place:
# Update all attributes of current instance
Expand Down Expand Up @@ -371,7 +368,7 @@ def update(
# Only make API call if there are fields to update
if update_data:
request = UpdateDomainRequest(**update_data)
response = _DOMAIN_API.update_domain(
response = get_domains_api().update_domain(
domain_id=self.id, update_domain_request=request
)

Expand Down Expand Up @@ -504,7 +501,7 @@ def export(self) -> dict:
- Grid array data: Use grid export endpoints
- Tree inventory records: Use inventory export endpoints
"""
return _DOMAIN_API.export_domain_data(domain_id=self.id)
return get_domains_api().export_domain_data(domain_id=self.id)

def delete(self) -> None:
"""Delete an existing domain resource based on the domain ID.
Expand All @@ -526,7 +523,7 @@ def delete(self) -> None:
>>> domain.get()
# Raises NotFoundException
"""
_DOMAIN_API.delete_domain(domain_id=self.id)
get_domains_api().delete_domain(domain_id=self.id)
return None


Expand Down Expand Up @@ -589,7 +586,7 @@ def list_domains(
"""
sort_by = DomainSortField(sort_by) if sort_by else None
sort_order = DomainSortOrder(sort_order) if sort_order else None
list_response = _DOMAIN_API.list_domains(
list_response = get_domains_api().list_domains(
page=page, size=size, sort_by=sort_by, sort_order=sort_order
)
list_response.domains = [Domain(**d.model_dump()) for d in list_response.domains]
Expand Down
Loading
Loading