myc_core/domain/dtos/webhook/
responses.rs1use super::WebHookTrigger;
2
3use base64::{engine::general_purpose, Engine};
4use chrono::{DateTime, Local};
5use mycelium_base::utils::errors::{dto_err, MappedErrors};
6use serde::{Deserialize, Serialize};
7use std::{fmt::Display, str::FromStr};
8use utoipa::ToSchema;
9use uuid::Uuid;
10
11#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
12#[serde(rename_all = "camelCase")]
13pub enum WebHookExecutionStatus {
14 Pending,
19
20 Success,
25
26 Failed,
31
32 Skipped,
37
38 Unknown,
43}
44
45impl Display for WebHookExecutionStatus {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 match self {
48 Self::Pending => write!(f, "pending"),
49 Self::Success => write!(f, "success"),
50 Self::Failed => write!(f, "failed"),
51 Self::Skipped => write!(f, "skipped"),
52 Self::Unknown => write!(f, "unknown"),
53 }
54 }
55}
56
57impl FromStr for WebHookExecutionStatus {
58 type Err = MappedErrors;
59
60 fn from_str(s: &str) -> Result<Self, Self::Err> {
61 match s {
62 "pending" => Ok(Self::Pending),
63 "success" => Ok(Self::Success),
64 "failed" => Ok(Self::Failed),
65 "skipped" => Ok(Self::Skipped),
66 "unknown" => Ok(Self::Unknown),
67 _ => dto_err("Invalid webhook execution status").as_error(),
68 }
69 }
70}
71
72#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
73#[serde(rename_all = "camelCase")]
74pub struct HookResponse {
75 pub url: String,
76 pub status: u16,
77 pub body: Option<String>,
78 pub datetime: DateTime<Local>,
79}
80
81#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
82#[serde(rename_all = "camelCase")]
83pub struct WebHookPayloadArtifact {
84 pub id: Option<Uuid>,
90
91 pub payload: String,
98
99 pub trigger: WebHookTrigger,
105
106 #[serde(skip_serializing_if = "Option::is_none")]
113 pub propagations: Option<Vec<HookResponse>>,
114
115 #[serde(skip_serializing_if = "Option::is_none")]
121 pub encrypted: Option<bool>,
122
123 #[serde(skip_serializing_if = "Option::is_none")]
129 pub attempts: Option<u8>,
130
131 #[serde(skip_serializing_if = "Option::is_none")]
136 pub attempted: Option<DateTime<Local>>,
137
138 pub created: Option<DateTime<Local>>,
143
144 pub status: Option<WebHookExecutionStatus>,
150}
151
152impl WebHookPayloadArtifact {
153 pub fn new(
154 id: Option<Uuid>,
155 payload: String,
156 trigger: WebHookTrigger,
157 ) -> Self {
158 Self {
159 id,
160 payload,
161 trigger,
162 propagations: None,
163 encrypted: None,
164 attempts: None,
165 attempted: None,
166 created: None,
167 status: Some(WebHookExecutionStatus::Pending),
168 }
169 }
170
171 pub fn encode_payload(&mut self) -> Result<Self, MappedErrors> {
176 let serialized_payload =
177 serde_json::to_string(&self.payload).map_err(|e| {
178 dto_err(format!("Failed to serialize payload: {}", e))
179 })?;
180
181 let encoded_payload =
182 general_purpose::STANDARD.encode(serialized_payload.as_bytes());
183
184 Ok(Self {
185 payload: encoded_payload,
186 ..self.clone()
187 })
188 }
189
190 pub fn decode_payload(
195 &self,
196 ) -> Result<WebHookPayloadArtifact, MappedErrors> {
197 let decoded_payload =
198 match general_purpose::STANDARD.decode(&self.payload) {
199 Err(_) => return dto_err("Failed to decode base64").as_error(),
200 Ok(decoded) => String::from_utf8(decoded)
201 .map_err(|_| dto_err("Failed to decode payload"))?,
202 };
203
204 let payload = serde_json::from_str(&decoded_payload).map_err(|e| {
205 dto_err(format!("Failed to deserialize payload: {}", e))
206 })?;
207
208 Ok(Self {
209 payload,
210 ..self.clone()
211 })
212 }
213}