Skip to main content

shared/models/
server_schedule_step.rs

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}