use std::collections::HashMap;
use reqwest::Method;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::{client::Sendry, error::Error, DeleteResponse, Page};
#[derive(Debug, Clone)]
pub struct Automations {
client: Sendry,
}
impl Automations {
pub(crate) fn new(client: Sendry) -> Self {
Self { client }
}
pub async fn list(&self, params: ListAutomations) -> Result<Page<Automation>, Error> {
let q = params.to_query();
self.client
.request(
self.client
.build::<()>(Method::GET, "/v1/automations", &q, None),
)
.await
}
pub async fn get(&self, id: &str) -> Result<Automation, Error> {
self.client
.request(self.client.build::<()>(
Method::GET,
&format!("/v1/automations/{id}"),
&[],
None,
))
.await
}
pub async fn create(&self, params: CreateAutomation) -> Result<Automation, Error> {
self.client
.request(
self.client
.build(Method::POST, "/v1/automations", &[], Some(¶ms)),
)
.await
}
pub async fn update(
&self,
id: &str,
params: UpdateAutomation,
) -> Result<Automation, Error> {
self.client
.request(self.client.build(
Method::PATCH,
&format!("/v1/automations/{id}"),
&[],
Some(¶ms),
))
.await
}
pub async fn delete(&self, id: &str) -> Result<DeleteResponse, Error> {
self.client
.request(self.client.build::<()>(
Method::DELETE,
&format!("/v1/automations/{id}"),
&[],
None,
))
.await
}
pub async fn activate(&self, id: &str) -> Result<Automation, Error> {
self.client
.request(self.client.build::<()>(
Method::POST,
&format!("/v1/automations/{id}/activate"),
&[],
None,
))
.await
}
pub async fn pause(&self, id: &str) -> Result<Automation, Error> {
self.client
.request(self.client.build::<()>(
Method::POST,
&format!("/v1/automations/{id}/pause"),
&[],
None,
))
.await
}
pub async fn archive(&self, id: &str) -> Result<Automation, Error> {
self.client
.request(self.client.build::<()>(
Method::POST,
&format!("/v1/automations/{id}/archive"),
&[],
None,
))
.await
}
#[must_use]
pub fn steps(&self) -> AutomationSteps {
AutomationSteps { client: self.client.clone() }
}
#[must_use]
pub fn runs(&self) -> AutomationRuns {
AutomationRuns { client: self.client.clone() }
}
}
#[derive(Debug, Clone)]
pub struct AutomationSteps {
client: Sendry,
}
impl AutomationSteps {
pub async fn list(&self, automation_id: &str) -> Result<AutomationStepList, Error> {
self.client
.request(self.client.build::<()>(
Method::GET,
&format!("/v1/automations/{automation_id}/steps"),
&[],
None,
))
.await
}
pub async fn add(
&self,
automation_id: &str,
params: AddAutomationStep,
) -> Result<AutomationStep, Error> {
self.client
.request(self.client.build(
Method::POST,
&format!("/v1/automations/{automation_id}/steps"),
&[],
Some(¶ms),
))
.await
}
pub async fn update(
&self,
automation_id: &str,
step_id: &str,
params: UpdateAutomationStep,
) -> Result<AutomationStep, Error> {
self.client
.request(self.client.build(
Method::PATCH,
&format!("/v1/automations/{automation_id}/steps/{step_id}"),
&[],
Some(¶ms),
))
.await
}
pub async fn delete(
&self,
automation_id: &str,
step_id: &str,
) -> Result<DeleteResponse, Error> {
self.client
.request(self.client.build::<()>(
Method::DELETE,
&format!("/v1/automations/{automation_id}/steps/{step_id}"),
&[],
None,
))
.await
}
}
#[derive(Debug, Clone)]
pub struct AutomationRuns {
client: Sendry,
}
impl AutomationRuns {
pub async fn list(
&self,
automation_id: &str,
params: ListAutomationRuns,
) -> Result<Page<AutomationRun>, Error> {
let q = params.to_query();
self.client
.request(self.client.build::<()>(
Method::GET,
&format!("/v1/automations/{automation_id}/runs"),
&q,
None,
))
.await
}
pub async fn get(
&self,
automation_id: &str,
run_id: &str,
) -> Result<AutomationRun, Error> {
self.client
.request(self.client.build::<()>(
Method::GET,
&format!("/v1/automations/{automation_id}/runs/{run_id}"),
&[],
None,
))
.await
}
pub async fn list_steps(
&self,
automation_id: &str,
run_id: &str,
) -> Result<AutomationRunStepList, Error> {
self.client
.request(self.client.build::<()>(
Method::GET,
&format!("/v1/automations/{automation_id}/runs/{run_id}/steps"),
&[],
None,
))
.await
}
pub async fn cancel(
&self,
automation_id: &str,
run_id: &str,
) -> Result<AutomationRun, Error> {
self.client
.request(self.client.build::<()>(
Method::POST,
&format!("/v1/automations/{automation_id}/runs/{run_id}/cancel"),
&[],
None,
))
.await
}
pub async fn create(
&self,
automation_id: &str,
params: CreateAutomationRun,
) -> Result<AutomationRun, Error> {
self.client
.request(self.client.build(
Method::POST,
&format!("/v1/automations/{automation_id}/runs"),
&[],
Some(¶ms),
))
.await
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct Automation {
pub id: String,
pub name: String,
pub description: Option<String>,
pub status: String,
pub trigger_type: String,
pub trigger_config: Value,
pub entry_segment_id: Option<String>,
pub reentry_policy: String,
pub reentry_cooldown_seconds: Option<u64>,
pub total_runs: u64,
pub active_runs: u64,
pub completed_runs: u64,
pub failed_runs: u64,
pub created_at: String,
pub updated_at: String,
}
#[derive(Debug, Clone, Default)]
pub struct ListAutomations {
pub limit: Option<u32>,
pub cursor: Option<String>,
pub status: Option<String>,
}
impl ListAutomations {
fn to_query(&self) -> Vec<(&'static str, String)> {
let mut q = Vec::new();
if let Some(v) = self.limit {
q.push(("limit", v.to_string()));
}
if let Some(v) = &self.cursor {
q.push(("cursor", v.clone()));
}
if let Some(v) = &self.status {
q.push(("status", v.clone()));
}
q
}
}
#[derive(Debug, Clone, Serialize)]
pub struct CreateAutomation {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub trigger_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub trigger_config: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub entry_segment_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reentry_policy: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reentry_cooldown_seconds: Option<u64>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct UpdateAutomation {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub trigger_config: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub entry_segment_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reentry_policy: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reentry_cooldown_seconds: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AbSplit {
pub a: u32,
pub b: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BranchCondition {
pub op: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub property: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub segment_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub event_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub within_seconds: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum AutomationStepConfig {
SendEmail {
#[serde(skip_serializing_if = "Option::is_none")]
template_id: Option<String>,
from: String,
#[serde(skip_serializing_if = "Option::is_none")]
reply_to: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
subject: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
html: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
message_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
topic_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
variables: Option<HashMap<String, String>>,
},
Wait {
duration_seconds: u64,
},
Branch {
condition: BranchCondition,
},
AbSplit {
split: AbSplit,
#[serde(skip_serializing_if = "Option::is_none")]
seed: Option<String>,
},
}
#[derive(Debug, Clone, Deserialize)]
pub struct AutomationStep {
pub id: String,
pub automation_id: String,
pub parent_step_id: Option<String>,
pub branch_label: Option<String>,
pub position: u32,
#[serde(rename = "type")]
pub step_type: String,
pub config: Value,
pub created_at: String,
pub updated_at: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct AutomationStepList {
pub data: Vec<AutomationStep>,
}
#[derive(Debug, Clone, Serialize)]
pub struct AddAutomationStep {
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_step_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub branch_label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<u32>,
pub config: AutomationStepConfig,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct UpdateAutomationStep {
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_step_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub branch_label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub config: Option<AutomationStepConfig>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct AutomationRun {
pub id: String,
pub automation_id: String,
pub contact_id: Option<String>,
pub contact_email: String,
pub trigger_event_id: Option<String>,
pub status: String,
pub current_step_id: Option<String>,
pub context: Value,
pub started_at: String,
pub completed_at: Option<String>,
pub failed_at: Option<String>,
pub failure_reason: Option<String>,
pub created_at: String,
pub updated_at: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct AutomationRunStep {
pub id: String,
pub run_id: String,
pub step_id: String,
pub status: String,
pub email_id: Option<String>,
pub branch_taken: Option<String>,
pub scheduled_for: Option<String>,
pub started_at: Option<String>,
pub completed_at: Option<String>,
pub error: Option<String>,
pub created_at: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct AutomationRunStepList {
pub data: Vec<AutomationRunStep>,
}
#[derive(Debug, Clone, Default)]
pub struct ListAutomationRuns {
pub limit: Option<u32>,
pub cursor: Option<String>,
pub status: Option<String>,
}
impl ListAutomationRuns {
fn to_query(&self) -> Vec<(&'static str, String)> {
let mut q = Vec::new();
if let Some(v) = self.limit {
q.push(("limit", v.to_string()));
}
if let Some(v) = &self.cursor {
q.push(("cursor", v.clone()));
}
if let Some(v) = &self.status {
q.push(("status", v.clone()));
}
q
}
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct CreateAutomationRun {
#[serde(skip_serializing_if = "Option::is_none")]
pub contact_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub contact_email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub context: Option<Value>,
}