use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone)]
pub struct RequestDetails {
pub status_code: u16,
pub response_body: String,
pub request_body: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PagedEntity {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub slug: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PagedPolicy {
#[serde(skip_serializing_if = "Option::is_none")]
pub policy: Option<PagedEntity>,
#[serde(skip_serializing_if = "Option::is_none")]
pub team: Option<PagedEntity>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Transition {
#[serde(skip_serializing_if = "Option::is_none", rename = "Name")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "At")]
pub at: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none", rename = "Message")]
pub message: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "By")]
pub by: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "Manually")]
pub manually: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none", rename = "alertId")]
pub alert_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "alertUrl")]
pub alert_url: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Incident {
#[serde(skip_serializing_if = "Option::is_none", rename = "alertCount")]
pub alert_count: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none", rename = "currentPhase")]
pub current_phase: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "entityDisplayName")]
pub entity_display_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "entityId")]
pub entity_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "entityState")]
pub entity_state: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "entityType")]
pub entity_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub host: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "incidentNumber")]
pub incident_number: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "lastAlertId")]
pub last_alert_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "lastAlertTime")]
pub last_alert_time: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub service: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "startTime")]
pub start_time: Option<DateTime<Utc>>,
#[serde(default, skip_serializing_if = "Vec::is_empty", rename = "pagedTeams")]
pub paged_teams: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty", rename = "pagedUsers")]
pub paged_users: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty", rename = "pagedPolicies")]
pub paged_policies: Vec<PagedPolicy>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub transitions: Vec<Transition>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IncidentResponse {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub incidents: Vec<Incident>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
#[serde(skip_serializing_if = "Option::is_none", rename = "firstName")]
pub first_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "lastName")]
pub last_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub username: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub admin: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none", rename = "expirationHours")]
pub expiration_hours: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none", rename = "createdAt")]
pub created_at: Option<String>,
#[serde(
skip_serializing_if = "Option::is_none",
rename = "passwordLastUpdated"
)]
pub password_last_updated: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub verified: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserList {
pub users: Vec<Vec<User>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserListV2 {
pub users: Vec<User>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Team {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub slug: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "memberCount")]
pub member_count: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none", rename = "isDefaultTeam")]
pub is_default_team: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TeamMembers {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub members: Vec<User>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Admin {
#[serde(skip_serializing_if = "Option::is_none")]
pub username: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "firstName")]
pub first_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "lastName")]
pub last_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "_selfUrl")]
pub self_url: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TeamAdmins {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub admin: Vec<Admin>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContactMethod {
pub id: f64,
pub label: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailsResponse {
#[serde(rename = "contactMethods")]
pub contact_methods: Vec<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiTeam {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub slug: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiEscalationPolicy {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub slug: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiUser {
#[serde(skip_serializing_if = "Option::is_none")]
pub username: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiOnCallOverride {
#[serde(skip_serializing_if = "Option::is_none", rename = "origOnCallUser")]
pub orig_on_call_user: Option<ApiUser>,
#[serde(skip_serializing_if = "Option::is_none", rename = "overrideOnCallUser")]
pub override_on_call_user: Option<ApiUser>,
#[serde(skip_serializing_if = "Option::is_none")]
pub start: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub policy: Option<ApiEscalationPolicy>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiOnCallRoll {
#[serde(skip_serializing_if = "Option::is_none")]
pub start: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none", rename = "onCallUser")]
pub on_call_user: Option<ApiUser>,
#[serde(skip_serializing_if = "Option::is_none", rename = "isRoll")]
pub is_roll: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiOnCallEntry {
#[serde(skip_serializing_if = "Option::is_none", rename = "onCallUser")]
pub on_call_user: Option<ApiUser>,
#[serde(skip_serializing_if = "Option::is_none", rename = "overrideOnCallUser")]
pub override_on_call_user: Option<ApiUser>,
#[serde(skip_serializing_if = "Option::is_none", rename = "onCallType")]
pub on_call_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "rotationName")]
pub rotation_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "shiftName")]
pub shift_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "shiftRoll")]
pub shift_roll: Option<DateTime<Utc>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub rolls: Vec<ApiOnCallRoll>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiEscalationPolicySchedule {
#[serde(skip_serializing_if = "Option::is_none")]
pub policy: Option<ApiEscalationPolicy>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub schedule: Vec<ApiOnCallEntry>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub overrides: Vec<ApiOnCallOverride>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiTeamSchedule {
#[serde(skip_serializing_if = "Option::is_none")]
pub team: Option<ApiTeam>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub schedules: Vec<ApiEscalationPolicySchedule>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ApiUserSchedule {
#[serde(default, skip_serializing_if = "Vec::is_empty", rename = "teamSchedules")]
pub schedules: Vec<ApiTeamSchedule>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TakeRequest {
#[serde(skip_serializing_if = "Option::is_none", rename = "fromUser")]
pub from_user: Option<String>,
#[serde(skip_serializing_if = "Option::is_none", rename = "toUser")]
pub to_user: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TakeResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EscalationPolicyStepEntry {
#[serde(skip_serializing_if = "Option::is_none", rename = "executionType")]
pub execution_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user: Option<std::collections::HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none", rename = "rotationGroup")]
pub rotation_group: Option<std::collections::HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub webhook: Option<std::collections::HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<std::collections::HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none", rename = "targetPolicy")]
pub target_policy: Option<std::collections::HashMap<String, String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EscalationPolicySteps {
pub timeout: i32,
pub entries: Vec<EscalationPolicyStepEntry>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EscalationPolicy {
pub name: String,
#[serde(rename = "teamSlug")]
pub team_id: String,
#[serde(rename = "ignoreCustomPagingPolicies")]
pub ignore_custom_paging_policies: bool,
pub steps: Vec<EscalationPolicySteps>,
#[serde(rename = "slug")]
pub id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EscalationPolicyListDetail {
pub name: String,
pub slug: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EscalationPolicyListElement {
pub policy: EscalationPolicyListDetail,
pub team: EscalationPolicyListDetail,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EscalationPolicyList {
pub policies: Vec<EscalationPolicyListElement>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoutingKey {
#[serde(skip_serializing_if = "Option::is_none", rename = "routingKey")]
pub routing_key: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub targets: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoutingKeyResponseTargets {
#[serde(skip_serializing_if = "Option::is_none", rename = "policySlug")]
pub policy_slug: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoutingKeyResponse {
#[serde(skip_serializing_if = "Option::is_none", rename = "routingKey")]
pub routing_key: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub targets: Vec<RoutingKeyResponseTargets>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoutingKeyResponseList {
#[serde(default, skip_serializing_if = "Vec::is_empty", rename = "routingKeys")]
pub routing_keys: Vec<RoutingKeyResponse>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ContactType {
Phone,
Email,
Device,
}
impl ContactType {
pub fn endpoint_noun(&self) -> &'static str {
match self {
ContactType::Phone => "phones",
ContactType::Email => "emails",
ContactType::Device => "devices",
}
}
pub fn from_notification_type(notification_type: &str) -> Option<Self> {
match notification_type {
"push" => Some(ContactType::Device),
"email" => Some(ContactType::Email),
"phone" | "sms" => Some(ContactType::Phone),
_ => None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Contact {
#[serde(skip_serializing_if = "Option::is_none", rename = "phone")]
pub phone_number: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rank: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none", rename = "extId")]
pub ext_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub verified: Option<String>,
}
impl Contact {
pub fn contact_type(&self) -> Option<ContactType> {
if self.phone_number.is_some() {
Some(ContactType::Phone)
} else if self.email.is_some() {
Some(ContactType::Email)
} else {
None
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContactGroup {
#[serde(rename = "contactMethods")]
pub contact_methods: Vec<Contact>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AllContactResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub phones: Option<ContactGroup>,
#[serde(skip_serializing_if = "Option::is_none")]
pub emails: Option<ContactGroup>,
#[serde(skip_serializing_if = "Option::is_none")]
pub devices: Option<ContactGroup>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetAllContactResponse {
#[serde(default, skip_serializing_if = "Vec::is_empty", rename = "contactMethods")]
pub contact_methods: Vec<Contact>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_contact_type_endpoint_noun() {
assert_eq!(ContactType::Phone.endpoint_noun(), "phones");
assert_eq!(ContactType::Email.endpoint_noun(), "emails");
assert_eq!(ContactType::Device.endpoint_noun(), "devices");
}
#[test]
fn test_contact_type_from_notification_type() {
assert_eq!(
ContactType::from_notification_type("push"),
Some(ContactType::Device)
);
assert_eq!(
ContactType::from_notification_type("email"),
Some(ContactType::Email)
);
assert_eq!(
ContactType::from_notification_type("phone"),
Some(ContactType::Phone)
);
assert_eq!(
ContactType::from_notification_type("sms"),
Some(ContactType::Phone)
);
assert_eq!(ContactType::from_notification_type("unknown"), None);
}
#[test]
fn test_contact_contact_type() {
let phone_contact = Contact {
phone_number: Some("555-1234".to_string()),
email: None,
label: Some("Primary".to_string()),
rank: Some(1),
ext_id: None,
id: None,
value: None,
verified: None,
};
assert_eq!(phone_contact.contact_type(), Some(ContactType::Phone));
let email_contact = Contact {
phone_number: None,
email: Some("test@example.com".to_string()),
label: Some("Work".to_string()),
rank: Some(1),
ext_id: None,
id: None,
value: None,
verified: None,
};
assert_eq!(email_contact.contact_type(), Some(ContactType::Email));
let empty_contact = Contact {
phone_number: None,
email: None,
label: None,
rank: None,
ext_id: None,
id: None,
value: None,
verified: None,
};
assert_eq!(empty_contact.contact_type(), None);
}
#[test]
fn test_contact_serialization() {
let contact = Contact {
phone_number: Some("555-1234".to_string()),
email: Some("test@example.com".to_string()),
label: Some("Primary".to_string()),
rank: Some(1),
ext_id: Some("ext123".to_string()),
id: Some(42),
value: Some("contact-value".to_string()),
verified: Some("true".to_string()),
};
let json = serde_json::to_string(&contact).unwrap();
let deserialized: Contact = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.phone_number, contact.phone_number);
assert_eq!(deserialized.email, contact.email);
assert_eq!(deserialized.label, contact.label);
assert_eq!(deserialized.rank, contact.rank);
}
#[test]
fn test_contact_group_serialization() {
let contact_group = ContactGroup {
contact_methods: vec![
Contact {
phone_number: Some("555-1234".to_string()),
email: None,
label: Some("Primary".to_string()),
rank: Some(1),
ext_id: None,
id: None,
value: None,
verified: None,
}
],
};
let json = serde_json::to_string(&contact_group).unwrap();
assert!(json.contains("contactMethods"));
let deserialized: ContactGroup = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.contact_methods.len(), 1);
}
}