Skip to content

Commit f975ce9

Browse files
committed
Move env var read to deserialization
Signed-off-by: Rob Geada <[email protected]>
1 parent de7ae67 commit f975ce9

File tree

3 files changed

+59
-4
lines changed

3 files changed

+59
-4
lines changed

src/clients/http.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,7 @@ impl HttpClient {
144144

145145
/// Injects the API token as a Bearer token in the Authorization header if configured and present in the environment.
146146
fn inject_api_token(&self, headers: &mut HeaderMap) -> Result<(), Error> {
147-
if let Some(env_var) = &self.api_token
148-
&& let Ok(token) = std::env::var(env_var)
149-
{
147+
if let Some(token) = &self.api_token {
150148
headers.insert(
151149
http::header::AUTHORIZATION,
152150
HeaderValue::from_str(&format!("Bearer {}", token)).map_err(|e| Error::Http {

src/config.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ use std::{
2323
use serde::Deserialize;
2424
use tracing::{debug, error, info, warn};
2525

26-
use crate::clients::{chunker::DEFAULT_CHUNKER_ID, is_valid_hostname};
26+
use crate::{
27+
clients::{chunker::DEFAULT_CHUNKER_ID, is_valid_hostname},
28+
utils::from_env,
29+
};
2730

2831
/// Default allowed headers to passthrough to clients.
2932
const DEFAULT_ALLOWED_HEADERS: &[&str] = &[];
@@ -87,6 +90,7 @@ pub struct ServiceConfig {
8790
/// Keep-alive timeout in seconds for client calls [currently only for grpc generation]
8891
pub keep_alive_timeout: Option<u64>,
8992
/// Name of environment variable that contains the API key to use for this service [currently only for http generation]
93+
#[serde(default, deserialize_with = "from_env")]
9094
pub api_token: Option<String>,
9195
}
9296

src/utils.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,56 @@ where
3434
OneOrMany::Many(values) => Ok(values),
3535
}
3636
}
37+
38+
/// Serde helper to deserialize value from environment variable.
39+
pub fn from_env<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
40+
where
41+
D: Deserializer<'de>,
42+
{
43+
let env_name: Option<String> = Option::deserialize(deserializer)?;
44+
if let Some(env_name) = env_name {
45+
let value = std::env::var(&env_name)
46+
.map_err(|_| serde::de::Error::custom(format!("env var `{env_name}` not found")))?;
47+
Ok(Some(value))
48+
} else {
49+
Ok(None)
50+
}
51+
}
52+
53+
#[cfg(test)]
54+
mod tests {
55+
use serde::Deserialize;
56+
use serde_json::json;
57+
58+
use super::from_env;
59+
60+
#[derive(Debug, Deserialize)]
61+
pub struct Config {
62+
#[serde(default, deserialize_with = "from_env")]
63+
pub api_token: Option<String>,
64+
}
65+
66+
#[test]
67+
fn test_from_env() -> Result<(), Box<dyn std::error::Error>> {
68+
// Test no value
69+
let config: Config = serde_json::from_value(json!({}))?;
70+
assert_eq!(config.api_token, None);
71+
72+
// Test invalid value
73+
let config: Result<Config, serde_json::error::Error> = serde_json::from_value(json!({
74+
"api_token": "DOES_NOT_EXIST"
75+
}));
76+
assert!(config.is_err_and(|err| err.to_string() == "env var `DOES_NOT_EXIST` not found"));
77+
78+
// Test valid value
79+
unsafe {
80+
std::env::set_var("CLIENT_API_TOKEN", "token");
81+
}
82+
let config: Config = serde_json::from_value(json!({
83+
"api_token": "CLIENT_API_TOKEN"
84+
}))?;
85+
assert_eq!(config.api_token, Some("token".into()));
86+
87+
Ok(())
88+
}
89+
}

0 commit comments

Comments
 (0)