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 enum PayloadId {
84 Uuid(Uuid),
85 String(String),
86 Number(u64),
87}
88
89impl Display for PayloadId {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 match self {
92 Self::Uuid(uuid) => write!(f, "{}", uuid),
93 Self::String(string) => write!(f, "{}", string),
94 Self::Number(number) => write!(f, "{}", number),
95 }
96 }
97}
98
99impl FromStr for PayloadId {
100 type Err = MappedErrors;
101
102 fn from_str(s: &str) -> Result<Self, Self::Err> {
103 if let Ok(uuid) = s.parse::<Uuid>() {
104 Ok(Self::Uuid(uuid))
105 } else if let Ok(number) = s.parse::<u64>() {
106 Ok(Self::Number(number))
107 } else {
108 Ok(Self::String(s.to_string()))
109 }
110 }
111}
112
113#[derive(Clone, Debug, Deserialize, Serialize, ToSchema)]
114#[serde(rename_all = "camelCase")]
115pub struct WebHookPayloadArtifact {
116 pub id: Option<Uuid>,
122
123 pub payload: String,
130
131 pub payload_id: PayloadId,
137
138 pub trigger: WebHookTrigger,
144
145 #[serde(skip_serializing_if = "Option::is_none")]
152 pub propagations: Option<Vec<HookResponse>>,
153
154 #[serde(skip_serializing_if = "Option::is_none")]
160 pub encrypted: Option<bool>,
161
162 #[serde(skip_serializing_if = "Option::is_none")]
168 pub attempts: Option<u8>,
169
170 #[serde(skip_serializing_if = "Option::is_none")]
175 pub attempted: Option<DateTime<Local>>,
176
177 pub created: Option<DateTime<Local>>,
182
183 pub status: Option<WebHookExecutionStatus>,
189}
190
191impl WebHookPayloadArtifact {
192 pub fn new(
193 id: Option<Uuid>,
194 payload: String,
195 payload_id: PayloadId,
196 trigger: WebHookTrigger,
197 ) -> Self {
198 Self {
199 id,
200 payload,
201 payload_id,
202 trigger,
203 propagations: None,
204 encrypted: None,
205 attempts: None,
206 attempted: None,
207 created: None,
208 status: Some(WebHookExecutionStatus::Pending),
209 }
210 }
211
212 pub fn encode_payload(&mut self) -> Result<Self, MappedErrors> {
217 let serialized_payload =
218 serde_json::to_string(&self.payload).map_err(|e| {
219 dto_err(format!("Failed to serialize payload: {}", e))
220 })?;
221
222 let encoded_payload =
223 general_purpose::STANDARD.encode(serialized_payload.as_bytes());
224
225 Ok(Self {
226 payload: encoded_payload,
227 ..self.clone()
228 })
229 }
230
231 pub fn decode_payload(
236 &self,
237 ) -> Result<WebHookPayloadArtifact, MappedErrors> {
238 let decoded_payload =
239 match general_purpose::STANDARD.decode(&self.payload) {
240 Err(_) => return dto_err("Failed to decode base64").as_error(),
241 Ok(decoded) => String::from_utf8(decoded)
242 .map_err(|_| dto_err("Failed to decode payload"))?,
243 };
244
245 let payload = serde_json::from_str(&decoded_payload).map_err(|e| {
246 dto_err(format!("Failed to deserialize payload: {}", e))
247 })?;
248
249 Ok(Self {
250 payload,
251 ..self.clone()
252 })
253 }
254}