Skip to main content

server/command/
config.rs

1use sqlx::Row;
2use sqlx::types::Uuid;
3use std::fmt::Debug;
4use supp_macro::command;
5
6use super::{CmdError, CmdResult};
7use crate::{
8    config::{ConfigError, ConfigOption},
9    db::get_connection,
10    user::User,
11};
12command! {
13    GetConfig {
14        #[required]
15        user_id: Uuid,
16        #[required]
17        name: String,
18    } => {
19        let user = User { id: user_id };
20        Ok(user.config(&name).await?.map(|v| match v {
21            ConfigOption::String(s) => CmdResult::String(s),
22            ConfigOption::Blob(b) => CmdResult::Data(b),
23        }))
24    }
25}
26
27command! {
28    GetVersion {
29    } => {
30        const HASH: &str = env!("GIT_HASH");
31        Ok(Some(CmdResult::String(HASH.to_string())))
32    }
33}
34
35command! {
36    GetBuildDate {
37    } => {
38    const BUILD: &str = env!("BUILD_DATE");
39        Ok(Some(CmdResult::String(BUILD.to_string())))
40    }
41}
42
43command! {
44    SetConfig {
45        #[required]
46        user_id: Uuid,
47        #[required]
48        name: String,
49        #[required]
50        value: String,
51    } => {
52        let user = User { id: user_id };
53        user.set_config(&name, ConfigOption::String(value)).await?;
54        Ok(None)
55    }
56}
57
58command! {
59    SelectColumn {
60        #[required]
61        field: String,
62        #[required]
63        table: String,
64    } => {
65        let mut conn = get_connection().await.map_err(|err| {
66            log::error!("{}", t!("Database error: %{err}", err = err : {:?}));
67            ConfigError::DB
68        })?;
69
70        let values: Vec<String> = sqlx::query(&format!("SELECT {field}::text FROM {table}"))
71            .fetch_all(&mut *conn)
72            .await?
73            .into_iter()
74            .map(|row| row.get::<Option<String>, _>(0).unwrap_or_default())
75            .collect();
76
77        Ok(Some(CmdResult::Lines(values)))
78    }
79}
80
81#[cfg(test)]
82mod command_config_tests {
83    use super::*;
84    use crate::{db::DB_POOL, user::User};
85    use sqlx::{PgPool, types::Uuid};
86    use supp_macro::local_db_sqlx_test;
87    use tokio::sync::OnceCell;
88
89    /// Context for keeping environment intact
90    static CONTEXT: OnceCell<()> = OnceCell::const_new();
91    static USER: OnceCell<User> = OnceCell::const_new();
92
93    async fn setup() {
94        CONTEXT
95            .get_or_init(|| async {
96                #[cfg(feature = "testlog")]
97                let _ = env_logger::builder()
98                    .is_test(true)
99                    .filter_level(log::LevelFilter::Trace)
100                    .try_init();
101            })
102            .await;
103        USER.get_or_init(|| async { User { id: Uuid::new_v4() } })
104            .await;
105    }
106
107    #[local_db_sqlx_test]
108    async fn set_then_get_config_round_trips_per_user(pool: PgPool) -> anyhow::Result<()> {
109        let user_id = Uuid::new_v4();
110        SetConfig::new()
111            .user_id(user_id)
112            .name("testfield".to_string())
113            .value("testval".to_string())
114            .run()
115            .await?;
116
117        let val = GetConfig::new()
118            .user_id(user_id)
119            .name("testfield".to_string())
120            .run()
121            .await?
122            .unwrap();
123        assert_eq!(val.to_string(), "testval");
124    }
125}