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, Clone)]
12pub struct NodeDatabaseAgentHost {
13 pub node: Fetchable<super::node::Node>,
14 pub database_agent_host: super::database_agent_host::DatabaseAgentHost,
15
16 pub created: chrono::NaiveDateTime,
17
18 extension_data: super::ModelExtensionData,
19}
20
21impl BaseModel for NodeDatabaseAgentHost {
22 const NAME: &'static str = "node_database_agent_host";
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 let mut columns = BTreeMap::from([
40 (
41 "node_database_agent_hosts.node_uuid",
42 compact_str::format_compact!("{prefix}node_uuid"),
43 ),
44 (
45 "node_database_agent_hosts.created",
46 compact_str::format_compact!("{prefix}created"),
47 ),
48 ]);
49
50 columns.extend(super::database_agent_host::DatabaseAgentHost::base_columns(
51 Some("database_agent_host_"),
52 ));
53
54 columns
55 }
56
57 #[inline]
58 fn map(prefix: Option<&str>, row: &PgRow) -> Result<Self, crate::database::DatabaseError> {
59 let prefix = prefix.unwrap_or_default();
60
61 Ok(Self {
62 node: super::node::Node::get_fetchable(
63 row.try_get(compact_str::format_compact!("{prefix}node_uuid").as_str())?,
64 ),
65 database_agent_host: super::database_agent_host::DatabaseAgentHost::map(
66 Some("database_agent_host_"),
67 row,
68 )?,
69 created: row.try_get(compact_str::format_compact!("{prefix}created").as_str())?,
70 extension_data: Self::map_extensions(prefix, row)?,
71 })
72 }
73}
74
75impl NodeDatabaseAgentHost {
76 pub async fn by_node_uuid_database_agent_host_uuid(
77 database: &crate::database::Database,
78 node_uuid: uuid::Uuid,
79 database_agent_host_uuid: uuid::Uuid,
80 ) -> Result<Option<Self>, crate::database::DatabaseError> {
81 let row = sqlx::query(sqlx::AssertSqlSafe(format!(
82 r#"
83 SELECT {}
84 FROM node_database_agent_hosts
85 JOIN database_agent_hosts ON node_database_agent_hosts.database_agent_host_uuid = database_agent_hosts.uuid
86 WHERE node_database_agent_hosts.node_uuid = $1 AND node_database_agent_hosts.database_agent_host_uuid = $2
87 "#,
88 Self::columns_sql(None)
89 )))
90 .bind(node_uuid)
91 .bind(database_agent_host_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_node_uuid_database_agent_host_uuid_with_transaction(
99 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
100 node_uuid: uuid::Uuid,
101 database_agent_host_uuid: uuid::Uuid,
102 ) -> Result<Option<Self>, crate::database::DatabaseError> {
103 let row = sqlx::query(sqlx::AssertSqlSafe(format!(
104 r#"
105 SELECT {}
106 FROM node_database_agent_hosts
107 JOIN database_agent_hosts ON node_database_agent_hosts.database_agent_host_uuid = database_agent_hosts.uuid
108 WHERE node_database_agent_hosts.node_uuid = $1 AND node_database_agent_hosts.database_agent_host_uuid = $2
109 "#,
110 Self::columns_sql(None)
111 )))
112 .bind(node_uuid)
113 .bind(database_agent_host_uuid)
114 .fetch_optional(&mut **transaction)
115 .await?;
116
117 row.try_map(|row| Self::map(None, &row))
118 }
119
120 pub async fn by_node_uuid_with_pagination(
121 database: &crate::database::Database,
122 node_uuid: uuid::Uuid,
123 page: i64,
124 per_page: i64,
125 search: Option<&str>,
126 ) -> Result<super::Pagination<Self>, crate::database::DatabaseError> {
127 let offset = (page - 1) * per_page;
128
129 let rows = sqlx::query(sqlx::AssertSqlSafe(format!(
130 r#"
131 SELECT {}, COUNT(*) OVER() AS total_count
132 FROM node_database_agent_hosts
133 JOIN database_agent_hosts ON node_database_agent_hosts.database_agent_host_uuid = database_agent_hosts.uuid
134 WHERE node_database_agent_hosts.node_uuid = $1 AND ($2 IS NULL OR database_agent_hosts.name ILIKE '%' || $2 || '%')
135 ORDER BY node_database_agent_hosts.created
136 LIMIT $3 OFFSET $4
137 "#,
138 Self::columns_sql(None)
139 )))
140 .bind(node_uuid)
141 .bind(search)
142 .bind(per_page)
143 .bind(offset)
144 .fetch_all(database.read())
145 .await?;
146
147 Ok(super::Pagination {
148 total: rows
149 .first()
150 .map_or(Ok(0), |row| row.try_get("total_count"))?,
151 per_page,
152 page,
153 data: rows
154 .into_iter()
155 .map(|row| Self::map(None, &row))
156 .try_collect_vec()?,
157 })
158 }
159}
160
161#[async_trait::async_trait]
162impl IntoAdminApiObject for NodeDatabaseAgentHost {
163 type AdminApiObject = AdminApiNodeDatabaseAgentHost;
164 type ExtraArgs<'a> = ();
165
166 async fn into_admin_api_object<'a>(
167 self,
168 state: &crate::State,
169 _args: Self::ExtraArgs<'a>,
170 ) -> Result<Self::AdminApiObject, crate::database::DatabaseError> {
171 let api_object = AdminApiNodeDatabaseAgentHost::init_hooks(&self, state).await?;
172
173 let api_object = finish_extendible!(
174 AdminApiNodeDatabaseAgentHost {
175 database_agent_host: self
176 .database_agent_host
177 .into_admin_api_object(state, ())
178 .await?,
179 created: self.created.and_utc(),
180 },
181 api_object,
182 state
183 )?;
184
185 Ok(api_object)
186 }
187}
188
189#[derive(ToSchema, Deserialize, Validate)]
190pub struct CreateNodeDatabaseAgentHostOptions {
191 #[garde(skip)]
192 pub node_uuid: uuid::Uuid,
193 #[garde(skip)]
194 pub database_agent_host_uuid: uuid::Uuid,
195}
196
197#[async_trait::async_trait]
198impl CreatableModel for NodeDatabaseAgentHost {
199 type CreateOptions<'a> = CreateNodeDatabaseAgentHostOptions;
200 type CreateResult = Self;
201
202 fn get_create_handlers() -> &'static LazyLock<CreateListenerList<Self>> {
203 static CREATE_LISTENERS: LazyLock<CreateListenerList<NodeDatabaseAgentHost>> =
204 LazyLock::new(|| Arc::new(ModelHandlerList::default()));
205
206 &CREATE_LISTENERS
207 }
208
209 async fn create_with_transaction(
210 state: &crate::State,
211 mut options: Self::CreateOptions<'_>,
212 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
213 ) -> Result<Self, crate::database::DatabaseError> {
214 options.validate()?;
215
216 super::database_agent_host::DatabaseAgentHost::by_uuid_optional_cached(
217 &state.database,
218 options.database_agent_host_uuid,
219 )
220 .await?
221 .ok_or(crate::database::InvalidRelationError("database_agent_host"))?;
222
223 let mut query_builder = InsertQueryBuilder::new("node_database_agent_hosts");
224
225 Self::run_create_handlers(&mut options, &mut query_builder, state, transaction).await?;
226
227 query_builder
228 .set("node_uuid", options.node_uuid)
229 .set("database_agent_host_uuid", options.database_agent_host_uuid);
230
231 query_builder.execute(&mut **transaction).await?;
232
233 let mut result = match Self::by_node_uuid_database_agent_host_uuid_with_transaction(
234 transaction,
235 options.node_uuid,
236 options.database_agent_host_uuid,
237 )
238 .await?
239 {
240 Some(node_database_agent_host) => node_database_agent_host,
241 None => return Err(sqlx::Error::RowNotFound.into()),
242 };
243
244 Self::run_after_create_handlers(&mut result, &options, state, transaction).await?;
245
246 Ok(result)
247 }
248}
249
250#[async_trait::async_trait]
251impl DeletableModel for NodeDatabaseAgentHost {
252 type DeleteOptions = ();
253
254 fn get_delete_handlers() -> &'static LazyLock<DeleteHandlerList<Self>> {
255 static DELETE_LISTENERS: LazyLock<DeleteHandlerList<NodeDatabaseAgentHost>> =
256 LazyLock::new(|| Arc::new(ModelHandlerList::default()));
257
258 &DELETE_LISTENERS
259 }
260
261 async fn delete_with_transaction(
262 &self,
263 state: &crate::State,
264 options: Self::DeleteOptions,
265 transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>,
266 ) -> Result<(), anyhow::Error> {
267 self.run_delete_handlers(&options, state, transaction)
268 .await?;
269
270 sqlx::query(
271 r#"
272 DELETE FROM node_database_agent_hosts
273 WHERE node_database_agent_hosts.node_uuid = $1 AND node_database_agent_hosts.database_agent_host_uuid = $2
274 "#,
275 )
276 .bind(self.node.uuid)
277 .bind(self.database_agent_host.uuid)
278 .execute(&mut **transaction)
279 .await?;
280
281 self.run_after_delete_handlers(&options, state, transaction)
282 .await?;
283
284 Ok(())
285 }
286}
287
288#[schema_extension_derive::extendible]
289#[init_args(NodeDatabaseAgentHost, crate::State)]
290#[hook_args(crate::State)]
291#[derive(ToSchema, Serialize)]
292#[schema(title = "NodeDatabaseAgentHost")]
293pub struct AdminApiNodeDatabaseAgentHost {
294 pub database_agent_host: super::database_agent_host::AdminApiDatabaseAgentHost,
295
296 pub created: chrono::DateTime<chrono::Utc>,
297}