use anyhow::{Context, Result};
use serde::Serialize;
use super::utils::{ApiResponse, Heartbeat, OpsgenieContext};
pub async fn list_heartbeats(ctx: &OpsgenieContext<'_>) -> Result<()> {
#[derive(serde::Deserialize)]
struct HeartbeatsResponse {
data: HeartbeatsData,
}
#[derive(serde::Deserialize)]
struct HeartbeatsData {
heartbeats: Vec<Heartbeat>,
}
tracing::debug!("Listing heartbeats");
let response: HeartbeatsResponse = ctx
.client
.get("heartbeats")
.await
.context("Failed to list heartbeats")?;
#[derive(Serialize)]
struct Row<'a> {
name: &'a str,
description: &'a str,
interval: i32,
interval_unit: &'a str,
enabled: bool,
expired: bool,
owner_team: &'a str,
}
let rows: Vec<Row<'_>> = response
.data
.heartbeats
.iter()
.map(|hb| Row {
name: &hb.name,
description: hb.description.as_deref().unwrap_or(""),
interval: hb.interval.unwrap_or(0),
interval_unit: hb.interval_unit.as_deref().unwrap_or(""),
enabled: hb.enabled.unwrap_or(true),
expired: hb.expired.unwrap_or(false),
owner_team: hb
.owner_team
.as_ref()
.and_then(|t| t.name.as_deref())
.unwrap_or(""),
})
.collect();
if rows.is_empty() {
tracing::info!("No heartbeats found");
println!("No heartbeats found");
return Ok(());
}
tracing::info!("Found {} heartbeats", rows.len());
ctx.renderer.render(&rows)
}
pub async fn get_heartbeat(ctx: &OpsgenieContext<'_>, name: &str) -> Result<()> {
let path = format!("heartbeats/{}", name);
tracing::debug!("Fetching heartbeat {}", name);
let response: ApiResponse<Heartbeat> = ctx
.client
.get(&path)
.await
.with_context(|| format!("Failed to fetch heartbeat {}", name))?;
let hb = response.data;
#[derive(Serialize)]
struct View<'a> {
name: &'a str,
description: &'a str,
interval: i32,
interval_unit: &'a str,
enabled: bool,
expired: bool,
owner_team: &'a str,
}
let view = View {
name: &hb.name,
description: hb.description.as_deref().unwrap_or(""),
interval: hb.interval.unwrap_or(0),
interval_unit: hb.interval_unit.as_deref().unwrap_or(""),
enabled: hb.enabled.unwrap_or(true),
expired: hb.expired.unwrap_or(false),
owner_team: hb
.owner_team
.as_ref()
.and_then(|t| t.name.as_deref())
.unwrap_or(""),
};
tracing::info!("Retrieved heartbeat {}", name);
ctx.renderer.render(&view)
}
pub async fn create_heartbeat(
ctx: &OpsgenieContext<'_>,
name: String,
description: Option<String>,
interval: i32,
interval_unit: String,
team_id: Option<String>,
) -> Result<()> {
#[derive(Serialize)]
struct CreateHeartbeatBody {
name: String,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
interval: i32,
#[serde(rename = "intervalUnit")]
interval_unit: String,
enabled: bool,
#[serde(rename = "ownerTeam", skip_serializing_if = "Option::is_none")]
owner_team: Option<TeamRef>,
}
#[derive(Serialize)]
struct TeamRef {
id: String,
}
let body = CreateHeartbeatBody {
name: name.clone(),
description,
interval,
interval_unit,
enabled: true,
owner_team: team_id.map(|id| TeamRef { id }),
};
tracing::debug!("Creating heartbeat: {}", name);
#[derive(serde::Deserialize)]
struct CreateResult {
name: String,
}
let response: ApiResponse<CreateResult> = ctx
.client
.post("heartbeats", &body)
.await
.context("Failed to create heartbeat")?;
tracing::info!("Created heartbeat {}", response.data.name);
println!("Created heartbeat: {}", response.data.name);
Ok(())
}
pub async fn delete_heartbeat(ctx: &OpsgenieContext<'_>, name: &str) -> Result<()> {
let path = format!("heartbeats/{}", name);
tracing::debug!("Deleting heartbeat {}", name);
ctx.client
.delete::<serde_json::Value>(&path)
.await
.with_context(|| format!("Failed to delete heartbeat {}", name))?;
tracing::info!("Deleted heartbeat {}", name);
println!("Successfully deleted heartbeat {}", name);
Ok(())
}
pub async fn enable_heartbeat(ctx: &OpsgenieContext<'_>, name: &str) -> Result<()> {
let path = format!("heartbeats/{}/enable", name);
tracing::debug!("Enabling heartbeat {}", name);
ctx.client
.post::<serde_json::Value, _>(&path, &serde_json::json!({}))
.await
.with_context(|| format!("Failed to enable heartbeat {}", name))?;
tracing::info!("Enabled heartbeat {}", name);
println!("Successfully enabled heartbeat {}", name);
Ok(())
}
pub async fn disable_heartbeat(ctx: &OpsgenieContext<'_>, name: &str) -> Result<()> {
let path = format!("heartbeats/{}/disable", name);
tracing::debug!("Disabling heartbeat {}", name);
ctx.client
.post::<serde_json::Value, _>(&path, &serde_json::json!({}))
.await
.with_context(|| format!("Failed to disable heartbeat {}", name))?;
tracing::info!("Disabled heartbeat {}", name);
println!("Successfully disabled heartbeat {}", name);
Ok(())
}
pub async fn ping_heartbeat(ctx: &OpsgenieContext<'_>, name: &str) -> Result<()> {
let path = format!("heartbeats/{}/ping", name);
tracing::debug!("Pinging heartbeat {}", name);
ctx.client
.get::<serde_json::Value>(&path)
.await
.with_context(|| format!("Failed to ping heartbeat {}", name))?;
tracing::info!("Pinged heartbeat {}", name);
println!("Successfully pinged heartbeat {}", name);
Ok(())
}