use anyhow::{Context, Result};
use serde::Serialize;
use super::utils::{ApiResponse, OpsgenieContext, PagedResponse, Team, TeamMember};
pub async fn list_teams(ctx: &OpsgenieContext<'_>, limit: usize) -> Result<()> {
let path = format!("teams?limit={}", limit.min(100));
tracing::debug!("Listing teams with limit {}", limit);
let response: PagedResponse<Team> = ctx
.client
.get(&path)
.await
.context("Failed to list teams")?;
#[derive(Serialize)]
struct Row<'a> {
id: &'a str,
name: &'a str,
description: &'a str,
}
let rows: Vec<Row<'_>> = response
.data
.iter()
.map(|team| Row {
id: &team.id,
name: &team.name,
description: team.description.as_deref().unwrap_or(""),
})
.collect();
if rows.is_empty() {
tracing::info!("No teams found");
println!("No teams found");
return Ok(());
}
tracing::info!("Found {} teams", rows.len());
ctx.renderer.render(&rows)
}
pub async fn get_team(ctx: &OpsgenieContext<'_>, identifier: &str) -> Result<()> {
let path = format!("teams/{}", identifier);
tracing::debug!("Fetching team {}", identifier);
let response: ApiResponse<Team> = ctx
.client
.get(&path)
.await
.with_context(|| format!("Failed to fetch team {}", identifier))?;
let team = response.data;
#[derive(Serialize)]
struct View<'a> {
id: &'a str,
name: &'a str,
description: &'a str,
}
let view = View {
id: &team.id,
name: &team.name,
description: team.description.as_deref().unwrap_or(""),
};
tracing::info!("Retrieved team {}", identifier);
ctx.renderer.render(&view)
}
pub async fn create_team(
ctx: &OpsgenieContext<'_>,
name: String,
description: Option<String>,
) -> Result<()> {
#[derive(Serialize)]
struct CreateTeamBody {
name: String,
#[serde(skip_serializing_if = "Option::is_none")]
description: Option<String>,
}
let body = CreateTeamBody {
name: name.clone(),
description,
};
tracing::debug!("Creating team: {}", name);
#[derive(serde::Deserialize)]
struct CreateResult {
id: String,
name: String,
}
let response: ApiResponse<CreateResult> = ctx
.client
.post("teams", &body)
.await
.context("Failed to create team")?;
tracing::info!(
"Created team {} (id: {})",
response.data.name,
response.data.id
);
println!(
"Created team: {} (id: {})",
response.data.name, response.data.id
);
Ok(())
}
pub async fn delete_team(ctx: &OpsgenieContext<'_>, identifier: &str) -> Result<()> {
let path = format!("teams/{}", identifier);
tracing::debug!("Deleting team {}", identifier);
ctx.client
.delete::<serde_json::Value>(&path)
.await
.with_context(|| format!("Failed to delete team {}", identifier))?;
tracing::info!("Deleted team {}", identifier);
println!("Successfully deleted team {}", identifier);
Ok(())
}
pub async fn list_members(ctx: &OpsgenieContext<'_>, identifier: &str) -> Result<()> {
#[derive(serde::Deserialize)]
struct MembersData {
members: Vec<TeamMember>,
}
let path = format!("teams/{}", identifier);
tracing::debug!("Listing members for team {}", identifier);
let response: ApiResponse<MembersData> = ctx
.client
.get(&path)
.await
.with_context(|| format!("Failed to list members for team {}", identifier))?;
#[derive(Serialize)]
struct Row<'a> {
id: &'a str,
username: &'a str,
role: &'a str,
}
let rows: Vec<Row<'_>> = response
.data
.members
.iter()
.map(|member| Row {
id: member.user.id.as_deref().unwrap_or(""),
username: member.user.username.as_deref().unwrap_or(""),
role: member.role.as_deref().unwrap_or(""),
})
.collect();
if rows.is_empty() {
tracing::info!("No members in team {}", identifier);
println!("No members found");
return Ok(());
}
tracing::info!("Found {} members in team {}", rows.len(), identifier);
ctx.renderer.render(&rows)
}
pub async fn add_member(
ctx: &OpsgenieContext<'_>,
identifier: &str,
user_id: &str,
role: Option<&str>,
) -> Result<()> {
#[derive(Serialize)]
struct AddMemberBody<'a> {
user: UserRef<'a>,
#[serde(skip_serializing_if = "Option::is_none")]
role: Option<&'a str>,
}
#[derive(Serialize)]
struct UserRef<'a> {
id: &'a str,
}
let path = format!("teams/{}/members", identifier);
let body = AddMemberBody {
user: UserRef { id: user_id },
role,
};
tracing::debug!("Adding user {} to team {}", user_id, identifier);
ctx.client
.post::<serde_json::Value, _>(&path, &body)
.await
.with_context(|| format!("Failed to add user {} to team {}", user_id, identifier))?;
tracing::info!("Added user {} to team {}", user_id, identifier);
println!("Successfully added user {} to team {}", user_id, identifier);
Ok(())
}
pub async fn remove_member(
ctx: &OpsgenieContext<'_>,
identifier: &str,
user_id: &str,
) -> Result<()> {
let path = format!("teams/{}/members/{}", identifier, user_id);
tracing::debug!("Removing user {} from team {}", user_id, identifier);
ctx.client
.delete::<serde_json::Value>(&path)
.await
.with_context(|| format!("Failed to remove user {} from team {}", user_id, identifier))?;
tracing::info!("Removed user {} from team {}", user_id, identifier);
println!(
"Successfully removed user {} from team {}",
user_id, identifier
);
Ok(())
}
pub async fn get_on_call(
ctx: &OpsgenieContext<'_>,
identifier: &str,
date: Option<&str>,
) -> Result<()> {
#[derive(serde::Deserialize)]
struct OnCallData {
#[serde(rename = "onCallParticipants")]
on_call_participants: Vec<OnCallEntry>,
}
#[derive(serde::Deserialize)]
struct OnCallEntry {
id: Option<String>,
name: Option<String>,
#[serde(rename = "type")]
participant_type: Option<String>,
}
let mut path = format!("teams/{}/on-calls", identifier);
if let Some(d) = date {
path = format!("{}?date={}", path, d);
}
tracing::debug!("Getting on-call for team {}", identifier);
let response: ApiResponse<OnCallData> = ctx
.client
.get(&path)
.await
.with_context(|| format!("Failed to get on-call for team {}", identifier))?;
#[derive(Serialize)]
struct Row<'a> {
id: &'a str,
name: &'a str,
participant_type: &'a str,
}
let rows: Vec<Row<'_>> = response
.data
.on_call_participants
.iter()
.map(|p| Row {
id: p.id.as_deref().unwrap_or(""),
name: p.name.as_deref().unwrap_or(""),
participant_type: p.participant_type.as_deref().unwrap_or(""),
})
.collect();
if rows.is_empty() {
tracing::info!("No one is on-call for team {}", identifier);
println!("No one is on-call");
return Ok(());
}
tracing::info!(
"Found {} on-call participants for team {}",
rows.len(),
identifier
);
ctx.renderer.render(&rows)
}