1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*
* 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(),
}
}
}