1use crate::{
2 models::{InsertQueryBuilder, UpdateQueryBuilder},
3 prelude::*,
4};
5use garde::Validate;
6use serde::{Deserialize, Serialize};
7use sqlx::{Row, postgres::PgRow};
8use std::{
9 collections::BTreeMap,
10 sync::{Arc, LazyLock},
11};
12use utoipa::ToSchema;
13
14#[derive(ToSchema, Validate, Serialize, Deserialize)]
15pub struct ExportedServerScheduleStep {
16 #[garde(dive)]
17 pub action: wings_api::ScheduleActionInner,
18 #[garde(skip)]
19 pub order: i16,
20}
21
22#[derive(Serialize, Deserialize)]
23pub struct ServerScheduleStep {
24 pub uuid: uuid::Uuid,
25
26 pub action: wings_api::ScheduleActionInner,
27 pub order: i16,
28 pub error: Option<compact_str::CompactString>,
29
30 pub created: chrono::NaiveDateTime,
31}
32
33impl BaseModel for ServerScheduleStep {
34 const NAME: &'static str = "server_schedule_step";
35
36 #[inline]
37 fn columns(prefix: Option<&str>) -> BTreeMap<&'static str, compact_str::CompactString> {
38 let prefix = prefix.unwrap_or_default();
39
40 BTreeMap::from([
41 (
42 "server_schedule_steps.uuid",
43 compact_str::format_compact!("{prefix}uuid"),
44 ),
45 (
46 "server_schedule_steps.action",
47 compact_str::format_compact!("{prefix}action"),
48 ),
49 (
50 "server_schedule_steps.order_",
51 compact_str::format_compact!("{prefix}order"),
52 ),
53 (
54 "server_schedule_steps.error",
55 compact_str::format_compact!("{prefix}error"),
56 ),
57 (
58 "server_schedule_steps.created",
59 compact_str::format_compact!("{prefix}created"),
60 ),
61 ])
62 }
63
64 #[inline]
65 fn map(prefix: Option<&str>, row: &PgRow) -> Result<Self, crate::database::DatabaseError> {
66 let prefix = prefix.unwrap_or_default();
67
68 Ok(Self {
69 uuid: row.try_get(compact_str::format_compact!("{prefix}uuid").as_str())?,
70 action: serde_json::from_value(
71 row.try_get(compact_str::format_compact!("{prefix}action").as_str())?,
72 )?,
73 order: row.try_get(compact_str::format_compact!("{prefix}order").as_str())?,
74 error: row.try_get(compact_str::format_compact!("{prefix}error").as_str())?,
75 created: row.try_get(compact_str::format_compact!("{prefix}created").as_str())?,
76 })
77 }
78}
79
80impl ServerScheduleStep {
81 pub async fn by_schedule_uuid_uuid(
82 database: &crate::database::Database,
83 schedule_uuid: uuid::Uuid,
84 uuid: uuid::Uuid,
85 ) -> Result<Option<Self>, crate::database::DatabaseError> {
86 let row = sqlx::query(&format!(
87 r#"
88 SELECT {}
89 FROM server_schedule_steps
90 WHERE server_schedule_steps.schedule_uuid = $1 AND server_schedule_steps.uuid = $2
91 "#,
92 Self::columns_sql(None)
93 ))
94 .bind(schedule_uuid)
95 .bind(uuid)
96 .fetch_optional(database.read())
97 .await?;
98
99 row.try_map(|row| Self::map(None, &row))
100 }
101
102 pub async fn all_by_schedule_uuid(
103 database: &crate::database::Database,
104 schedule_uuid: uuid::Uuid,
105 ) -> Result<Vec<Self>, crate::database::DatabaseError> {
106 let rows = sqlx::query(&format!(
107 r#"
108 SELECT {}
109 FROM server_schedule_steps
110 WHERE server_schedule_steps.schedule_uuid = $1
111 ORDER BY server_schedule_steps.order_, server_schedule_steps.created
112 "#,
113 Self::columns_sql(None)
114 ))
115 .bind(schedule_uuid)
116 .fetch_all(database.read())
117 .await?;
118
119 rows.into_iter()
120 .map(|row| Self::map(None, &row))
121 .try_collect_vec()
122 }
123
124 pub async fn count_by_schedule_uuid(
125 database: &crate::database::Database,
126 schedule_uuid: uuid::Uuid,
127 ) -> i64 {
128 sqlx::query_scalar(
129 r#"
130 SELECT COUNT(*)
131 FROM server_schedule_steps
132 WHERE server_schedule_steps.schedule_uuid = $1
133 "#,
134 )
135 .bind(schedule_uuid)
136 .fetch_one(database.read())
137 .await
138 .unwrap_or(0)
139 }
140
141 #[inline]
142 pub fn into_exported(self) -> ExportedServerScheduleStep {
143 ExportedServerScheduleStep {
144 action: self.action,
145 order: self.order,
146 }
147 }
148
149 #[inline]
150 pub fn into_api_object(self) -> ApiServerScheduleStep {
151 ApiServerScheduleStep {
152 uuid: self.uuid,
153 action: self.action,
154 order: self.order,
155 error: self.error,
156 created: self.created.and_utc(),
157 }
158 }
159}
160
161#[derive(ToSchema, Deserialize, Validate)]
162pub struct CreateServerScheduleStepOptions {
163 #[garde(skip)]
164 pub schedule_uuid: uuid::Uuid,
165 #[garde(dive)]
166 pub action: wings_api::ScheduleActionInner,
167 #[garde(skip)]
168 pub order: i16,
169}
170
171#[async_trait::async_trait]
172impl CreatableModel for ServerScheduleStep {
173 type CreateOptions<'a> = CreateServerScheduleStepOptions;
174 type CreateResult = Self;
175
176 fn get_create_handlers() -> &'static LazyLock<CreateListenerList<Self>> {
177 static CREATE_LISTENERS: LazyLock<CreateListenerList<ServerScheduleStep>> =
178 LazyLock::new(|| Arc::new(ModelHandlerList::default()));
179
180 &CREATE_LISTENERS
181 }
182
183 async fn create(
184 state: &crate::State,
185 mut options: Self::CreateOptions<'_>,
186 ) -> Result<Self, crate::database::DatabaseError> {
187 options.validate()?;
188
189 let mut transaction = state.database.write().begin().await?;
190
191 let mut query_builder = InsertQueryBuilder::new("server_schedule_steps");
192
193 Self::run_create_handlers(&mut options, &mut query_builder, state, &mut transaction)
194 .await?;
195
196 query_builder
197 .set("schedule_uuid", options.schedule_uuid)
198 .set("action", serde_json::to_value(&options.action)?)
199 .set("order_", options.order);
200
201 let row = query_builder
202 .returning(&Self::columns_sql(None))
203 .fetch_one(&mut *transaction)
204 .await?;
205 let step = Self::map(None, &row)?;
206
207 transaction.commit().await?;
208
209 Ok(step)
210 }
211}
212
213#[derive(ToSchema, Serialize, Deserialize, Validate, Default)]
214pub struct UpdateServerScheduleStepOptions {
215 #[garde(dive)]
216 pub action: Option<wings_api::ScheduleActionInner>,
217 #[garde(skip)]
218 pub order: Option<i16>,
219}
220
221#[async_trait::async_trait]
222impl UpdatableModel for ServerScheduleStep {
223 type UpdateOptions = UpdateServerScheduleStepOptions;
224
225 fn get_update_handlers() -> &'static LazyLock<UpdateListenerList<Self>> {
226 static UPDATE_LISTENERS: LazyLock<UpdateListenerList<ServerScheduleStep>> =
227 LazyLock::new(|| Arc::new(ModelHandlerList::default()));
228
229 &UPDATE_LISTENERS
230 }
231
232 async fn update(
233 &mut self,
234 state: &crate::State,
235 mut options: Self::UpdateOptions,
236 ) -> Result<(), crate::database::DatabaseError> {
237 options.validate()?;
238
239 let mut transaction = state.database.write().begin().await?;
240
241 let mut query_builder = UpdateQueryBuilder::new("server_schedule_steps");
242
243 Self::run_update_handlers(
244 self,
245 &mut options,
246 &mut query_builder,
247 state,
248 &mut transaction,
249 )
250 .await?;
251
252 query_builder
253 .set(
254 "action",
255 if let Some(action) = &options.action {
256 Some(serde_json::to_value(action)?)
257 } else {
258 None
259 },
260 )
261 .set("order_", options.order)
262 .where_eq("uuid", self.uuid);
263
264 query_builder.execute(&mut *transaction).await?;
265
266 if let Some(action) = options.action {
267 self.action = action;
268 }
269 if let Some(order) = options.order {
270 self.order = order;
271 }
272
273 transaction.commit().await?;
274
275 Ok(())
276 }
277}
278
279#[async_trait::async_trait]
280impl DeletableModel for ServerScheduleStep {
281 type DeleteOptions = ();
282
283 fn get_delete_handlers() -> &'static LazyLock<DeleteListenerList<Self>> {
284 static DELETE_LISTENERS: LazyLock<DeleteListenerList<ServerScheduleStep>> =
285 LazyLock::new(|| Arc::new(ModelHandlerList::default()));
286
287 &DELETE_LISTENERS
288 }
289
290 async fn delete(
291 &self,
292 state: &crate::State,
293 options: Self::DeleteOptions,
294 ) -> Result<(), anyhow::Error> {
295 let mut transaction = state.database.write().begin().await?;
296
297 self.run_delete_handlers(&options, state, &mut transaction)
298 .await?;
299
300 sqlx::query(
301 r#"
302 DELETE FROM server_schedule_steps
303 WHERE server_schedule_steps.uuid = $1
304 "#,
305 )
306 .bind(self.uuid)
307 .execute(&mut *transaction)
308 .await?;
309
310 transaction.commit().await?;
311
312 Ok(())
313 }
314}
315
316#[derive(ToSchema, Serialize)]
317#[schema(title = "ServerScheduleStep")]
318pub struct ApiServerScheduleStep {
319 pub uuid: uuid::Uuid,
320
321 pub action: wings_api::ScheduleActionInner,
322 pub order: i16,
323 pub error: Option<compact_str::CompactString>,
324
325 pub created: chrono::DateTime<chrono::Utc>,
326}