1use crate::{models::InsertQueryBuilder, prelude::*};
2use garde::Validate;
3use serde::{Deserialize, Serialize};
4use sqlx::{Row, postgres::PgRow};
5use std::{
6 collections::BTreeMap,
7 sync::{Arc, LazyLock},
8};
9use utoipa::ToSchema;
10
11#[derive(Serialize, Deserialize)]
12pub struct SystemBackupPolicyServer {
13 pub system_backup_policy: Fetchable<super::system_backup_policy::SystemBackupPolicy>,
14 pub server: Fetchable<super::server::Server>,
15
16 pub created: chrono::NaiveDateTime,
17
18 extension_data: super::ModelExtensionData,
19}
20
21impl BaseModel for SystemBackupPolicyServer {
22 const NAME: &'static str = "system_backup_policy_server";
23
24 fn get_extension_list() -> &'static super::ModelExtensionList {
25 static EXTENSIONS: LazyLock<super::ModelExtensionList> =
26 LazyLock::new(|| parking_lot::RwLock::new(Vec::new()));
27
28 &EXTENSIONS
29 }
30
31 fn get_extension_data(&self) -> &super::ModelExtensionData {
32 &self.extension_data
33 }
34
35 #[inline]
36 fn base_columns(prefix: Option<&str>) -> BTreeMap<&'static str, compact_str::CompactString> {
37 let prefix = prefix.unwrap_or_default();
38
39 BTreeMap::from([
40 (
41 "system_backup_policy_servers.system_backup_policy_uuid",
42 compact_str::format_compact!("{prefix}system_backup_policy_uuid"),
43 ),
44 (
45 "system_backup_policy_servers.server_uuid",
46 compact_str::format_compact!("{prefix}server_uuid"),
47 ),
48 (
49 "system_backup_policy_servers.created",
50 compact_str::format_compact!("{prefix}created"),
51 ),
52 ])
53 }
54
55 #[inline]
56 fn map(prefix: Option<&str>, row: &PgRow) -> Result<Self, crate::database::DatabaseError> {
57 let prefix = prefix.unwrap_or_default();
58
59 Ok(Self {
60 system_backup_policy: super::system_backup_policy::SystemBackupPolicy::get_fetchable(
61 row.try_get(
62 compact_str::format_compact!("{prefix}system_backup_policy_uuid").as_str(),
63 )?,
64 ),
65 server: super::server::Server::get_fetchable(
66 row.try_get(compact_str::format_compact!("{prefix}server_uuid").as_str())?,
67 ),
68 created: row.try_get(compact_str::format_compact!("{prefix}created").as_str())?,
69 extension_data: Self::map_extensions(prefix, row)?,
70 })
71 }
72}
73
74impl SystemBackupPolicyServer {
75 pub async fn by_system_backup_policy_uuid_server_uuid(
76 database: &crate::database::Database,
77 system_backup_policy_uuid: uuid::Uuid,
78 server_uuid: uuid::Uuid,
79 ) -> Result<Option<Self>, crate::database::DatabaseError> {
80 let row = sqlx::query(sqlx::AssertSqlSafe(format!(
81 r#"
82 SELECT {}
83 FROM system_backup_policy_servers
84 WHERE
85 system_backup_policy_servers.system_backup_policy_uuid = $1
86 AND system_backup_policy_servers.server_uuid = $2
87 "#,
88 Self::columns_sql(None)
89 )))
90 .bind(system_backup_policy_uuid)
91 .bind(server_uuid)
92 .fetch_optional(database.read())
93 .await?;
94
95 row.try_map(|row| Self::map(None, &row))
96 }
97
98 pub async fn by_system_backup_policy_uuid_with_pagination(
99 database: &crate::database::Database,
100 system_backup_policy_uuid: uuid::Uuid,
101 page: i64,
102 per_page: i64,
103 search: Option<&str>,
104 ) -> Result<super::Pagination<Self>, crate::database::DatabaseError> {
105 let offset = (page - 1) * per_page;
106
107 let rows = sqlx::query(sqlx::AssertSqlSafe(format!(
108 r#"
109 SELECT {}, COUNT(*) OVER() AS total_count
110 FROM system_backup_policy_servers
111 JOIN servers ON servers.uuid = system_backup_policy_servers.server_uuid
112 WHERE
113 system_backup_policy_servers.system_backup_policy_uuid = $1
114 AND ($2 IS NULL OR servers.name ILIKE '%' || $2 || '%')
115 ORDER BY system_backup_policy_servers.created
116 LIMIT $3 OFFSET $4
117 "#,
118 Self::columns_sql(None)
119 )))
120 .bind(system_backup_policy_uuid)
121 .bind(search)
122 .bind(per_page)
123 .bind(offset)
124 .fetch_all(database.read())
125 .await?;
126
127 Ok(super::Pagination {
128 total: rows
129 .first()
130 .map_or(Ok(0), |row| row.try_get("total_count"))?,
131 per_page,
132 page,
133 data: rows
134 .into_iter()
135 .map(|row| Self::map(None, &row))
136 .try_collect_vec()?,
137 })
138 }
139}
140
141#[async_trait::async_trait]
142impl IntoAdminApiObject for SystemBackupPolicyServer {
143 type AdminApiObject = AdminApiSystemBackupPolicyServer;
144 type ExtraArgs<'a> = &'a crate::storage::StorageUrlRetriever<'a>;
145
146 async fn into_admin_api_object<'a>(
147 self,
148 state: &crate::State,
149 storage_url_retriever: Self::ExtraArgs<'a>,
150 ) -> Result<Self::AdminApiObject, crate::database::DatabaseError> {
151 let api_object = AdminApiSystemBackupPolicyServer::init_hooks(&self, state).await?;
152
153 let api_object = finish_extendible!(
154 AdminApiSystemBackupPolicyServer {
155 server: self
156 .server
157 .fetch_cached(&state.database)
158 .await?
159 .into_admin_api_object(state, storage_url_retriever)
160 .await?,
161 created: self.created.and_utc(),
162 },
163 api_object,
164 state
165 )?;
166
167 Ok(api_object)
168 }
169}
170
171#[derive(ToSchema, Deserialize, Validate)]
172pub struct CreateSystemBackupPolicyServerOptions {
173 #[garde(skip)]
174 pub system_backup_policy_uuid: uuid::Uuid,
175 #[garde(skip)]
176 pub server_uuid: uuid::Uuid,
177}
178
179#[async_trait::async_trait]
180impl CreatableModel for SystemBackupPolicyServer {
181 type CreateOptions<'a> = CreateSystemBackupPolicyServerOptions;
182 type CreateResult = Self;
183
184 fn get_create_handlers() -> &'static LazyLock<CreateListenerList<Self>> {
185 static CREATE_LISTENERS: LazyLock<CreateListenerList<SystemBackupPolicyServer>> =
186 LazyLock::new(|| Arc::new(ModelHandlerList::default()));
187
188 &CREATE_LISTENERS
189 }
190
191 async fn create_with_transaction(
192 state: &crate::State,
193 mut options: Self::CreateOptions<'_>,
194 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
195 ) -> Result<Self, crate::database::DatabaseError> {
196 options.validate()?;
197
198 super::server::Server::by_uuid_optional_cached(&state.database, options.server_uuid)
199 .await?
200 .ok_or(crate::database::InvalidRelationError("server"))?;
201
202 let mut query_builder = InsertQueryBuilder::new("system_backup_policy_servers");
203
204 Self::run_create_handlers(&mut options, &mut query_builder, state, transaction).await?;
205
206 query_builder
207 .set(
208 "system_backup_policy_uuid",
209 options.system_backup_policy_uuid,
210 )
211 .set("server_uuid", options.server_uuid);
212
213 let row = query_builder
214 .returning(&Self::columns_sql(None))
215 .fetch_one(&mut **transaction)
216 .await?;
217 let mut policy_server = Self::map(None, &row)?;
218
219 Self::run_after_create_handlers(&mut policy_server, &options, state, transaction).await?;
220
221 Ok(policy_server)
222 }
223}
224
225#[async_trait::async_trait]
226impl DeletableModel for SystemBackupPolicyServer {
227 type DeleteOptions = ();
228
229 fn get_delete_handlers() -> &'static LazyLock<DeleteHandlerList<Self>> {
230 static DELETE_LISTENERS: LazyLock<DeleteHandlerList<SystemBackupPolicyServer>> =
231 LazyLock::new(|| Arc::new(ModelHandlerList::default()));
232
233 &DELETE_LISTENERS
234 }
235
236 async fn delete_with_transaction(
237 &self,
238 state: &crate::State,
239 options: Self::DeleteOptions,
240 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
241 ) -> Result<(), anyhow::Error> {
242 self.run_delete_handlers(&options, state, transaction)
243 .await?;
244
245 sqlx::query(
246 r#"
247 DELETE FROM system_backup_policy_servers
248 WHERE
249 system_backup_policy_servers.system_backup_policy_uuid = $1
250 AND system_backup_policy_servers.server_uuid = $2
251 "#,
252 )
253 .bind(self.system_backup_policy.uuid)
254 .bind(self.server.uuid)
255 .execute(&mut **transaction)
256 .await?;
257
258 self.run_after_delete_handlers(&options, state, transaction)
259 .await?;
260
261 Ok(())
262 }
263}
264
265#[schema_extension_derive::extendible]
266#[init_args(SystemBackupPolicyServer, crate::State)]
267#[hook_args(crate::State)]
268#[derive(ToSchema, Serialize)]
269#[schema(title = "SystemBackupPolicyServer")]
270pub struct AdminApiSystemBackupPolicyServer {
271 pub server: super::server::AdminApiServer,
272
273 pub created: chrono::DateTime<chrono::Utc>,
274}