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 SystemBackupPolicyLocation {
13 pub system_backup_policy: Fetchable<super::system_backup_policy::SystemBackupPolicy>,
14 pub location: Fetchable<super::location::Location>,
15
16 pub created: chrono::NaiveDateTime,
17
18 extension_data: super::ModelExtensionData,
19}
20
21impl BaseModel for SystemBackupPolicyLocation {
22 const NAME: &'static str = "system_backup_policy_location";
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_locations.system_backup_policy_uuid",
42 compact_str::format_compact!("{prefix}system_backup_policy_uuid"),
43 ),
44 (
45 "system_backup_policy_locations.location_uuid",
46 compact_str::format_compact!("{prefix}location_uuid"),
47 ),
48 (
49 "system_backup_policy_locations.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 location: super::location::Location::get_fetchable(
66 row.try_get(compact_str::format_compact!("{prefix}location_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 SystemBackupPolicyLocation {
75 pub async fn by_system_backup_policy_uuid_location_uuid(
76 database: &crate::database::Database,
77 system_backup_policy_uuid: uuid::Uuid,
78 location_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_locations
84 WHERE
85 system_backup_policy_locations.system_backup_policy_uuid = $1
86 AND system_backup_policy_locations.location_uuid = $2
87 "#,
88 Self::columns_sql(None)
89 )))
90 .bind(system_backup_policy_uuid)
91 .bind(location_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 super::PaginatedQueryBuilder::new(
106 Self::columns_sql(None),
107 format!(
108 r#"
109 FROM system_backup_policy_locations
110 JOIN locations ON locations.uuid = system_backup_policy_locations.location_uuid
111 WHERE
112 system_backup_policy_locations.system_backup_policy_uuid = $1
113 AND {search}
114 "#,
115 search = super::search_sql(2, &["locations.name"], &["locations.uuid"]),
116 ),
117 "system_backup_policy_locations.created, system_backup_policy_locations.system_backup_policy_uuid, system_backup_policy_locations.location_uuid",
118 )
119 .bind(system_backup_policy_uuid)
120 .bind(search)
121 .fetch(database, page, per_page)
122 .await
123 }
124}
125
126#[async_trait::async_trait]
127impl IntoAdminApiObject for SystemBackupPolicyLocation {
128 type AdminApiObject = AdminApiSystemBackupPolicyLocation;
129 type ExtraArgs<'a> = ();
130
131 async fn into_admin_api_object<'a>(
132 self,
133 state: &crate::State,
134 _args: Self::ExtraArgs<'a>,
135 ) -> Result<Self::AdminApiObject, crate::database::DatabaseError> {
136 let api_object = AdminApiSystemBackupPolicyLocation::init_hooks(&self, state).await?;
137
138 let api_object = finish_extendible!(
139 AdminApiSystemBackupPolicyLocation {
140 location: self
141 .location
142 .fetch_cached(&state.database)
143 .await?
144 .into_admin_api_object(state, ())
145 .await?,
146 created: self.created.and_utc(),
147 },
148 api_object,
149 state
150 )?;
151
152 Ok(api_object)
153 }
154}
155
156#[derive(ToSchema, Deserialize, Validate)]
157pub struct CreateSystemBackupPolicyLocationOptions {
158 #[garde(skip)]
159 pub system_backup_policy_uuid: uuid::Uuid,
160 #[garde(skip)]
161 pub location_uuid: uuid::Uuid,
162}
163
164#[async_trait::async_trait]
165impl CreatableModel for SystemBackupPolicyLocation {
166 type CreateOptions<'a> = CreateSystemBackupPolicyLocationOptions;
167 type CreateResult = Self;
168
169 fn get_create_handlers() -> &'static LazyLock<CreateListenerList<Self>> {
170 static CREATE_LISTENERS: LazyLock<CreateListenerList<SystemBackupPolicyLocation>> =
171 LazyLock::new(|| Arc::new(ModelHandlerList::default()));
172
173 &CREATE_LISTENERS
174 }
175
176 async fn create_with_transaction(
177 state: &crate::State,
178 mut options: Self::CreateOptions<'_>,
179 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
180 ) -> Result<Self, crate::database::DatabaseError> {
181 options.validate()?;
182
183 super::location::Location::by_uuid_optional_cached(&state.database, options.location_uuid)
184 .await?
185 .ok_or(crate::database::InvalidRelationError("location"))?;
186
187 let mut query_builder = InsertQueryBuilder::new("system_backup_policy_locations");
188
189 Self::run_create_handlers(&mut options, &mut query_builder, state, transaction).await?;
190
191 query_builder
192 .set(
193 "system_backup_policy_uuid",
194 options.system_backup_policy_uuid,
195 )
196 .set("location_uuid", options.location_uuid);
197
198 let row = query_builder
199 .returning(&Self::columns_sql(None))
200 .fetch_one(&mut **transaction)
201 .await?;
202 let mut policy_location = Self::map(None, &row)?;
203
204 Self::run_after_create_handlers(&mut policy_location, &options, state, transaction).await?;
205
206 Ok(policy_location)
207 }
208}
209
210#[async_trait::async_trait]
211impl DeletableModel for SystemBackupPolicyLocation {
212 type DeleteOptions = ();
213
214 fn get_delete_handlers() -> &'static LazyLock<DeleteHandlerList<Self>> {
215 static DELETE_LISTENERS: LazyLock<DeleteHandlerList<SystemBackupPolicyLocation>> =
216 LazyLock::new(|| Arc::new(ModelHandlerList::default()));
217
218 &DELETE_LISTENERS
219 }
220
221 async fn delete_with_transaction(
222 &self,
223 state: &crate::State,
224 options: Self::DeleteOptions,
225 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
226 ) -> Result<(), anyhow::Error> {
227 self.run_delete_handlers(&options, state, transaction)
228 .await?;
229
230 sqlx::query(
231 r#"
232 DELETE FROM system_backup_policy_locations
233 WHERE
234 system_backup_policy_locations.system_backup_policy_uuid = $1
235 AND system_backup_policy_locations.location_uuid = $2
236 "#,
237 )
238 .bind(self.system_backup_policy.uuid)
239 .bind(self.location.uuid)
240 .execute(&mut **transaction)
241 .await?;
242
243 self.run_after_delete_handlers(&options, state, transaction)
244 .await?;
245
246 Ok(())
247 }
248}
249
250#[schema_extension_derive::extendible]
251#[init_args(SystemBackupPolicyLocation, crate::State)]
252#[hook_args(crate::State)]
253#[derive(ToSchema, Serialize)]
254#[schema(title = "SystemBackupPolicyLocation")]
255pub struct AdminApiSystemBackupPolicyLocation {
256 pub location: super::location::AdminApiLocation,
257
258 pub created: chrono::DateTime<chrono::Utc>,
259}