hotdata 0.14.0

Powerful data platform API for datasets, queries, and analytics.
Documentation
/*
 * Hotdata API
 *
 * Powerful data platform API for managed databases, queries, and analytics.
 *
 * The version of the OpenAPI document: 1.0.0
 * Contact: developers@hotdata.dev
 * Generated by: https://openapi-generator.tech
 */

use std::collections::HashMap;

#[derive(Clone)]
pub struct Configuration {
    pub base_path: String,
    pub user_agent: Option<String>,
    pub client: reqwest::Client,
    pub basic_auth: Option<BasicAuth>,
    pub oauth_access_token: Option<String>,
    pub bearer_access_token: Option<String>,
    pub token_provider: Option<std::sync::Arc<dyn crate::auth::BearerTokenProvider>>,
    pub api_keys: HashMap<String, ApiKey>,
    /// HTTP 429 (`OVERLOADED`) retry policy applied to every generated `apis::*`
    /// operation: the request is retried with `Retry-After`/backoff before the
    /// op returns. Defaults to [`RetryPolicy::default`]; set `max_retries` to 0
    /// to disable retry. The enhanced query path ([`crate::query`]) uses its own
    /// per-call [`QueryConfig::retry`](crate::query::QueryConfig::retry) instead.
    pub retry: crate::query::RetryPolicy,
}

pub type BasicAuth = (String, Option<String>);

#[derive(Clone)]
pub struct ApiKey {
    pub prefix: Option<String>,
    pub key: String,
}

// Credentials are redacted rather than derived: `Configuration`/`Client` are
// long-lived and easy to `{:?}`/`dbg!` in a caller's log, and the bearer
// token in particular is a long-lived API token, not a short-lived JWT.
impl std::fmt::Debug for ApiKey {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("ApiKey")
            .field("prefix", &self.prefix)
            .field("key", &"<redacted>")
            .finish()
    }
}

impl std::fmt::Debug for Configuration {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Configuration")
            .field("base_path", &self.base_path)
            .field("user_agent", &self.user_agent)
            .field("client", &self.client)
            .field(
                "basic_auth",
                &self.basic_auth.as_ref().map(|_| "<redacted>"),
            )
            .field(
                "oauth_access_token",
                &self.oauth_access_token.as_ref().map(|_| "<redacted>"),
            )
            .field(
                "bearer_access_token",
                &self.bearer_access_token.as_ref().map(|_| "<redacted>"),
            )
            // Only whether a provider is installed, never the provider itself:
            // an implementor's own Debug may well print the credential it holds.
            .field(
                "token_provider",
                &self.token_provider.as_ref().map(|_| "<installed>"),
            )
            .field("api_keys", &self.api_keys)
            .field("retry", &self.retry)
            .finish()
    }
}

impl Configuration {
    pub fn new() -> Configuration {
        Configuration::default()
    }

    /// Resolve the bearer token for a request, preferring a pluggable
    /// async token provider (for a host that owns its own credential
    /// lifecycle) and falling back to the static bearer_access_token when
    /// none is set.
    ///
    /// If a provider is configured but errors, the error is logged via the
    /// `log` facade and `None` is returned. The request then proceeds
    /// unauthenticated and the server replies 401.
    ///
    /// The `Option` return cannot carry that cause to the caller, so the
    /// `log::warn!` is the only trace of it — and it is visible only if the
    /// host installed a `log` implementation with `warn` enabled for the
    /// `hotdata` target. In a binary with no logger, a provider failure is
    /// indistinguishable from any other 401, so wire up a logger before
    /// debugging one.
    pub async fn resolve_bearer_token(&self) -> Option<String> {
        if let Some(ref provider) = self.token_provider {
            match provider.bearer_value().await {
                Ok(token) => return Some(token),
                Err(e) => {
                    log::warn!(
                        "hotdata: bearer token resolution failed; \
                         sending request unauthenticated: {e}"
                    );
                    return None;
                }
            }
        }
        self.bearer_access_token.clone()
    }
}

impl Default for Configuration {
    fn default() -> Self {
        Configuration {
            base_path: "https://api.hotdata.dev".to_owned(),
            // Computed from CARGO_PKG_VERSION at compile time so the default UA
            // always matches the shipped crate version (issue #69). Deliberately
            // ignores the generator's httpUserAgent: a regen has no idea which
            // version will publish, so baking a concrete version here lags. This
            // mirrors what ClientBuilder already does for the ergonomic surface.
            user_agent: Some(concat!("hotdata-rust/", env!("CARGO_PKG_VERSION")).to_owned()),
            client: reqwest::Client::new(),
            basic_auth: None,
            oauth_access_token: None,
            bearer_access_token: None,
            token_provider: None,
            api_keys: HashMap::new(),
            retry: crate::query::RetryPolicy::default(),
        }
    }
}