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
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}