From b141caf5ac0c34cf9d1a57555afbc1d2128f2b87 Mon Sep 17 00:00:00 2001 From: Stephen Rosenthal Date: Mon, 24 Aug 2026 16:37:40 -0700 Subject: [PATCH 1/4] pup: enable OAuth for Observability Pipelines commands Remove 6 obs-pipelines endpoints from OAUTH_EXCLUDED_ENDPOINTS so raw_get/raw_post send the OAuth bearer token. The server already accepts OAuth on all v2 obs-pipelines routes. --- src/raw_client.rs | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/src/raw_client.rs b/src/raw_client.rs index ed14c965..03c7bd31 100644 --- a/src/raw_client.rs +++ b/src/raw_client.rs @@ -194,31 +194,6 @@ static OAUTH_EXCLUDED_ENDPOINTS: &[EndpointRequirement] = &[ path: "/api/v2/fleet/schedules/", method: "POST", }, - // Observability Pipelines (6) — API key only, no OAuth support - EndpointRequirement { - path: "/api/v2/obs-pipelines/pipelines", - method: "GET", - }, - EndpointRequirement { - path: "/api/v2/obs-pipelines/pipelines", - method: "POST", - }, - EndpointRequirement { - path: "/api/v2/obs-pipelines/pipelines/", - method: "GET", - }, - EndpointRequirement { - path: "/api/v2/obs-pipelines/pipelines/", - method: "PUT", - }, - EndpointRequirement { - path: "/api/v2/obs-pipelines/pipelines/", - method: "DELETE", - }, - EndpointRequirement { - path: "/api/v2/obs-pipelines/pipelines/validate", - method: "POST", - }, // Cost / Billing (11) — API key only, no OAuth support EndpointRequirement { path: "/api/v2/usage/projected_cost", @@ -836,7 +811,7 @@ mod tests { #[test] fn test_oauth_excluded_count() { - assert_eq!(OAUTH_EXCLUDED_ENDPOINTS.len(), 46); + assert_eq!(OAUTH_EXCLUDED_ENDPOINTS.len(), 40); } #[test] From 502e23aad3cff79314bec4d428f4a649a3a35d91 Mon Sep 17 00:00:00 2001 From: Stephen Rosenthal Date: Tue, 25 Aug 2026 08:45:55 -0700 Subject: [PATCH 2/4] test: add OAuth routing tests for obs-pipelines endpoints Add two tests verifying that obs-pipelines routes no longer require API-key fallback after removal from OAUTH_EXCLUDED_ENDPOINTS: - test_no_fallback_for_obs_pipelines: asserts requires_api_key_fallback returns false for all formerly-excluded method/path combinations (collection, ID-parameterized, validation), plus a non-matching method - test_raw_get_obs_pipelines_uses_oauth_bearer: end-to-end test that raw_get sends the Authorization: Bearer header and omits DD-API-KEY for an obs-pipelines pipeline path --- src/raw_client.rs | 58 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/src/raw_client.rs b/src/raw_client.rs index 03c7bd31..26372d13 100644 --- a/src/raw_client.rs +++ b/src/raw_client.rs @@ -814,6 +814,64 @@ mod tests { assert_eq!(OAUTH_EXCLUDED_ENDPOINTS.len(), 40); } + #[test] + fn test_no_fallback_for_obs_pipelines() { + // Observability Pipelines routes already accept OAuth server-side; + // removing them from OAUTH_EXCLUDED_ENDPOINTS means raw_get/raw_post + // (used by `pup obs-pipelines diff` and the `pup api` passthrough) + // should send the OAuth bearer instead of forcing API-key fallback. + // Collection endpoint + assert!(!requires_api_key_fallback("GET", "/api/v2/obs-pipelines/pipelines")); + assert!(!requires_api_key_fallback("POST", "/api/v2/obs-pipelines/pipelines")); + // ID-parameterized endpoints (prefix match via trailing "/") + assert!(!requires_api_key_fallback( + "GET", + "/api/v2/obs-pipelines/pipelines/abc-123" + )); + assert!(!requires_api_key_fallback( + "PUT", + "/api/v2/obs-pipelines/pipelines/abc-123" + )); + assert!(!requires_api_key_fallback( + "DELETE", + "/api/v2/obs-pipelines/pipelines/abc-123" + )); + // Validation endpoint + assert!(!requires_api_key_fallback( + "POST", + "/api/v2/obs-pipelines/pipelines/validate" + )); + // Non-matching method on a formerly-excluded path + assert!(!requires_api_key_fallback( + "PATCH", + "/api/v2/obs-pipelines/pipelines" + )); + } + + #[tokio::test] + async fn test_raw_get_obs_pipelines_uses_oauth_bearer() { + let _lock = lock_env().await; + let mut server = mockito::Server::new_async().await; + let mut cfg = test_config(&server.url()); + cfg.access_token = Some("token".into()); + let mock = server + .mock("GET", "/api/v2/obs-pipelines/pipelines/abc-123") + .match_header("Authorization", "Bearer token") + .match_header("DD-API-KEY", mockito::Matcher::Missing) + .with_status(200) + .with_header("content-type", "application/json") + .with_body(r#"{"data": []}"#) + .expect(1) + .create_async() + .await; + + let result = raw_get(&cfg, "/api/v2/obs-pipelines/pipelines/abc-123", &[]).await; + + assert!(result.is_ok(), "raw get failed: {:?}", result.err()); + mock.assert_async().await; + cleanup_env(); + } + #[test] fn test_no_fallback_for_notebooks() { assert!(!requires_api_key_fallback("GET", "/api/v1/notebooks")); From 646b33e1f1deb5a63fd4502e2626269acd6db7f3 Mon Sep 17 00:00:00 2001 From: Stephen Rosenthal Date: Tue, 25 Aug 2026 09:13:23 -0700 Subject: [PATCH 3/4] test: remove brittle OAUTH_EXCLUDED_ENDPOINTS count assertion The count test is prone to merge conflicts and tests no actual behavior. The per-group fallback tests provide real coverage. --- src/raw_client.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/raw_client.rs b/src/raw_client.rs index 26372d13..e6e7f227 100644 --- a/src/raw_client.rs +++ b/src/raw_client.rs @@ -809,11 +809,6 @@ mod tests { )); } - #[test] - fn test_oauth_excluded_count() { - assert_eq!(OAUTH_EXCLUDED_ENDPOINTS.len(), 40); - } - #[test] fn test_no_fallback_for_obs_pipelines() { // Observability Pipelines routes already accept OAuth server-side; From ff944d260fc4de5f408d65cc5363bfd8f8002506 Mon Sep 17 00:00:00 2001 From: Stephen Rosenthal Date: Tue, 25 Aug 2026 12:25:10 -0700 Subject: [PATCH 4/4] fmt: apply cargo fmt formatting --- src/raw_client.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/raw_client.rs b/src/raw_client.rs index e6e7f227..ba8d9928 100644 --- a/src/raw_client.rs +++ b/src/raw_client.rs @@ -816,8 +816,14 @@ mod tests { // (used by `pup obs-pipelines diff` and the `pup api` passthrough) // should send the OAuth bearer instead of forcing API-key fallback. // Collection endpoint - assert!(!requires_api_key_fallback("GET", "/api/v2/obs-pipelines/pipelines")); - assert!(!requires_api_key_fallback("POST", "/api/v2/obs-pipelines/pipelines")); + assert!(!requires_api_key_fallback( + "GET", + "/api/v2/obs-pipelines/pipelines" + )); + assert!(!requires_api_key_fallback( + "POST", + "/api/v2/obs-pipelines/pipelines" + )); // ID-parameterized endpoints (prefix match via trailing "/") assert!(!requires_api_key_fallback( "GET",