diff --git a/src/commands/api.rs b/src/commands/api.rs index 7dd7dde5..e4302276 100644 --- a/src/commands/api.rs +++ b/src/commands/api.rs @@ -870,13 +870,12 @@ mod tests { cleanup_env(); } - /// OAuth-excluded endpoints (e.g. GET /api/v2/fleet/agents) must use API-key + /// OAuth-excluded endpoints (e.g. GET /api/v2/test-oauth-excluded/some-id) must use API-key /// auth even when a bearer token is present. This exercises the reuse of /// raw_client::apply_auth's per-endpoint fallback table. /// - /// Fleet Automation is just today's example of a still-excluded endpoint, - /// not a claim it's meant to stay that way -- update this test if/when - /// Fleet gets OAuth support too. + /// Uses a test fixture entry (permanently excluded) so this test doesn't + /// churn when real endpoints gain OAuth support. #[tokio::test] async fn test_api_oauth_excluded_uses_api_keys() { let _lock = lock_env().await; @@ -886,7 +885,7 @@ mod tests { // must prefer the API keys. cfg.access_token = Some("bearer-token".into()); let _mock = server - .mock("GET", "/api/v2/fleet/agents") + .mock("GET", "/api/v2/test-oauth-excluded/some-id") .match_query(mockito::Matcher::Any) .match_header("DD-API-KEY", "test-api-key") .match_header("DD-APPLICATION-KEY", "test-app-key") @@ -899,7 +898,7 @@ mod tests { let result = super::run( &cfg, - "v2/fleet/agents", + "v2/test-oauth-excluded/some-id", "GET", &[], &[], @@ -927,7 +926,7 @@ mod tests { let mut cfg = test_config(&server.url()); cfg.access_token = Some("bearer-token".into()); let _mock = server - .mock("GET", "/api/v2/fleet/agents") + .mock("GET", "/api/v2/test-oauth-excluded/some-id") .match_query(mockito::Matcher::Any) .match_header("DD-API-KEY", "test-api-key") .match_header("authorization", mockito::Matcher::Missing) @@ -938,7 +937,7 @@ mod tests { .await; // Pass the fully-qualified URL, not a relative path. - let absolute = format!("{}/api/v2/fleet/agents", server.url()); + let absolute = format!("{}/api/v2/test-oauth-excluded/some-id", server.url()); let result = super::run( &cfg, &absolute, diff --git a/src/raw_client.rs b/src/raw_client.rs index ed14c965..7d2e89b4 100644 --- a/src/raw_client.rs +++ b/src/raw_client.rs @@ -120,6 +120,12 @@ fn find_endpoint_requirement(method: &str, path: &str) -> Option<&'static Endpoi /// Endpoints that don't support OAuth. /// Trailing "/" means prefix match for ID-parameterized paths. static OAUTH_EXCLUDED_ENDPOINTS: &[EndpointRequirement] = &[ + // Test fixture (1) — not a real API; exists only so tests that need a + // "still excluded" example don't churn when real endpoints gain OAuth. + EndpointRequirement { + path: "/api/v2/test-oauth-excluded/", + method: "GET", + }, // DDSQL editor tools (3) EndpointRequirement { path: "/api/unstable/ddsql-editor/tools/ddsql-docs", @@ -133,162 +139,6 @@ static OAUTH_EXCLUDED_ENDPOINTS: &[EndpointRequirement] = &[ path: "/api/unstable/ddsql-editor/tools/table-data", method: "POST", }, - // Fleet Automation (15) - EndpointRequirement { - path: "/api/v2/fleet/agents", - method: "GET", - }, - EndpointRequirement { - path: "/api/v2/fleet/agents/", - method: "GET", - }, - EndpointRequirement { - path: "/api/v2/fleet/agents/versions", - method: "GET", - }, - EndpointRequirement { - path: "/api/v2/fleet/deployments", - method: "GET", - }, - EndpointRequirement { - path: "/api/v2/fleet/deployments/", - method: "GET", - }, - EndpointRequirement { - path: "/api/v2/fleet/deployments/configure", - method: "POST", - }, - EndpointRequirement { - path: "/api/v2/fleet/deployments/upgrade", - method: "POST", - }, - EndpointRequirement { - path: "/api/v2/fleet/deployments/", - method: "POST", - }, - EndpointRequirement { - path: "/api/v2/fleet/deployments/", - method: "DELETE", - }, - EndpointRequirement { - path: "/api/v2/fleet/schedules", - method: "GET", - }, - EndpointRequirement { - path: "/api/v2/fleet/schedules/", - method: "GET", - }, - EndpointRequirement { - path: "/api/v2/fleet/schedules", - method: "POST", - }, - EndpointRequirement { - path: "/api/v2/fleet/schedules/", - method: "PATCH", - }, - EndpointRequirement { - path: "/api/v2/fleet/schedules/", - method: "DELETE", - }, - 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", - method: "GET", - }, - EndpointRequirement { - path: "/api/v2/usage/cost_by_org", - method: "GET", - }, - EndpointRequirement { - path: "/api/v2/cost_by_tag/monthly_cost_attribution", - method: "GET", - }, - // Cloud Cost Management config (12) - EndpointRequirement { - path: "/api/v2/cost/aws_cur_config", - method: "GET", - }, - EndpointRequirement { - path: "/api/v2/cost/aws_cur_config", - method: "POST", - }, - EndpointRequirement { - path: "/api/v2/cost/aws_cur_config/", - method: "GET", - }, - EndpointRequirement { - path: "/api/v2/cost/aws_cur_config/", - method: "DELETE", - }, - EndpointRequirement { - path: "/api/v2/cost/azure_uc_config", - method: "GET", - }, - EndpointRequirement { - path: "/api/v2/cost/azure_uc_config", - method: "POST", - }, - EndpointRequirement { - path: "/api/v2/cost/azure_uc_config/", - method: "GET", - }, - EndpointRequirement { - path: "/api/v2/cost/azure_uc_config/", - method: "DELETE", - }, - EndpointRequirement { - path: "/api/v2/cost/gcp_uc_config", - method: "GET", - }, - EndpointRequirement { - path: "/api/v2/cost/gcp_uc_config", - method: "POST", - }, - EndpointRequirement { - path: "/api/v2/cost/gcp_uc_config/", - method: "GET", - }, - EndpointRequirement { - path: "/api/v2/cost/gcp_uc_config/", - method: "DELETE", - }, - EndpointRequirement { - path: "/api/v2/cost/oci_config", - method: "GET", - }, - EndpointRequirement { - path: "/api/v2/cost/anomalies", - method: "GET", - }, // Profiling (4) // No OAuth scope is declared for Continuous Profiler endpoints; force API-key auth. EndpointRequirement { @@ -818,10 +668,11 @@ mod tests { #[test] fn test_prefix_matching_with_id() { - // Trailing "/" in the pattern should match paths with IDs + // Trailing "/" in the pattern should match paths with IDs. + // Uses the test fixture entry (permanently excluded) as the example. assert!(requires_api_key_fallback( "GET", - "/api/v2/fleet/agents/agent-123" + "/api/v2/test-oauth-excluded/some-id" )); } @@ -834,11 +685,6 @@ mod tests { )); } - #[test] - fn test_oauth_excluded_count() { - assert_eq!(OAUTH_EXCLUDED_ENDPOINTS.len(), 46); - } - #[test] fn test_no_fallback_for_notebooks() { assert!(!requires_api_key_fallback("GET", "/api/v1/notebooks")); @@ -847,12 +693,27 @@ mod tests { } #[test] - fn test_requires_api_key_fallback_fleet() { - assert!(requires_api_key_fallback("GET", "/api/v2/fleet/agents")); - assert!(requires_api_key_fallback( + fn test_no_fallback_for_fleet() { + // Fleet Automation v2 routes already accept OAuth server-side; + // the raw/generic `pup api` passthrough should use the OAuth bearer + // like the typed fleet commands do. + assert!(!requires_api_key_fallback("GET", "/api/v2/fleet/agents")); + assert!(!requires_api_key_fallback( "GET", "/api/v2/fleet/agents/agent-123" )); + assert!(!requires_api_key_fallback( + "GET", + "/api/v2/fleet/deployments" + )); + assert!(!requires_api_key_fallback( + "POST", + "/api/v2/fleet/deployments/configure" + )); + assert!(!requires_api_key_fallback( + "POST", + "/api/v2/fleet/schedules/sched-123/trigger" + )); } #[test] @@ -1016,16 +877,15 @@ mod tests { #[test] fn test_other_oauth_excluded_endpoints_still_require_both_keys() { - // Uses Fleet Automation as a currently-still-excluded example. This is - // just today's state of OAUTH_EXCLUDED_ENDPOINTS, not a claim that Fleet - // (or anything else in the table) is meant to stay that way -- update - // this example if/when its entries get OAuth support and are removed. + // Uses the test fixture entry (permanently excluded) so this test + // doesn't churn when real endpoints gain OAuth support. let mut cfg = test_cfg(); cfg.app_key = None; - let req = reqwest::Client::new().get("https://api.datadoghq.com/api/v2/fleet/agents"); + let req = reqwest::Client::new() + .get("https://api.datadoghq.com/api/v2/test-oauth-excluded/some-id"); - let err = match apply_auth(req, &cfg, "GET", "/api/v2/fleet/agents") { - Ok(_) => panic!("Fleet Automation should require both keys"), + let err = match apply_auth(req, &cfg, "GET", "/api/v2/test-oauth-excluded/some-id") { + Ok(_) => panic!("test fixture should require both keys"), Err(err) => err, }; assert!(err.to_string().contains("DD_API_KEY and DD_APP_KEY"));