use crate::{Result, client::WachtClient, error::Error};
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use url::form_urlencoded;
#[derive(Debug, Deserialize)]
pub struct ListWebhookAppsResponse {
pub data: Vec<WebhookApp>,
#[serde(default)]
pub limit: Option<i32>,
#[serde(default)]
pub offset: Option<i32>,
pub has_more: bool,
}
#[derive(Debug, Clone, Serialize, Default)]
pub struct ListWebhookEndpointsQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub include_inactive: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub offset: Option<i32>,
}
impl ListWebhookEndpointsQuery {
pub fn is_empty(&self) -> bool {
self.include_inactive.is_none() && self.limit.is_none() && self.offset.is_none()
}
}
#[derive(Debug, Clone, Serialize, Default)]
pub struct GetAppWebhookDeliveriesQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub endpoint_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub event_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub offset: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub since: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub until: Option<String>,
}
#[derive(Debug, Clone, Serialize, Default)]
pub struct WebhookAnalyticsQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub app_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub endpoint_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub start_date: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_date: Option<String>,
}
#[derive(Debug, Clone, Serialize, Default)]
pub struct WebhookTimeseriesQuery {
#[serde(skip_serializing_if = "Option::is_none")]
pub app_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub endpoint_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub start_date: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_date: Option<String>,
pub interval: String,
}
#[derive(Debug, Serialize)]
pub struct CreateWebhookAppRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub app_slug: Option<String>,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub failure_notification_emails: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub event_catalog_slug: Option<String>,
}
#[derive(Debug, Serialize)]
pub struct UpdateWebhookAppRequest {
#[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 is_active: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub failure_notification_emails: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub event_catalog_slug: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RateLimitConfig {
pub duration_ms: i64,
pub max_requests: i32,
}
#[derive(Debug, Serialize)]
pub struct CreateWebhookEndpointRequest {
pub app_slug: String,
pub url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub headers: Option<Value>,
pub subscriptions: Vec<EventSubscription>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_retries: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout_seconds: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rate_limit_config: Option<RateLimitConfig>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct EventSubscription {
pub event_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter_rules: Option<Value>,
}
#[derive(Debug, Serialize)]
pub struct UpdateWebhookEndpointRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub headers: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_active: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_retries: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub timeout_seconds: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subscriptions: Option<Vec<EventSubscription>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rate_limit_config: Option<RateLimitConfig>,
}
#[derive(Debug, Serialize)]
pub struct TriggerWebhookEventRequest {
pub app_slug: String,
pub event_name: String,
pub payload: Value,
#[serde(skip_serializing_if = "Option::is_none")]
pub filter_context: Option<Value>,
}
#[derive(Debug, Deserialize)]
pub struct TriggerWebhookEventResponse {
pub delivery_ids: Vec<i64>,
pub filtered_count: usize,
pub delivered_count: usize,
}
#[derive(Debug, Deserialize)]
pub struct TestWebhookEndpointResponse {
pub success: bool,
pub status_code: u16,
pub response_time_ms: u64,
pub response_body: Option<String>,
pub error: Option<String>,
}
#[derive(Debug, Deserialize)]
pub struct WebhookApp {
pub deployment_id: String,
pub app_slug: String,
pub name: String,
pub description: Option<String>,
pub signing_secret: String,
#[serde(default)]
pub failure_notification_emails: Vec<String>,
pub event_catalog_slug: Option<String>,
pub is_active: bool,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct WebhookEventDefinition {
pub name: String,
pub description: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub group: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub schema: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub example_payload: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_archived: Option<bool>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct WebhookEventCatalog {
pub deployment_id: String,
pub slug: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(default)]
pub events: Vec<WebhookEventDefinition>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Serialize)]
pub struct CreateWebhookEventCatalogRequest {
pub slug: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub events: Vec<WebhookEventDefinition>,
}
#[derive(Debug, Serialize, Default)]
pub struct UpdateWebhookEventCatalogRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
#[derive(Debug, Serialize)]
pub struct AppendWebhookEventCatalogEventsRequest {
pub events: Vec<WebhookEventDefinition>,
}
#[derive(Debug, Serialize)]
pub struct ArchiveWebhookEventInCatalogRequest {
pub event_name: String,
pub is_archived: bool,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct WebhookDelivery {
pub deployment_id: String,
pub delivery_id: String,
pub app_slug: String,
pub endpoint_id: String,
pub event_name: String,
pub status: String,
pub http_status_code: Option<i32>,
pub response_time_ms: Option<i32>,
pub attempt_number: i32,
pub max_attempts: i32,
pub timestamp: DateTime<Utc>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct WebhookDeliveryDetails {
pub deployment_id: String,
pub delivery_id: String,
pub app_slug: String,
pub endpoint_id: String,
pub event_name: String,
pub status: String,
pub http_status_code: Option<i32>,
pub response_time_ms: Option<i32>,
pub attempt_number: i32,
pub max_attempts: i32,
pub response_body: Option<String>,
pub response_headers: Option<Value>,
pub timestamp: DateTime<Utc>,
pub payload: Option<Value>,
}
#[derive(Debug, Deserialize)]
pub struct WebhookEndpoint {
pub id: String,
pub deployment_id: String,
pub app_slug: String,
pub url: String,
pub description: Option<String>,
pub headers: Option<Value>,
pub is_active: bool,
pub signing_secret: Option<String>,
pub max_retries: i32,
pub timeout_seconds: i32,
pub failure_count: i32,
pub last_failure_at: Option<DateTime<Utc>>,
pub auto_disabled: bool,
pub auto_disabled_at: Option<DateTime<Utc>>,
pub rate_limit_config: Option<Value>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
#[serde(default)]
pub subscriptions: Vec<EventSubscription>,
}
#[derive(Debug, Deserialize)]
pub struct WebhookEndpointWithSubscriptions {
pub endpoint: WebhookEndpoint,
pub subscribed_events: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct WebhookAppEvent {
pub deployment_id: String,
pub app_slug: String,
pub event_name: String,
pub description: Option<String>,
pub schema: Option<Value>,
pub created_at: DateTime<Utc>,
}
#[derive(Debug, Deserialize)]
struct EventsResponse {
events: Vec<WebhookAppEvent>,
}
#[derive(Debug, Clone)]
pub struct WebhooksApi {
client: WachtClient,
}
impl WebhooksApi {
pub(crate) fn new(client: WachtClient) -> Self {
Self { client }
}
pub fn list_webhook_apps(&self) -> ListWebhookAppsBuilder {
let mut b = ListWebhookAppsBuilder::new();
b.sdk = Some(self.client.clone());
b
}
pub fn get_webhook_app(&self, app_name: &str) -> GetWebhookAppBuilder {
let mut b = GetWebhookAppBuilder::new(app_name);
b.sdk = Some(self.client.clone());
b
}
pub fn create_webhook_app(&self, name: &str) -> CreateWebhookAppBuilder {
let mut b = CreateWebhookAppBuilder::new(name);
b.sdk = Some(self.client.clone());
b
}
pub fn update_webhook_app(&self, app_name: &str) -> UpdateWebhookAppBuilder {
let mut b = UpdateWebhookAppBuilder::new(app_name);
b.sdk = Some(self.client.clone());
b
}
pub fn delete_webhook_app(&self, app_name: &str) -> DeleteWebhookAppBuilder {
let mut b = DeleteWebhookAppBuilder::new(app_name);
b.sdk = Some(self.client.clone());
b
}
pub fn rotate_webhook_secret(&self, app_name: &str) -> RotateWebhookSecretBuilder {
let mut b = RotateWebhookSecretBuilder::new(app_name);
b.sdk = Some(self.client.clone());
b
}
pub fn get_webhook_events(&self, app_name: &str) -> GetWebhookEventsBuilder {
let mut b = GetWebhookEventsBuilder::new(app_name);
b.sdk = Some(self.client.clone());
b
}
pub fn list_webhook_event_catalogs(&self) -> ListWebhookEventCatalogsBuilder {
let mut b = ListWebhookEventCatalogsBuilder::new();
b.sdk = Some(self.client.clone());
b
}
pub fn get_webhook_event_catalog(&self, slug: &str) -> GetWebhookEventCatalogBuilder {
let mut b = GetWebhookEventCatalogBuilder::new(slug);
b.sdk = Some(self.client.clone());
b
}
pub fn create_webhook_event_catalog(
&self,
request: CreateWebhookEventCatalogRequest,
) -> CreateWebhookEventCatalogBuilder {
let mut b = CreateWebhookEventCatalogBuilder::new(request);
b.sdk = Some(self.client.clone());
b
}
pub fn update_webhook_event_catalog(
&self,
slug: &str,
request: UpdateWebhookEventCatalogRequest,
) -> UpdateWebhookEventCatalogBuilder {
let mut b = UpdateWebhookEventCatalogBuilder::new(slug, request);
b.sdk = Some(self.client.clone());
b
}
pub fn append_webhook_event_catalog_events(
&self,
slug: &str,
request: AppendWebhookEventCatalogEventsRequest,
) -> AppendWebhookEventCatalogEventsBuilder {
let mut b = AppendWebhookEventCatalogEventsBuilder::new(slug, request);
b.sdk = Some(self.client.clone());
b
}
pub fn archive_webhook_event_in_catalog(
&self,
slug: &str,
request: ArchiveWebhookEventInCatalogRequest,
) -> ArchiveWebhookEventInCatalogBuilder {
let mut b = ArchiveWebhookEventInCatalogBuilder::new(slug, request);
b.sdk = Some(self.client.clone());
b
}
pub fn get_webhook_catalog(&self, app_name: &str) -> GetWebhookCatalogBuilder {
let mut b = GetWebhookCatalogBuilder::new(app_name);
b.sdk = Some(self.client.clone());
b
}
pub fn list_webhook_endpoints(&self, app_name: &str) -> ListWebhookEndpointsBuilder {
let mut b = ListWebhookEndpointsBuilder::new().app_name(app_name);
b.sdk = Some(self.client.clone());
b
}
pub fn get_webhook_endpoints_with_subscriptions(
&self,
app_name: &str,
) -> GetWebhookEndpointsWithSubscriptionsBuilder {
let mut b = GetWebhookEndpointsWithSubscriptionsBuilder::new(app_name);
b.sdk = Some(self.client.clone());
b
}
pub fn create_webhook_endpoint(
&self,
app_name: &str,
url: &str,
) -> CreateWebhookEndpointBuilder {
let mut b = CreateWebhookEndpointBuilder::new(app_name, url);
b.sdk = Some(self.client.clone());
b
}
pub fn update_webhook_endpoint(
&self,
app_name: &str,
endpoint_id: &str,
) -> UpdateWebhookEndpointBuilder {
let mut b = UpdateWebhookEndpointBuilder::new(app_name, endpoint_id);
b.sdk = Some(self.client.clone());
b
}
pub fn delete_webhook_endpoint(
&self,
app_name: &str,
endpoint_id: &str,
) -> DeleteWebhookEndpointBuilder {
let mut b = DeleteWebhookEndpointBuilder::new(app_name, endpoint_id);
b.sdk = Some(self.client.clone());
b
}
pub fn trigger_webhook_event(
&self,
app_name: &str,
event_name: &str,
payload: Value,
) -> TriggerWebhookEventBuilder {
let mut b = TriggerWebhookEventBuilder::new(app_name, event_name, payload);
b.sdk = Some(self.client.clone());
b
}
pub fn list_webhook_deliveries(&self, app_name: &str) -> GetWebhookDeliveriesBuilder {
let mut b = GetWebhookDeliveriesBuilder::new(app_name);
b.sdk = Some(self.client.clone());
b
}
pub fn get_webhook_delivery_details(
&self,
app_name: &str,
delivery_id: &str,
) -> GetWebhookDeliveryDetailsBuilder {
let mut b = GetWebhookDeliveryDetailsBuilder::new(app_name, delivery_id);
b.sdk = Some(self.client.clone());
b
}
pub fn replay_webhook_deliveries(&self, app_name: &str) -> ReplayWebhookDeliveriesBuilder {
let mut b = ReplayWebhookDeliveriesBuilder::new(app_name);
b.sdk = Some(self.client.clone());
b
}
pub fn list_webhook_replay_tasks(&self, app_name: &str) -> ListWebhookReplayTasksBuilder {
let mut b = ListWebhookReplayTasksBuilder::new(app_name);
b.sdk = Some(self.client.clone());
b
}
pub fn get_webhook_replay_task_status(
&self,
app_name: &str,
task_id: &str,
) -> GetWebhookReplayTaskStatusBuilder {
let mut b = GetWebhookReplayTaskStatusBuilder::new(app_name, task_id);
b.sdk = Some(self.client.clone());
b
}
pub fn cancel_webhook_replay_task(
&self,
app_name: &str,
task_id: &str,
) -> CancelWebhookReplayTaskBuilder {
let mut b = CancelWebhookReplayTaskBuilder::new(app_name, task_id);
b.sdk = Some(self.client.clone());
b
}
pub fn reactivate_webhook_endpoint(
&self,
endpoint_id: &str,
) -> ReactivateWebhookEndpointBuilder {
let mut b = ReactivateWebhookEndpointBuilder::new(endpoint_id);
b.sdk = Some(self.client.clone());
b
}
pub fn test_webhook_endpoint(
&self,
app_name: &str,
endpoint_id: &str,
event_name: &str,
) -> TestWebhookEndpointBuilder {
let mut b = TestWebhookEndpointBuilder::new(app_name, endpoint_id, event_name);
b.sdk = Some(self.client.clone());
b
}
pub fn get_webhook_stats(&self, app_name: &str) -> GetWebhookStatsBuilder {
let mut b = GetWebhookStatsBuilder::new(app_name);
b.sdk = Some(self.client.clone());
b
}
pub fn get_webhook_timeseries(
&self,
app_name: &str,
interval: &str,
) -> GetWebhookTimeseriesBuilder {
let mut b = GetWebhookTimeseriesBuilder::new(app_name, interval);
b.sdk = Some(self.client.clone());
b
}
pub fn get_webhook_analytics(&self, app_name: &str) -> GetWebhookAnalyticsBuilder {
let mut b = GetWebhookAnalyticsBuilder::new(app_name);
b.sdk = Some(self.client.clone());
b
}
}
pub struct ListWebhookAppsBuilder {
sdk: Option<WachtClient>,
include_inactive: Option<bool>,
}
pub struct GetWebhookAppBuilder {
sdk: Option<WachtClient>,
app_name: String,
}
impl GetWebhookAppBuilder {
pub fn new(app_name: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
}
}
pub async fn send(self) -> Result<WebhookApp> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!("{}/webhooks/apps/{}", sdk.config().base_url, self.app_name);
let response = client.get(&url).send().await?;
if response.status().is_success() {
let app = response.json().await?;
Ok(app)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to get webhook app",
&error_text,
))
}
}
}
impl ListWebhookAppsBuilder {
pub fn new() -> Self {
Self {
sdk: None,
include_inactive: None,
}
}
pub fn include_inactive(mut self, include: bool) -> Self {
self.include_inactive = Some(include);
self
}
pub async fn send(self) -> Result<Vec<WebhookApp>> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let mut url = format!("{}/webhooks/apps", sdk.config().base_url);
if let Some(inactive) = self.include_inactive {
url.push_str(&format!("?include_inactive={inactive}"));
}
let response = client.get(&url).send().await?;
if response.status().is_success() {
let result: ListWebhookAppsResponse = response.json().await?;
Ok(result.data)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to list webhook apps",
&error_text,
))
}
}
}
pub struct CreateWebhookAppBuilder {
sdk: Option<WachtClient>,
request: CreateWebhookAppRequest,
}
impl CreateWebhookAppBuilder {
pub fn new(name: &str) -> Self {
Self {
sdk: None,
request: CreateWebhookAppRequest {
app_slug: None,
name: name.to_string(),
description: None,
failure_notification_emails: None,
event_catalog_slug: None,
},
}
}
pub fn app_slug(mut self, app_slug: &str) -> Self {
self.request.app_slug = Some(app_slug.to_string());
self
}
pub fn description(mut self, description: &str) -> Self {
self.request.description = Some(description.to_string());
self
}
pub fn failure_notification_emails(mut self, emails: Vec<String>) -> Self {
self.request.failure_notification_emails = Some(emails);
self
}
pub fn is_active(self, _is_active: bool) -> Self {
self
}
pub fn event_catalog_slug(mut self, slug: &str) -> Self {
self.request.event_catalog_slug = Some(slug.to_string());
self
}
pub async fn send(self) -> Result<WebhookApp> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!("{}/webhooks/apps", sdk.config().base_url);
let response = client.post(&url).json(&self.request).send().await?;
if response.status().is_success() {
let app = response.json().await?;
Ok(app)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to create webhook app",
&error_text,
))
}
}
}
pub struct UpdateWebhookAppBuilder {
sdk: Option<WachtClient>,
app_name: String,
name: Option<String>,
description: Option<String>,
is_active: Option<bool>,
failure_notification_emails: Option<Vec<String>>,
event_catalog_slug: Option<String>,
}
impl UpdateWebhookAppBuilder {
pub fn new(app_name: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
name: None,
description: None,
is_active: None,
failure_notification_emails: None,
event_catalog_slug: None,
}
}
pub fn name(mut self, name: &str) -> Self {
self.name = Some(name.to_string());
self
}
pub fn description(mut self, description: &str) -> Self {
self.description = Some(description.to_string());
self
}
pub fn is_active(mut self, is_active: bool) -> Self {
self.is_active = Some(is_active);
self
}
pub fn failure_notification_emails(mut self, emails: Vec<String>) -> Self {
self.failure_notification_emails = Some(emails);
self
}
pub fn event_catalog_slug(mut self, slug: &str) -> Self {
self.event_catalog_slug = Some(slug.to_string());
self
}
pub async fn send(self) -> Result<WebhookApp> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!("{}/webhooks/apps/{}", sdk.config().base_url, self.app_name);
let request = UpdateWebhookAppRequest {
name: self.name,
description: self.description,
is_active: self.is_active,
failure_notification_emails: self.failure_notification_emails,
event_catalog_slug: self.event_catalog_slug,
};
let response = client.patch(&url).json(&request).send().await?;
if response.status().is_success() {
let app = response.json().await?;
Ok(app)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to update webhook app",
&error_text,
))
}
}
}
pub struct DeleteWebhookAppBuilder {
sdk: Option<WachtClient>,
app_name: String,
}
impl DeleteWebhookAppBuilder {
pub fn new(app_name: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
}
}
pub async fn send(self) -> Result<()> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!("{}/webhooks/apps/{}", sdk.config().base_url, self.app_name);
let response = client.delete(&url).send().await?;
if response.status().is_success() {
Ok(())
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to delete webhook app",
&error_text,
))
}
}
}
pub struct RotateWebhookSecretBuilder {
sdk: Option<WachtClient>,
app_name: String,
}
impl RotateWebhookSecretBuilder {
pub fn new(app_name: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
}
}
pub async fn send(self) -> Result<WebhookApp> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/apps/{}/rotate-secret",
sdk.config().base_url,
self.app_name
);
let response = client.post(&url).send().await?;
if response.status().is_success() {
let result = response.json().await?;
Ok(result)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to rotate webhook secret",
&error_text,
))
}
}
}
pub struct GetWebhookEventsBuilder {
sdk: Option<WachtClient>,
app_name: String,
}
impl GetWebhookEventsBuilder {
pub fn new(app_name: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
}
}
pub async fn send(self) -> Result<Vec<WebhookAppEvent>> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/apps/{}/events",
sdk.config().base_url,
self.app_name
);
let response = client.get(&url).send().await?;
if response.status().is_success() {
let response_data: EventsResponse = response.json().await?;
Ok(response_data.events)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to get webhook events",
&error_text,
))
}
}
}
pub struct ListWebhookEventCatalogsBuilder {
sdk: Option<WachtClient>,
limit: Option<i32>,
offset: Option<i32>,
}
impl ListWebhookEventCatalogsBuilder {
pub fn new() -> Self {
Self {
sdk: None,
limit: None,
offset: None,
}
}
pub fn limit(mut self, limit: i32) -> Self {
self.limit = Some(limit);
self
}
pub fn offset(mut self, offset: i32) -> Self {
self.offset = Some(offset);
self
}
pub async fn send(self) -> Result<Vec<WebhookEventCatalog>> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let mut url = format!("{}/webhooks/event-catalogs", sdk.config().base_url);
let mut params = Vec::new();
if let Some(limit) = self.limit {
params.push(format!("limit={limit}"));
}
if let Some(offset) = self.offset {
params.push(format!("offset={offset}"));
}
if !params.is_empty() {
url.push('?');
url.push_str(¶ms.join("&"));
}
let response = client.get(&url).send().await?;
if response.status().is_success() {
let result: ListWebhookEventCatalogsResponse = response.json().await?;
Ok(result.data)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to list webhook event catalogs",
&error_text,
))
}
}
}
pub struct GetWebhookEventCatalogBuilder {
sdk: Option<WachtClient>,
slug: String,
}
impl GetWebhookEventCatalogBuilder {
pub fn new(slug: &str) -> Self {
Self {
sdk: None,
slug: slug.to_string(),
}
}
pub async fn send(self) -> Result<WebhookEventCatalog> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/event-catalogs/{}",
sdk.config().base_url,
urlencoding::encode(&self.slug)
);
let response = client.get(&url).send().await?;
if response.status().is_success() {
Ok(response.json().await?)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to get webhook event catalog",
&error_text,
))
}
}
}
pub struct CreateWebhookEventCatalogBuilder {
sdk: Option<WachtClient>,
request: CreateWebhookEventCatalogRequest,
}
impl CreateWebhookEventCatalogBuilder {
pub fn new(request: CreateWebhookEventCatalogRequest) -> Self {
Self { sdk: None, request }
}
pub async fn send(self) -> Result<WebhookEventCatalog> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!("{}/webhooks/event-catalogs", sdk.config().base_url);
let response = client.post(&url).json(&self.request).send().await?;
if response.status().is_success() {
Ok(response.json().await?)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to create webhook event catalog",
&error_text,
))
}
}
}
pub struct UpdateWebhookEventCatalogBuilder {
sdk: Option<WachtClient>,
slug: String,
request: UpdateWebhookEventCatalogRequest,
}
impl UpdateWebhookEventCatalogBuilder {
pub fn new(slug: &str, request: UpdateWebhookEventCatalogRequest) -> Self {
Self {
sdk: None,
slug: slug.to_string(),
request,
}
}
pub async fn send(self) -> Result<WebhookEventCatalog> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/event-catalogs/{}",
sdk.config().base_url,
urlencoding::encode(&self.slug)
);
let response = client.put(&url).json(&self.request).send().await?;
if response.status().is_success() {
Ok(response.json().await?)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to update webhook event catalog",
&error_text,
))
}
}
}
pub struct AppendWebhookEventCatalogEventsBuilder {
sdk: Option<WachtClient>,
slug: String,
request: AppendWebhookEventCatalogEventsRequest,
}
impl AppendWebhookEventCatalogEventsBuilder {
pub fn new(slug: &str, request: AppendWebhookEventCatalogEventsRequest) -> Self {
Self {
sdk: None,
slug: slug.to_string(),
request,
}
}
pub async fn send(self) -> Result<WebhookEventCatalog> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/event-catalogs/{}/append-events",
sdk.config().base_url,
urlencoding::encode(&self.slug)
);
let response = client.post(&url).json(&self.request).send().await?;
if response.status().is_success() {
Ok(response.json().await?)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to append webhook catalog events",
&error_text,
))
}
}
}
pub struct ArchiveWebhookEventInCatalogBuilder {
sdk: Option<WachtClient>,
slug: String,
request: ArchiveWebhookEventInCatalogRequest,
}
impl ArchiveWebhookEventInCatalogBuilder {
pub fn new(slug: &str, request: ArchiveWebhookEventInCatalogRequest) -> Self {
Self {
sdk: None,
slug: slug.to_string(),
request,
}
}
pub async fn send(self) -> Result<WebhookEventCatalog> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/event-catalogs/{}/archive-event",
sdk.config().base_url,
urlencoding::encode(&self.slug)
);
let response = client.post(&url).json(&self.request).send().await?;
if response.status().is_success() {
Ok(response.json().await?)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to archive webhook event in catalog",
&error_text,
))
}
}
}
pub struct GetWebhookCatalogBuilder {
sdk: Option<WachtClient>,
app_name: String,
}
impl GetWebhookCatalogBuilder {
pub fn new(app_name: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
}
}
pub async fn send(self) -> Result<WebhookEventCatalog> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/apps/{}/catalog",
sdk.config().base_url,
self.app_name
);
let response = client.get(&url).send().await?;
if response.status().is_success() {
Ok(response.json().await?)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to get webhook app catalog",
&error_text,
))
}
}
}
#[derive(Debug, Deserialize)]
pub struct ListWebhookEventCatalogsResponse {
pub data: Vec<WebhookEventCatalog>,
pub has_more: bool,
#[serde(default)]
pub limit: Option<i32>,
#[serde(default)]
pub offset: Option<i32>,
}
#[derive(Debug, Deserialize)]
pub struct EndpointsResponse {
pub data: Vec<WebhookEndpoint>,
#[serde(default)]
pub limit: Option<i32>,
#[serde(default)]
pub offset: Option<i32>,
pub has_more: bool,
}
#[derive(Debug, Deserialize)]
pub struct PaginatedEndpointsResponse {
pub endpoints: Vec<WebhookEndpointWithSubscriptions>,
pub count: usize,
pub limit: i32,
pub offset: i32,
pub has_more: bool,
}
pub struct ListWebhookEndpointsBuilder {
sdk: Option<WachtClient>,
app_name: Option<String>,
include_inactive: Option<bool>,
limit: Option<i32>,
offset: Option<i32>,
}
impl ListWebhookEndpointsBuilder {
pub fn new() -> Self {
Self {
sdk: None,
app_name: None,
include_inactive: None,
limit: None,
offset: None,
}
}
pub fn app_name(mut self, app_name: &str) -> Self {
self.app_name = Some(app_name.to_string());
self
}
pub fn include_inactive(mut self, include: bool) -> Self {
self.include_inactive = Some(include);
self
}
pub fn limit(mut self, limit: i32) -> Self {
self.limit = Some(limit);
self
}
pub fn offset(mut self, offset: i32) -> Self {
self.offset = Some(offset);
self
}
pub async fn send(self) -> Result<Vec<WebhookEndpoint>> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
if let Some(app_name) = &self.app_name {
let mut url = format!(
"{}/webhooks/apps/{}/endpoints",
sdk.config().base_url,
app_name
);
let mut params = Vec::new();
if let Some(inactive) = self.include_inactive {
params.push(format!("include_inactive={inactive}"));
}
if let Some(limit) = self.limit {
params.push(format!("limit={limit}"));
}
if let Some(offset) = self.offset {
params.push(format!("offset={offset}"));
}
if !params.is_empty() {
url.push('?');
url.push_str(¶ms.join("&"));
}
let response = client.get(&url).send().await?;
if response.status().is_success() {
let result: EndpointsResponse = response.json().await?;
Ok(result.data)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to list webhook endpoints",
&error_text,
))
}
} else {
Err(Error::InvalidRequest(
"app_name is required for listing webhook endpoints".to_string(),
))
}
}
}
pub struct GetWebhookEndpointsWithSubscriptionsBuilder {
sdk: Option<WachtClient>,
app_name: String,
include_inactive: Option<bool>,
limit: Option<i32>,
offset: Option<i32>,
}
impl GetWebhookEndpointsWithSubscriptionsBuilder {
pub fn new(app_name: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
include_inactive: None,
limit: None,
offset: None,
}
}
pub fn include_inactive(mut self, include: bool) -> Self {
self.include_inactive = Some(include);
self
}
pub fn limit(mut self, limit: i32) -> Self {
self.limit = Some(limit);
self
}
pub fn offset(mut self, offset: i32) -> Self {
self.offset = Some(offset);
self
}
pub async fn send(self) -> Result<PaginatedEndpointsResponse> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/apps/{}/endpoints",
sdk.config().base_url,
self.app_name
);
let mut options = ListWebhookEndpointsQuery::default();
options.include_inactive = self.include_inactive;
options.limit = self.limit;
options.offset = self.offset;
let mut request = client.get(&url);
if !options.is_empty() {
request = request.query(&options);
}
let response = request.send().await?;
if response.status().is_success() {
let response_data: EndpointsResponse = response.json().await?;
let endpoints_with_subs: Vec<WebhookEndpointWithSubscriptions> = response_data
.data
.into_iter()
.map(|mut endpoint| {
let subscribed_events = endpoint
.subscriptions
.iter()
.map(|sub| sub.event_name.clone())
.collect();
endpoint.subscriptions.clear();
WebhookEndpointWithSubscriptions {
endpoint,
subscribed_events,
}
})
.collect();
let count = endpoints_with_subs.len();
Ok(PaginatedEndpointsResponse {
endpoints: endpoints_with_subs,
count,
limit: response_data.limit.unwrap_or(100),
offset: response_data.offset.unwrap_or(0),
has_more: response_data.has_more,
})
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to get webhook endpoints with subscriptions",
&error_text,
))
}
}
}
pub struct CreateWebhookEndpointBuilder {
sdk: Option<WachtClient>,
app_slug: String,
url: String,
description: Option<String>,
headers: Option<Value>,
subscriptions: Vec<EventSubscription>,
max_retries: Option<i32>,
timeout_seconds: Option<i32>,
rate_limit_config: Option<RateLimitConfig>,
}
impl CreateWebhookEndpointBuilder {
pub fn new(app_slug: &str, url: &str) -> Self {
Self {
sdk: None,
app_slug: app_slug.to_string(),
url: url.to_string(),
description: None,
headers: None,
subscriptions: Vec::new(),
max_retries: None,
timeout_seconds: None,
rate_limit_config: None,
}
}
pub fn description(mut self, description: &str) -> Self {
self.description = Some(description.to_string());
self
}
pub fn headers(mut self, headers: Value) -> Self {
self.headers = Some(headers);
self
}
pub fn subscriptions(mut self, subscriptions: Vec<EventSubscription>) -> Self {
self.subscriptions = subscriptions;
self
}
pub fn add_subscription(mut self, subscription: EventSubscription) -> Self {
self.subscriptions.push(subscription);
self
}
pub fn add_event(mut self, event_name: &str, filter_rules: Value) -> Self {
self.subscriptions.push(EventSubscription {
event_name: event_name.to_string(),
filter_rules: Some(filter_rules),
});
self
}
pub fn max_retries(mut self, max_retries: i32) -> Self {
self.max_retries = Some(max_retries);
self
}
pub fn timeout_seconds(mut self, timeout_seconds: i32) -> Self {
self.timeout_seconds = Some(timeout_seconds);
self
}
pub fn rate_limit_config(mut self, rate_limit_config: RateLimitConfig) -> Self {
self.rate_limit_config = Some(rate_limit_config);
self
}
pub async fn send(self) -> Result<WebhookEndpoint> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/apps/{}/endpoints",
sdk.config().base_url,
self.app_slug
);
let request = CreateWebhookEndpointRequest {
app_slug: self.app_slug,
url: self.url,
description: self.description,
headers: self.headers,
subscriptions: self.subscriptions,
max_retries: self.max_retries,
timeout_seconds: self.timeout_seconds,
rate_limit_config: self.rate_limit_config,
};
let response = client.post(&url).json(&request).send().await?;
if response.status().is_success() {
let endpoint = response.json().await?;
Ok(endpoint)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to create webhook endpoint",
&error_text,
))
}
}
}
pub struct UpdateWebhookEndpointBuilder {
sdk: Option<WachtClient>,
app_name: String,
endpoint_id: String,
url: Option<String>,
description: Option<String>,
headers: Option<Value>,
is_active: Option<bool>,
max_retries: Option<i32>,
timeout_seconds: Option<i32>,
subscriptions: Option<Vec<EventSubscription>>,
rate_limit_config: Option<RateLimitConfig>,
}
impl UpdateWebhookEndpointBuilder {
pub fn new(app_name: &str, endpoint_id: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
endpoint_id: endpoint_id.to_string(),
url: None,
description: None,
headers: None,
is_active: None,
max_retries: None,
timeout_seconds: None,
subscriptions: None,
rate_limit_config: None,
}
}
pub fn url(mut self, url: &str) -> Self {
self.url = Some(url.to_string());
self
}
pub fn description(mut self, description: &str) -> Self {
self.description = Some(description.to_string());
self
}
pub fn headers(mut self, headers: Value) -> Self {
self.headers = Some(headers);
self
}
pub fn is_active(mut self, is_active: bool) -> Self {
self.is_active = Some(is_active);
self
}
pub fn max_retries(mut self, max_retries: i32) -> Self {
self.max_retries = Some(max_retries);
self
}
pub fn timeout_seconds(mut self, timeout_seconds: i32) -> Self {
self.timeout_seconds = Some(timeout_seconds);
self
}
pub fn subscriptions(mut self, subscriptions: Vec<EventSubscription>) -> Self {
self.subscriptions = Some(subscriptions);
self
}
pub fn rate_limit_config(mut self, rate_limit_config: RateLimitConfig) -> Self {
self.rate_limit_config = Some(rate_limit_config);
self
}
pub async fn send(self) -> Result<WebhookEndpoint> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/apps/{}/endpoints/{}",
sdk.config().base_url,
self.app_name,
self.endpoint_id
);
let request = UpdateWebhookEndpointRequest {
url: self.url,
description: self.description,
headers: self.headers,
is_active: self.is_active,
max_retries: self.max_retries,
timeout_seconds: self.timeout_seconds,
subscriptions: self.subscriptions,
rate_limit_config: self.rate_limit_config,
};
let response = client.patch(&url).json(&request).send().await?;
if response.status().is_success() {
let endpoint = response.json().await?;
Ok(endpoint)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to update webhook endpoint",
&error_text,
))
}
}
}
pub struct DeleteWebhookEndpointBuilder {
sdk: Option<WachtClient>,
app_name: String,
endpoint_id: String,
}
impl DeleteWebhookEndpointBuilder {
pub fn new(app_name: &str, endpoint_id: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
endpoint_id: endpoint_id.to_string(),
}
}
pub async fn send(self) -> Result<()> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/apps/{}/endpoints/{}",
sdk.config().base_url,
self.app_name,
self.endpoint_id
);
let response = client.delete(&url).send().await?;
if response.status().is_success() {
Ok(())
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to delete webhook endpoint",
&error_text,
))
}
}
}
pub struct TriggerWebhookEventBuilder {
sdk: Option<WachtClient>,
app_slug: String,
event_name: String,
payload: Value,
filter_context: Option<Value>,
}
impl TriggerWebhookEventBuilder {
pub fn new(app_slug: &str, event_name: &str, payload: Value) -> Self {
Self {
sdk: None,
app_slug: app_slug.to_string(),
event_name: event_name.to_string(),
payload,
filter_context: None,
}
}
pub fn filter_context(mut self, filter_context: Value) -> Self {
self.filter_context = Some(filter_context);
self
}
pub async fn send(self) -> Result<TriggerWebhookEventResponse> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/apps/{}/trigger",
sdk.config().base_url,
self.app_slug
);
let request = TriggerWebhookEventRequest {
app_slug: self.app_slug.clone(),
event_name: self.event_name,
payload: self.payload,
filter_context: self.filter_context,
};
let response = client.post(&url).json(&request).send().await?;
if response.status().is_success() {
let result = response.json().await?;
Ok(result)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to trigger webhook event",
&error_text,
))
}
}
}
pub struct GetWebhookDeliveriesBuilder {
sdk: Option<WachtClient>,
app_name: String,
endpoint_id: Option<i64>,
event_name: Option<String>,
status: Option<String>,
limit: Option<i32>,
offset: Option<i32>,
since: Option<String>,
until: Option<String>,
}
impl GetWebhookDeliveriesBuilder {
pub fn new(app_name: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
endpoint_id: None,
event_name: None,
status: None,
limit: None,
offset: None,
since: None,
until: None,
}
}
pub fn endpoint_id(mut self, endpoint_id: i64) -> Self {
self.endpoint_id = Some(endpoint_id);
self
}
pub fn event_name(mut self, event_name: &str) -> Self {
self.event_name = Some(event_name.to_string());
self
}
pub fn status(mut self, status: &str) -> Self {
self.status = Some(status.to_string());
self
}
pub fn limit(mut self, limit: i32) -> Self {
self.limit = Some(limit);
self
}
pub fn offset(mut self, offset: i32) -> Self {
self.offset = Some(offset);
self
}
pub fn since(mut self, since: &str) -> Self {
self.since = Some(since.to_string());
self
}
pub fn until(mut self, until: &str) -> Self {
self.until = Some(until.to_string());
self
}
pub async fn send(self) -> Result<GetWebhookDeliveriesResponse> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let mut url = format!(
"{}/webhooks/apps/{}/deliveries",
sdk.config().base_url,
self.app_name
);
let mut params = Vec::new();
if let Some(id) = self.endpoint_id {
params.push(format!("endpoint_id={id}"));
}
if let Some(event) = &self.event_name {
params.push(format!("event_name={event}"));
}
if let Some(s) = &self.status {
params.push(format!("status={s}"));
}
if let Some(l) = self.limit {
params.push(format!("limit={l}"));
}
if let Some(o) = self.offset {
params.push(format!("offset={o}"));
}
if let Some(since) = &self.since {
params.push(format!("since={since}"));
}
if let Some(until) = &self.until {
params.push(format!("until={until}"));
}
if !params.is_empty() {
url.push('?');
url.push_str(¶ms.join("&"));
}
let response = client.get(&url).send().await?;
if response.status().is_success() {
let result = response.json().await?;
Ok(result)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to get webhook deliveries",
&error_text,
))
}
}
}
#[derive(Debug, Deserialize)]
pub struct GetWebhookDeliveriesResponse {
pub data: Vec<WebhookDelivery>,
pub has_more: bool,
#[serde(default)]
pub limit: Option<i32>,
#[serde(default)]
pub offset: Option<i32>,
}
pub struct GetWebhookDeliveryDetailsBuilder {
sdk: Option<WachtClient>,
app_name: String,
delivery_id: String,
}
impl GetWebhookDeliveryDetailsBuilder {
pub fn new(app_name: &str, delivery_id: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
delivery_id: delivery_id.to_string(),
}
}
pub async fn send(self) -> Result<WebhookDeliveryDetails> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/apps/{}/deliveries/{}",
sdk.config().base_url,
self.app_name,
self.delivery_id
);
let response = client.get(&url).send().await?;
if response.status().is_success() {
let result = response.json().await?;
Ok(result)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to get webhook delivery details",
&error_text,
))
}
}
}
pub struct ReplayWebhookDeliveriesBuilder {
sdk: Option<WachtClient>,
app_name: String,
request: ReplayWebhookDeliveriesRequest,
}
#[derive(Debug, Serialize)]
#[serde(untagged)]
pub enum ReplayWebhookDeliveriesRequest {
ByIds {
delivery_ids: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
idempotency_key: Option<String>,
},
ByDateRange {
start_date: DateTime<Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
end_date: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
idempotency_key: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
event_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
endpoint_id: Option<String>,
},
}
impl ReplayWebhookDeliveriesBuilder {
pub fn new(app_name: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
request: ReplayWebhookDeliveriesRequest::ByIds {
delivery_ids: Vec::new(),
idempotency_key: None,
},
}
}
pub fn by_ids(mut self, delivery_ids: Vec<String>) -> Self {
let idempotency_key = match self.request {
ReplayWebhookDeliveriesRequest::ByIds {
idempotency_key, ..
} => idempotency_key,
ReplayWebhookDeliveriesRequest::ByDateRange {
idempotency_key, ..
} => idempotency_key,
};
self.request = ReplayWebhookDeliveriesRequest::ByIds {
delivery_ids,
idempotency_key,
};
self
}
pub fn by_date_range(
mut self,
start_date: DateTime<Utc>,
end_date: Option<DateTime<Utc>>,
) -> Self {
let idempotency_key = match self.request {
ReplayWebhookDeliveriesRequest::ByIds {
idempotency_key, ..
} => idempotency_key,
ReplayWebhookDeliveriesRequest::ByDateRange {
idempotency_key, ..
} => idempotency_key,
};
self.request = ReplayWebhookDeliveriesRequest::ByDateRange {
start_date,
end_date,
idempotency_key,
status: None,
event_name: None,
endpoint_id: None,
};
self
}
pub fn idempotency_key(mut self, key: &str) -> Self {
self.request = match self.request {
ReplayWebhookDeliveriesRequest::ByIds { delivery_ids, .. } => {
ReplayWebhookDeliveriesRequest::ByIds {
delivery_ids,
idempotency_key: Some(key.to_string()),
}
}
ReplayWebhookDeliveriesRequest::ByDateRange {
start_date,
end_date,
status,
event_name,
endpoint_id,
..
} => ReplayWebhookDeliveriesRequest::ByDateRange {
start_date,
end_date,
idempotency_key: Some(key.to_string()),
status,
event_name,
endpoint_id,
},
};
self
}
pub fn status(mut self, status: &str) -> Self {
if let ReplayWebhookDeliveriesRequest::ByDateRange {
start_date,
end_date,
idempotency_key,
event_name,
endpoint_id,
..
} = self.request
{
self.request = ReplayWebhookDeliveriesRequest::ByDateRange {
start_date,
end_date,
idempotency_key,
status: Some(status.to_string()),
event_name,
endpoint_id,
};
}
self
}
pub fn event_name(mut self, event_name: &str) -> Self {
if let ReplayWebhookDeliveriesRequest::ByDateRange {
start_date,
end_date,
idempotency_key,
status,
endpoint_id,
..
} = self.request
{
self.request = ReplayWebhookDeliveriesRequest::ByDateRange {
start_date,
end_date,
idempotency_key,
status,
event_name: Some(event_name.to_string()),
endpoint_id,
};
}
self
}
pub fn endpoint_id(mut self, endpoint_id: &str) -> Self {
if let ReplayWebhookDeliveriesRequest::ByDateRange {
start_date,
end_date,
idempotency_key,
status,
event_name,
..
} = self.request
{
self.request = ReplayWebhookDeliveriesRequest::ByDateRange {
start_date,
end_date,
idempotency_key,
status,
event_name,
endpoint_id: Some(endpoint_id.to_string()),
};
}
self
}
pub async fn send(self) -> Result<ReplayWebhookDeliveriesResponse> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/apps/{}/deliveries/replay",
sdk.config().base_url,
self.app_name
);
let response = client.post(&url).json(&self.request).send().await?;
if response.status().is_success() {
let result = response.json().await?;
Ok(result)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to replay webhook deliveries",
&error_text,
))
}
}
}
#[derive(Debug, Deserialize)]
pub struct ReplayWebhookDeliveriesResponse {
pub status: String,
pub message: String,
#[serde(default)]
pub task_id: Option<String>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct ReplayTaskStatus {
pub task_id: String,
pub app_slug: String,
pub status: String,
#[serde(default)]
pub created_at: Option<String>,
#[serde(default)]
pub started_at: Option<String>,
#[serde(default)]
pub completed_at: Option<String>,
pub total_count: i64,
pub processed: i64,
pub replayed_count: i64,
pub failed_count: i64,
#[serde(default)]
pub last_delivery_id: Option<i64>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct ReplayTaskListResponse {
pub data: Vec<ReplayTaskStatus>,
pub limit: i32,
pub offset: i32,
pub has_more: bool,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct ReplayTaskCancelResponse {
pub status: String,
pub message: String,
}
pub struct ListWebhookReplayTasksBuilder {
sdk: Option<WachtClient>,
app_name: String,
limit: Option<i32>,
offset: Option<i32>,
}
impl ListWebhookReplayTasksBuilder {
pub fn new(app_name: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
limit: None,
offset: None,
}
}
pub fn limit(mut self, limit: i32) -> Self {
self.limit = Some(limit);
self
}
pub fn offset(mut self, offset: i32) -> Self {
self.offset = Some(offset);
self
}
pub async fn send(self) -> Result<ReplayTaskListResponse> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let mut url = format!(
"{}/webhooks/apps/{}/deliveries/replay",
sdk.config().base_url,
self.app_name
);
let mut params = Vec::new();
if let Some(limit) = self.limit {
params.push(format!("limit={limit}"));
}
if let Some(offset) = self.offset {
params.push(format!("offset={offset}"));
}
if !params.is_empty() {
url.push('?');
url.push_str(¶ms.join("&"));
}
let response = client.get(&url).send().await?;
if response.status().is_success() {
Ok(response.json().await?)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to list webhook replay tasks",
&error_text,
))
}
}
}
pub struct GetWebhookReplayTaskStatusBuilder {
sdk: Option<WachtClient>,
app_name: String,
task_id: String,
}
impl GetWebhookReplayTaskStatusBuilder {
pub fn new(app_name: &str, task_id: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
task_id: task_id.to_string(),
}
}
pub async fn send(self) -> Result<ReplayTaskStatus> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/apps/{}/deliveries/replay/{}",
sdk.config().base_url,
self.app_name,
self.task_id
);
let response = client.get(&url).send().await?;
if response.status().is_success() {
Ok(response.json().await?)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to get webhook replay task status",
&error_text,
))
}
}
}
pub struct CancelWebhookReplayTaskBuilder {
sdk: Option<WachtClient>,
app_name: String,
task_id: String,
}
impl CancelWebhookReplayTaskBuilder {
pub fn new(app_name: &str, task_id: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
task_id: task_id.to_string(),
}
}
pub async fn send(self) -> Result<ReplayTaskCancelResponse> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/apps/{}/deliveries/replay/{}/cancel",
sdk.config().base_url,
self.app_name,
self.task_id
);
let response = client.post(&url).send().await?;
if response.status().is_success() {
Ok(response.json().await?)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to cancel webhook replay task",
&error_text,
))
}
}
}
pub struct ReactivateWebhookEndpointBuilder {
sdk: Option<WachtClient>,
endpoint_id: String,
}
impl ReactivateWebhookEndpointBuilder {
pub fn new(endpoint_id: &str) -> Self {
Self {
sdk: None,
endpoint_id: endpoint_id.to_string(),
}
}
pub async fn send(self) -> Result<ReactivateEndpointResponse> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/endpoints/{}/reactivate",
sdk.config().base_url,
self.endpoint_id
);
let response = client.post(&url).send().await?;
if response.status().is_success() {
let result = response.json().await?;
Ok(result)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to reactivate webhook endpoint",
&error_text,
))
}
}
}
#[derive(Debug, Deserialize)]
pub struct ReactivateEndpointResponse {
pub success: bool,
pub message: String,
}
pub struct TestWebhookEndpointBuilder {
sdk: Option<WachtClient>,
app_name: String,
endpoint_id: String,
event_name: String,
payload: Option<Value>,
}
impl TestWebhookEndpointBuilder {
pub fn new(app_name: &str, endpoint_id: &str, event_name: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
endpoint_id: endpoint_id.to_string(),
event_name: event_name.to_string(),
payload: None,
}
}
pub fn payload(mut self, payload: Value) -> Self {
self.payload = Some(payload);
self
}
pub async fn send(self) -> Result<TestWebhookEndpointResponse> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/apps/{}/endpoints/{}/test",
sdk.config().base_url,
self.app_name,
self.endpoint_id
);
let request_body = serde_json::json!({
"event_name": self.event_name,
"payload": self.payload
});
let response = client.post(&url).json(&request_body).send().await?;
if response.status().is_success() {
let result: TestWebhookEndpointResponse = response.json().await?;
Ok(result)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to test webhook endpoint",
&error_text,
))
}
}
}
#[derive(Debug, Deserialize)]
pub struct AnalyticsResult {
pub total_events: i64,
pub total_deliveries: i64,
pub successful_deliveries: i64,
pub failed_deliveries: i64,
pub filtered_deliveries: i64,
pub avg_response_time_ms: Option<f64>,
pub p50_response_time_ms: Option<f64>,
pub p95_response_time_ms: Option<f64>,
pub p99_response_time_ms: Option<f64>,
pub success_rate: f64,
pub top_events: Vec<AnalyticsEventCount>,
pub endpoint_performance: Vec<AnalyticsEndpointPerformance>,
pub failure_reasons: Vec<AnalyticsFailureReason>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct AnalyticsEventCount {
pub event_name: String,
pub count: i64,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct AnalyticsEndpointPerformance {
pub endpoint_id: i64,
pub endpoint_url: String,
pub total_attempts: i64,
pub successful_attempts: i64,
pub failed_attempts: i64,
pub avg_response_time_ms: Option<f64>,
pub success_rate: f64,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct AnalyticsFailureReason {
pub reason: String,
pub count: i64,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct WebhookStats {
pub total_deliveries: i64,
pub success_rate: f64,
pub active_endpoints: i64,
pub failed_deliveries_24h: i64,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct TimeseriesResult {
pub data: Vec<TimeseriesDataPoint>,
pub interval: String,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct TimeseriesDataPoint {
pub timestamp: DateTime<Utc>,
pub total_events: i64,
pub total_deliveries: i64,
pub successful_deliveries: i64,
pub failed_deliveries: i64,
pub filtered_deliveries: i64,
pub avg_response_time_ms: Option<f64>,
pub success_rate: f64,
}
pub struct GetWebhookTimeseriesBuilder {
sdk: Option<WachtClient>,
app_name: String,
interval: String,
start_date: Option<String>,
end_date: Option<String>,
}
impl GetWebhookTimeseriesBuilder {
pub fn new(app_name: &str, interval: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
interval: interval.to_string(),
start_date: None,
end_date: None,
}
}
pub fn start_date(mut self, start_date: &str) -> Self {
self.start_date = Some(start_date.to_string());
self
}
pub fn end_date(mut self, end_date: &str) -> Self {
self.end_date = Some(end_date.to_string());
self
}
pub async fn send(self) -> Result<TimeseriesResult> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let mut url = format!(
"{}/webhooks/apps/{}/timeseries",
sdk.config().base_url,
self.app_name
);
let mut query_params = Vec::new();
query_params.push(format!("interval={}", urlencoding::encode(&self.interval)));
if let Some(start) = &self.start_date {
query_params.push(format!("start_date={}", urlencoding::encode(start)));
}
if let Some(end) = &self.end_date {
query_params.push(format!("end_date={}", urlencoding::encode(end)));
}
if !query_params.is_empty() {
url.push('?');
url.push_str(&query_params.join("&"));
}
let response = client.get(&url).send().await?;
if response.status().is_success() {
let timeseries: TimeseriesResult = response.json().await?;
Ok(timeseries)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to get webhook timeseries",
&error_text,
))
}
}
}
pub struct GetWebhookAnalyticsBuilder {
sdk: Option<WachtClient>,
app_name: String,
start_date: Option<String>,
end_date: Option<String>,
endpoint_id: Option<i64>,
}
impl GetWebhookAnalyticsBuilder {
pub fn new(app_name: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
start_date: None,
end_date: None,
endpoint_id: None,
}
}
pub fn start_date(mut self, start_date: &str) -> Self {
self.start_date = Some(start_date.to_string());
self
}
pub fn end_date(mut self, end_date: &str) -> Self {
self.end_date = Some(end_date.to_string());
self
}
pub fn endpoint_id(mut self, endpoint_id: i64) -> Self {
self.endpoint_id = Some(endpoint_id);
self
}
pub async fn send(self) -> Result<AnalyticsResult> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let mut url = format!(
"{}/webhooks/apps/{}/analytics",
sdk.config().base_url,
self.app_name
);
let mut query_params = Vec::new();
if let Some(start) = &self.start_date {
query_params.push(format!("start_date={}", urlencoding::encode(start)));
}
if let Some(end) = &self.end_date {
query_params.push(format!("end_date={}", urlencoding::encode(end)));
}
if let Some(endpoint_id) = self.endpoint_id {
query_params.push(format!("endpoint_id={}", endpoint_id));
}
if !query_params.is_empty() {
url.push('?');
url.push_str(&query_params.join("&"));
}
let response = client.get(&url).send().await?;
if response.status().is_success() {
let analytics: AnalyticsResult = response.json().await?;
Ok(analytics)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to get webhook analytics",
&error_text,
))
}
}
}
pub struct GetWebhookStatsBuilder {
sdk: Option<WachtClient>,
app_name: String,
}
impl GetWebhookStatsBuilder {
pub fn new(app_name: &str) -> Self {
Self {
sdk: None,
app_name: app_name.to_string(),
}
}
pub async fn send(self) -> Result<WebhookStats> {
let sdk = self.sdk.ok_or_else(|| {
Error::InvalidRequest("Webhook builder is not bound to a WachtClient".to_string())
})?;
let client = sdk.http_client();
let url = format!(
"{}/webhooks/apps/{}/stats",
sdk.config().base_url,
self.app_name
);
let response = client.get(&url).send().await?;
if response.status().is_success() {
let stats: WebhookStats = response.json().await?;
Ok(stats)
} else {
let status = response.status();
let error_text = response.text().await.unwrap_or_default();
Err(Error::api_from_text(
status,
"Failed to get webhook stats",
&error_text,
))
}
}
}