Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ zip = { version = "8", default-features = false, features = ["deflate"], optiona

# Datadog API client — pinned to 0.33.0 tag
# Use default-features = false; feature sets are activated per-target via features above
datadog-api-client = { git = "https://github.com/DataDog/datadog-api-client-rust", rev = "68fea1224e7ce303787b842811103b99c6406cf4", optional = true, default-features = false }
datadog-api-client = { git = "https://github.com/DataDog/datadog-api-client-rust", rev = "e32b89365fc8df6d564da0549de82a79f2381a3b", optional = true, default-features = false }

# HTTP middleware (version-matched to DD client; compiles on all targets)
reqwest-middleware = "0.5"
Expand Down
2 changes: 2 additions & 0 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Code generated by openapi-transformer. DO NOT EDIT.

use reqwest_middleware::{ClientBuilder, ClientWithMiddleware};

#[cfg(not(target_arch = "wasm32"))]
Expand Down
2 changes: 2 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Code generated by openapi-transformer. DO NOT EDIT.

use anyhow::{bail, Result};
#[cfg(not(feature = "browser"))]
use serde::Deserialize;
Expand Down
2 changes: 2 additions & 0 deletions src/formatter.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Code generated by openapi-transformer. DO NOT EDIT.

use anyhow::Result;
use serde::Serialize;

Expand Down
300 changes: 300 additions & 0 deletions src/generated/downtimes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
// Code generated by openapi-transformer. DO NOT EDIT.

use anyhow::Result;
use datadog_api_client::datadogV2::api_downtimes::{
DowntimesAPI, GetDowntimeOptionalParams, ListDowntimesOptionalParams,
ListMonitorDowntimesOptionalParams,
};
use datadog_api_client::datadogV2::model::{DowntimeCreateRequest, DowntimeUpdateRequest};

use crate::config::Config;
use crate::formatter;
use crate::util;

#[derive(clap::Subcommand)]
pub enum Command {
/// Cancel a downtime
///
/// Cancel a downtime.
///
/// **Note**: Downtimes canceled through the API are no longer active, but are retained for approximately two days before being permanently removed. The downtime may still appear in search results until it is permanently removed.
Cancel {
/// ID of the downtime to cancel.
downtime_id: String,
},
/// Schedule a downtime
///
/// Schedule a downtime.
Create {
#[arg(long)]
file: String,
},
/// Get a downtime
///
/// Get downtime detail by `downtime_id`.
Get {
/// ID of the downtime to fetch.
downtime_id: String,
/// Comma-separated list of resource paths for related resources to include in the response. Supported resource
/// paths are `created_by` and `monitor`.
#[arg(long)]
include: Option<String>,
/// If `true`, include the `run_as` attribute in the response, which lists the principals allowed to
/// act on behalf of the downtime.
///
/// **Note**: This feature is currently in Preview and may not be available for all organizations.
#[arg(long)]
with_run_as: Option<bool>,
},
/// Get all downtimes
///
/// List scheduled downtimes, optionally filtering to those that are active when the request is made.
List {
/// Only return downtimes that are active when the request is made.
#[arg(long)]
current_only: Option<bool>,
/// Comma-separated list of resource paths for related resources to include in the response. Supported resource
/// paths are `created_by` and `monitor`.
#[arg(long)]
include: Option<String>,
/// Specific offset to use as the beginning of the returned page.
#[arg(long)]
page_offset: Option<i64>,
/// Maximum number of downtimes in the response.
#[arg(long)]
page_limit: Option<i64>,
},
/// Get active downtimes for a monitor
///
/// Get all active downtimes for the specified monitor.
ListMonitor {
/// The id of the monitor.
monitor_id: i64,
/// Specific offset to use as the beginning of the returned page.
#[arg(long)]
page_offset: Option<i64>,
/// Maximum number of downtimes in the response.
#[arg(long)]
page_limit: Option<i64>,
},
/// Update a downtime
///
/// Update a downtime by `downtime_id`.
Update {
/// ID of the downtime to update.
downtime_id: String,
#[arg(long)]
file: String,
},
}

pub async fn run(cfg: &Config, command: Command) -> Result<()> {
match command {
Command::Cancel { downtime_id } => cancel(cfg, downtime_id).await,
Command::Create { file } => create(cfg, &file).await,
Command::Get {
downtime_id,
include,
with_run_as,
} => get(cfg, downtime_id, include, with_run_as).await,
Command::List {
current_only,
include,
page_offset,
page_limit,
} => list(cfg, current_only, include, page_offset, page_limit).await,
Command::ListMonitor {
monitor_id,
page_offset,
page_limit,
} => list_monitor(cfg, monitor_id, page_offset, page_limit).await,
Command::Update { downtime_id, file } => update(cfg, downtime_id, &file).await,
}
}

/// Cancel a downtime
///
/// Cancel a downtime.
///
/// **Note**: Downtimes canceled through the API are no longer active, but are retained for approximately two days before being permanently removed. The downtime may still appear in search results until it is permanently removed.
pub async fn cancel(cfg: &Config, downtime_id: String) -> Result<()> {
let api = crate::make_api!(DowntimesAPI, cfg);
api.cancel_downtime(downtime_id)
.await
.map_err(|e| anyhow::anyhow!("failed to cancel_downtime: {:?}", e))?;
println!("downtimes cancel: ok");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Use the shared formatter for cancel output

Agents and scripts cannot parse a successful cancel result.

Assertion details
  • Input: Run pup --agent downtimes cancel <valid-id> or request JSON output.
  • Expected: The command must use the selected output format and the agent JSON envelope.
  • Actual: The command prints downtimes cancel: ok as plain text.

Was this helpful? React 👍 or 👎
🤖 Datadog Autotest · What is Autotest? · @DataDog review to ask questions · Any feedback? Reach out in #autotest · Open Bits AI session

Ok(())
}

/// Schedule a downtime
///
/// Schedule a downtime.
pub async fn create(cfg: &Config, file: &str) -> Result<()> {
let body: DowntimeCreateRequest = util::read_json_file(file)?;
let api = crate::make_api!(DowntimesAPI, cfg);
let resp = api
.create_downtime(body)
.await
.map_err(|e| anyhow::anyhow!("failed to create_downtime: {:?}", e))?;
formatter::output(cfg, &resp)
}

/// Get a downtime
///
/// Get downtime detail by `downtime_id`.
pub async fn get(
cfg: &Config,
downtime_id: String,
include: Option<String>,
with_run_as: Option<bool>,
) -> Result<()> {
let api = crate::make_api!(DowntimesAPI, cfg);
let mut params = GetDowntimeOptionalParams::default();
if let Some(v) = include {
params = params.include(v);
}
if let Some(v) = with_run_as {
params = params.with_run_as(v);
}
let resp = api
.get_downtime(downtime_id, params)
.await
.map_err(|e| anyhow::anyhow!("failed to get_downtime: {:?}", e))?;
formatter::output(cfg, &resp)
}

/// Get all downtimes
///
/// List scheduled downtimes, optionally filtering to those that are active when the request is made.
pub async fn list(
cfg: &Config,
current_only: Option<bool>,
include: Option<String>,
page_offset: Option<i64>,
page_limit: Option<i64>,
) -> Result<()> {
let api = crate::make_api!(DowntimesAPI, cfg);
let mut params = ListDowntimesOptionalParams::default();
if let Some(v) = current_only {
params = params.current_only(v);
}
if let Some(v) = include {
params = params.include(v);
}
if let Some(v) = page_offset {
params = params.page_offset(v);
}
if let Some(v) = page_limit {
params = params.page_limit(v);
}
let resp = api
.list_downtimes(params)
.await
.map_err(|e| anyhow::anyhow!("failed to list_downtimes: {:?}", e))?;
let count = resp.data.as_ref().map_or(0, |d| d.len());
let truncated = false;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Report partial downtime pages

Agents can stop after the first page and omit valid downtimes.

Assertion details
  • Input: The API returns fewer records than total_filtered_count, such as 30 of 31.
  • Expected: The metadata must mark the page as partial and give the next offset.
  • Actual: Both list functions set truncated to false and omit the next action.

Was this helpful? React 👍 or 👎
🤖 Datadog Autotest · What is Autotest? · @DataDog review to ask questions · Any feedback? Reach out in #autotest · Open Bits AI session

let next_action: Option<String> = None;
let meta = formatter::Metadata {
count: Some(count),
truncated,
command: Some("downtimes list".to_string()),
next_action,
};
formatter::format_and_print(
&resp,
&cfg.output_format,
cfg.agent_mode,
Some(&meta),
cfg.jq.as_deref(),
)
}

/// Get active downtimes for a monitor
///
/// Get all active downtimes for the specified monitor.
pub async fn list_monitor(
cfg: &Config,
monitor_id: i64,
page_offset: Option<i64>,
page_limit: Option<i64>,
) -> Result<()> {
let api = crate::make_api!(DowntimesAPI, cfg);
let mut params = ListMonitorDowntimesOptionalParams::default();
if let Some(v) = page_offset {
params = params.page_offset(v);
}
if let Some(v) = page_limit {
params = params.page_limit(v);
}
let resp = api
.list_monitor_downtimes(monitor_id, params)
.await
.map_err(|e| anyhow::anyhow!("failed to list_monitor_downtimes: {:?}", e))?;
let count = resp.data.as_ref().map_or(0, |d| d.len());
let truncated = false;
let next_action: Option<String> = None;
let meta = formatter::Metadata {
count: Some(count),
truncated,
command: Some("downtimes list_monitor".to_string()),
next_action,
};
formatter::format_and_print(
&resp,
&cfg.output_format,
cfg.agent_mode,
Some(&meta),
cfg.jq.as_deref(),
)
}

/// Update a downtime
///
/// Update a downtime by `downtime_id`.
pub async fn update(cfg: &Config, downtime_id: String, file: &str) -> Result<()> {
let body: DowntimeUpdateRequest = util::read_json_file(file)?;
let api = crate::make_api!(DowntimesAPI, cfg);
let resp = api
.update_downtime(downtime_id, body)
.await
.map_err(|e| anyhow::anyhow!("failed to update_downtime: {:?}", e))?;
formatter::output(cfg, &resp)
}

#[cfg(test)]
mod tests {
use crate::test_support::*;

#[tokio::test]
async fn test_get_ok() {
let _lock = lock_env().await;
let mut server = mockito::Server::new_async().await;
let cfg = test_config(&server.url());
let _mock = mock_any(&mut server, "GET", r##"{"data": {"attributes": {"created": "2024-01-01T00:00:00+00:00", "display_timezone": "America/New_York", "message": "Message about the downtime", "modified": "2024-01-01T00:00:00+00:00", "monitor_identifier": {"monitor_tags": ["*"]}, "mute_first_recovery_notification": false, "notify_end_states": ["alert", "warn"], "notify_end_types": ["canceled", "expired"], "scope": "env:(staging OR prod) AND datacenter:us-east-1", "status": "active"}, "id": "00000000-0000-1234-0000-000000000000", "type": "downtime"}}"##).await;
let result = super::get(&cfg, "test".to_string(), None, None).await;
assert!(result.is_ok(), "get failed: {:?}", result.err());
cleanup_env();
}

#[tokio::test]
async fn test_list_ok() {
let _lock = lock_env().await;
let mut server = mockito::Server::new_async().await;
let cfg = test_config(&server.url());
let _mock = mock_any(&mut server, "GET", r##"{"data": [{"attributes": {"created": "2024-01-01T00:00:00+00:00", "display_timezone": "America/New_York", "message": "Message about the downtime", "modified": "2024-01-01T00:00:00+00:00", "monitor_identifier": {"monitor_tags": ["*"]}, "mute_first_recovery_notification": false, "notify_end_states": ["alert", "warn"], "notify_end_types": ["canceled", "expired"], "scope": "env:(staging OR prod) AND datacenter:us-east-1", "status": "active"}, "id": "00000000-0000-1234-0000-000000000000", "type": "downtime"}], "meta": {"page": {"total_filtered_count": 1}}}"##).await;
let result = super::list(&cfg, None, None, None, None).await;
assert!(result.is_ok(), "list failed: {:?}", result.err());
cleanup_env();
}

#[tokio::test]
async fn test_list_monitor_ok() {
let _lock = lock_env().await;
let mut server = mockito::Server::new_async().await;
let cfg = test_config(&server.url());
let _mock = mock_any(&mut server, "GET", r##"{"data": [{"attributes": {"end": "2024-01-01T01:00:00+00:00", "groups": ["service:postgres"], "scope": "env:(staging OR prod) AND datacenter:us-east-1", "start": "2024-01-01T00:00:00+00:00"}, "id": "00000000-0000-1234-0000-000000000000", "type": "downtime_match"}], "meta": {"page": {"total_filtered_count": 1}}}"##).await;
let result = super::list_monitor(&cfg, 1, None, None).await;
assert!(result.is_ok(), "list_monitor failed: {:?}", result.err());
cleanup_env();
}
}
25 changes: 18 additions & 7 deletions src/generated/mod.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
// Stable integration point for openapi-transformer-generated pup commands.
//
// Future regenerations add variants to `GeneratedCommand` and modules within
// this directory in place; main.rs never needs to change again.
// Code generated by openapi-transformer. DO NOT EDIT.

pub mod downtimes;

use anyhow::Result;

use crate::config::Config;

/// All spec-generated pup commands.
///
/// Re-run the generator to add or remove tags; pup never needs manual changes
/// for new commands.
#[derive(clap::Subcommand)]
pub enum GeneratedCommand {}
pub enum GeneratedCommand {
/// Manage downtimes resources
Downtimes {
#[command(subcommand)]
action: downtimes::Command,
},
}

pub async fn run(_cfg: &Config, command: GeneratedCommand) -> Result<()> {
match command {}
pub async fn run(cfg: &Config, command: GeneratedCommand) -> Result<()> {
match command {
GeneratedCommand::Downtimes { action } => downtimes::run(cfg, action).await,
}
}
2 changes: 2 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Code generated by openapi-transformer. DO NOT EDIT.

use anyhow::Result;

/// Read a JSON file and deserialize into the specified type.
Expand Down
Loading