Skip to content

Commit 3040061

Browse files
Merge pull request #653 from jaluna0/feat/synthetics-tests-run-oauth
feat(synthetics): accept OAuth2 for `synthetics tests run`
2 parents 6347a25 + 92d3e78 commit 3040061

1 file changed

Lines changed: 57 additions & 16 deletions

File tree

src/commands/synthetics.rs

Lines changed: 57 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,23 +29,29 @@ fn synthetics_intake_base_url(cfg: &Config) -> String {
2929
#[cfg(not(target_arch = "wasm32"))]
3030
fn build_auth_headers(cfg: &Config) -> anyhow::Result<reqwest::header::HeaderMap> {
3131
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
32-
let api_key = cfg
33-
.api_key
34-
.as_ref()
35-
.ok_or_else(|| anyhow::anyhow!("DD_API_KEY is required for 'synthetics tests run'"))?;
36-
let app_key = cfg
37-
.app_key
38-
.as_ref()
39-
.ok_or_else(|| anyhow::anyhow!("DD_APP_KEY is required for 'synthetics tests run'"))?;
4032
let mut headers = HeaderMap::new();
41-
headers.insert(
42-
HeaderName::from_static("dd-api-key"),
43-
HeaderValue::from_str(api_key)?,
44-
);
45-
headers.insert(
46-
HeaderName::from_static("dd-application-key"),
47-
HeaderValue::from_str(app_key)?,
48-
);
33+
34+
// Prefer OAuth2 bearer token, falling back to API + app keys.
35+
if let Some(token) = cfg.access_token.as_ref() {
36+
headers.insert(
37+
reqwest::header::AUTHORIZATION,
38+
HeaderValue::from_str(&format!("Bearer {token}"))?,
39+
);
40+
} else if let (Some(api_key), Some(app_key)) = (cfg.api_key.as_ref(), cfg.app_key.as_ref()) {
41+
headers.insert(
42+
HeaderName::from_static("dd-api-key"),
43+
HeaderValue::from_str(api_key)?,
44+
);
45+
headers.insert(
46+
HeaderName::from_static("dd-application-key"),
47+
HeaderValue::from_str(app_key)?,
48+
);
49+
} else {
50+
anyhow::bail!(
51+
"'synthetics tests run' requires authentication: run 'pup auth login' or set DD_API_KEY and DD_APP_KEY"
52+
);
53+
}
54+
4955
headers.insert(
5056
reqwest::header::USER_AGENT,
5157
HeaderValue::from_str(&crate::useragent::get())?,
@@ -959,6 +965,41 @@ mod tests {
959965
cleanup_env();
960966
}
961967

968+
#[cfg(not(target_arch = "wasm32"))]
969+
#[test]
970+
fn test_build_auth_headers_prefers_bearer() {
971+
let mut cfg = test_config("http://unused.local");
972+
cfg.access_token = Some("tok-123".into());
973+
let headers = super::build_auth_headers(&cfg).expect("bearer headers");
974+
assert_eq!(headers.get("authorization").unwrap(), "Bearer tok-123");
975+
assert!(headers.get("dd-api-key").is_none());
976+
assert!(headers.get("dd-application-key").is_none());
977+
}
978+
979+
#[cfg(not(target_arch = "wasm32"))]
980+
#[test]
981+
fn test_build_auth_headers_falls_back_to_api_keys() {
982+
let mut cfg = test_config("http://unused.local");
983+
cfg.access_token = None;
984+
cfg.api_key = Some("api-1".into());
985+
cfg.app_key = Some("app-1".into());
986+
let headers = super::build_auth_headers(&cfg).expect("api-key headers");
987+
assert_eq!(headers.get("dd-api-key").unwrap(), "api-1");
988+
assert_eq!(headers.get("dd-application-key").unwrap(), "app-1");
989+
assert!(headers.get("authorization").is_none());
990+
}
991+
992+
#[cfg(not(target_arch = "wasm32"))]
993+
#[test]
994+
fn test_build_auth_headers_requires_some_auth() {
995+
let mut cfg = test_config("http://unused.local");
996+
cfg.access_token = None;
997+
cfg.api_key = None;
998+
cfg.app_key = None;
999+
let err = super::build_auth_headers(&cfg).expect_err("should require auth");
1000+
assert!(err.to_string().contains("requires authentication"));
1001+
}
1002+
9621003
async fn server_mock_delete(s: &mut mockito::Server) -> mockito::Mock {
9631004
s.mock("DELETE", mockito::Matcher::Any)
9641005
.with_status(204)

0 commit comments

Comments
 (0)