1pub mod user {
2 use crate::db::DBError;
3 use crate::error::ServerError;
4 use crate::user::User;
5 use finance::tag::Tag;
6 use sqlx::Connection;
7 use sqlx::types::Uuid;
8
9 const KIND_AUTOMATION: &str = "automation";
10 const KIND_TEMPLATE: &str = "template";
11
12 pub struct ScriptInfo {
13 pub id: Uuid,
14 pub name: Option<String>,
15 pub size: i32,
16 pub enabled: Option<String>,
17 }
18
19 pub struct ScriptDetail {
20 pub id: Uuid,
21 pub name: Option<String>,
22 pub bytecode: Vec<u8>,
23 pub enabled: Option<String>,
24 }
25
26 pub struct TemplateInfo {
27 pub id: Uuid,
28 pub name: Option<String>,
29 pub size: i32,
30 pub enabled: Option<String>,
31 }
32
33 pub struct TemplateDetail {
34 pub id: Uuid,
35 pub name: Option<String>,
36 pub source: String,
37 pub enabled: Option<String>,
38 }
39
40 fn db_err(err: sqlx::Error) -> ServerError {
41 log::error!("{}", t!("Database error: %{err}", err = err : {:?}));
42 ServerError::DB(DBError::Sqlx(err))
43 }
44
45 impl User {
46 async fn link_artifact_tag(
50 conn: &mut sqlx::PgConnection,
51 artifact_id: Uuid,
52 name: &str,
53 value: &str,
54 ) -> Result<(), ServerError> {
55 let canonical_id = Tag {
56 id: Uuid::new_v4(),
57 tag_name: name.to_string(),
58 tag_value: value.to_string(),
59 description: None,
60 }
61 .commit(&mut *conn)
62 .await
63 .map_err(|err| {
64 log::error!("{}", t!("Database error: %{err}", err = err : {:?}));
65 ServerError::Finance(err)
66 })?;
67
68 sqlx::query_file!(
69 "sql/insert/artifact_tags/artifact_tag.sql",
70 &artifact_id,
71 &canonical_id
72 )
73 .execute(&mut *conn)
74 .await
75 .map_err(db_err)?;
76
77 Ok(())
78 }
79
80 pub async fn list_scripts(&self) -> Result<Vec<ScriptInfo>, ServerError> {
81 let mut conn = self.get_connection().await?;
82
83 let rows = sqlx::query_file!("sql/select/artifacts/by_kind.sql", KIND_AUTOMATION)
84 .fetch_all(&mut *conn)
85 .await
86 .map_err(db_err)?;
87
88 Ok(rows
89 .into_iter()
90 .map(|r| ScriptInfo {
91 id: r.id,
92 name: r.artifact_name,
93 size: r.size.unwrap_or(0),
94 enabled: r.enabled,
95 })
96 .collect())
97 }
98
99 pub async fn get_script(&self, id: Uuid) -> Result<ScriptDetail, ServerError> {
100 let mut conn = self.get_connection().await?;
101
102 let row = sqlx::query_file!("sql/select/artifacts/by_id.sql", &id, KIND_AUTOMATION)
103 .fetch_one(&mut *conn)
104 .await
105 .map_err(db_err)?;
106
107 Ok(ScriptDetail {
108 id: row.id,
109 name: row.artifact_name,
110 bytecode: row.bytecode.unwrap_or_default(),
111 enabled: row.enabled,
112 })
113 }
114
115 pub async fn create_script(
116 &self,
117 bytecode: Vec<u8>,
118 name: Option<String>,
119 ) -> Result<Uuid, ServerError> {
120 let mut conn = self.get_connection().await?;
121 let mut tx = conn.begin().await.map_err(db_err)?;
122
123 let id = Uuid::new_v4();
124 sqlx::query_file!(
125 "sql/insert/artifacts/artifact.sql",
126 &id,
127 Some(&bytecode[..]),
128 None::<&str>
129 )
130 .execute(&mut *tx)
131 .await
132 .map_err(db_err)?;
133
134 User::link_artifact_tag(&mut tx, id, "kind", KIND_AUTOMATION).await?;
135 if let Some(name) = name {
136 User::link_artifact_tag(&mut tx, id, "name", &name).await?;
137 }
138
139 tx.commit().await.map_err(db_err)?;
140 Ok(id)
141 }
142
143 pub async fn update_script_bytecode(
144 &self,
145 id: Uuid,
146 bytecode: Vec<u8>,
147 ) -> Result<(), ServerError> {
148 let mut conn = self.get_connection().await?;
149
150 sqlx::query_file!("sql/update/artifacts/bytecode.sql", &id, &bytecode)
151 .execute(&mut *conn)
152 .await
153 .map_err(db_err)?;
154
155 Ok(())
156 }
157
158 pub async fn delete_script(&self, id: Uuid) -> Result<(), ServerError> {
159 self.delete_artifact(id).await
160 }
161
162 pub async fn set_script_enabled(&self, id: Uuid, enabled: bool) -> Result<(), ServerError> {
163 self.set_artifact_enabled(id, enabled).await
164 }
165
166 pub async fn update_script_name(
167 &self,
168 id: Uuid,
169 name: Option<String>,
170 ) -> Result<(), ServerError> {
171 self.update_artifact_name(id, name).await
172 }
173
174 pub async fn list_templates(&self) -> Result<Vec<TemplateInfo>, ServerError> {
175 let mut conn = self.get_connection().await?;
176
177 let rows = sqlx::query_file!("sql/select/artifacts/by_kind.sql", KIND_TEMPLATE)
178 .fetch_all(&mut *conn)
179 .await
180 .map_err(db_err)?;
181
182 Ok(rows
183 .into_iter()
184 .map(|r| TemplateInfo {
185 id: r.id,
186 name: r.artifact_name,
187 size: r.size.unwrap_or(0),
188 enabled: r.enabled,
189 })
190 .collect())
191 }
192
193 pub async fn get_template(&self, id: Uuid) -> Result<TemplateDetail, ServerError> {
194 let mut conn = self.get_connection().await?;
195
196 let row = sqlx::query_file!("sql/select/artifacts/by_id.sql", &id, KIND_TEMPLATE)
197 .fetch_one(&mut *conn)
198 .await
199 .map_err(db_err)?;
200
201 Ok(TemplateDetail {
202 id: row.id,
203 name: row.artifact_name,
204 source: row.source.unwrap_or_default(),
205 enabled: row.enabled,
206 })
207 }
208
209 pub async fn create_template(
210 &self,
211 source: String,
212 name: Option<String>,
213 ) -> Result<Uuid, ServerError> {
214 let mut conn = self.get_connection().await?;
215 let mut tx = conn.begin().await.map_err(db_err)?;
216
217 let id = Uuid::new_v4();
218 sqlx::query_file!(
219 "sql/insert/artifacts/artifact.sql",
220 &id,
221 None::<&[u8]>,
222 Some(source.as_str())
223 )
224 .execute(&mut *tx)
225 .await
226 .map_err(db_err)?;
227
228 User::link_artifact_tag(&mut tx, id, "kind", KIND_TEMPLATE).await?;
229 if let Some(name) = name {
230 User::link_artifact_tag(&mut tx, id, "name", &name).await?;
231 }
232
233 tx.commit().await.map_err(db_err)?;
234 Ok(id)
235 }
236
237 pub async fn update_template_source(
238 &self,
239 id: Uuid,
240 source: String,
241 ) -> Result<(), ServerError> {
242 let mut conn = self.get_connection().await?;
243
244 sqlx::query_file!("sql/update/artifacts/source.sql", &id, &source)
245 .execute(&mut *conn)
246 .await
247 .map_err(db_err)?;
248
249 Ok(())
250 }
251
252 pub async fn delete_template(&self, id: Uuid) -> Result<(), ServerError> {
253 self.delete_artifact(id).await
254 }
255
256 pub async fn update_template_name(
257 &self,
258 id: Uuid,
259 name: Option<String>,
260 ) -> Result<(), ServerError> {
261 self.update_artifact_name(id, name).await
262 }
263
264 async fn delete_artifact(&self, id: Uuid) -> Result<(), ServerError> {
267 let mut conn = self.get_connection().await?;
268 let mut tx = conn.begin().await.map_err(db_err)?;
269
270 sqlx::query_file!("sql/delete/artifact_tags/by_artifact.sql", &id)
271 .execute(&mut *tx)
272 .await
273 .map_err(db_err)?;
274
275 sqlx::query_file!("sql/delete/artifacts/by_id.sql", &id)
276 .execute(&mut *tx)
277 .await
278 .map_err(db_err)?;
279
280 tx.commit().await.map_err(db_err)?;
281 Ok(())
282 }
283
284 async fn set_artifact_enabled(&self, id: Uuid, enabled: bool) -> Result<(), ServerError> {
285 let mut conn = self.get_connection().await?;
286 let mut tx = conn.begin().await.map_err(db_err)?;
287
288 sqlx::query_file!(
289 "sql/delete/artifact_tags/by_artifact_and_name.sql",
290 &id,
291 "enabled"
292 )
293 .execute(&mut *tx)
294 .await
295 .map_err(db_err)?;
296
297 if !enabled {
298 User::link_artifact_tag(&mut tx, id, "enabled", "false").await?;
299 }
300
301 tx.commit().await.map_err(db_err)?;
302 Ok(())
303 }
304
305 async fn update_artifact_name(
306 &self,
307 id: Uuid,
308 name: Option<String>,
309 ) -> Result<(), ServerError> {
310 let mut conn = self.get_connection().await?;
311 let mut tx = conn.begin().await.map_err(db_err)?;
312
313 sqlx::query_file!(
314 "sql/delete/artifact_tags/by_artifact_and_name.sql",
315 &id,
316 "name"
317 )
318 .execute(&mut *tx)
319 .await
320 .map_err(db_err)?;
321
322 if let Some(name) = name {
323 User::link_artifact_tag(&mut tx, id, "name", &name).await?;
324 }
325
326 tx.commit().await.map_err(db_err)?;
327 Ok(())
328 }
329 }
330}
331
332#[cfg(test)]
333mod artifact_mgmt_tests;