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