Skip to main content

headless_lms_models/
study_registry_registrars.rs

1use crate::prelude::*;
2
3#[derive(Clone, PartialEq, Deserialize, Serialize)]
4pub struct StudyRegistryRegistrar {
5    pub id: Uuid,
6    pub created_at: DateTime<Utc>,
7    pub updated_at: DateTime<Utc>,
8    pub deleted_at: Option<DateTime<Utc>>,
9    pub name: String,
10    pub secret_key: String,
11}
12
13pub async fn insert(
14    conn: &mut PgConnection,
15    pkey_policy: PKeyPolicy<Uuid>,
16    name: &str,
17    secret_key: &str,
18) -> ModelResult<Uuid> {
19    let res = sqlx::query!(
20        "
21INSERT INTO study_registry_registrars (id, name, secret_key)
22VALUES ($1, $2, $3)
23RETURNING *
24    ",
25        pkey_policy.into_uuid(),
26        name,
27        secret_key
28    )
29    .fetch_one(conn)
30    .await?;
31    Ok(res.id)
32}
33
34pub async fn get_by_id(conn: &mut PgConnection, id: Uuid) -> ModelResult<StudyRegistryRegistrar> {
35    let res = sqlx::query_as!(
36        StudyRegistryRegistrar,
37        "
38SELECT *
39FROM study_registry_registrars
40WHERE id = $1
41  AND deleted_at IS NULL
42        ",
43        id,
44    )
45    .fetch_one(conn)
46    .await?;
47    Ok(res)
48}
49
50pub async fn get_by_secret_key(
51    conn: &mut PgConnection,
52    secret_key: &str,
53) -> ModelResult<StudyRegistryRegistrar> {
54    let res = sqlx::query_as!(
55        StudyRegistryRegistrar,
56        "
57SELECT *
58FROM study_registry_registrars
59WHERE secret_key = $1
60  AND deleted_at IS NULL
61    ",
62        secret_key
63    )
64    .fetch_one(conn)
65    .await?;
66    Ok(res)
67}
68
69pub async fn delete(conn: &mut PgConnection, id: Uuid) -> ModelResult<()> {
70    sqlx::query!(
71        "
72UPDATE study_registry_registrars
73SET deleted_at = now()
74WHERE id = $1
75AND deleted_at IS NULL
76        ",
77        id,
78    )
79    .execute(conn)
80    .await?;
81    Ok(())
82}
83
84/// Registrar for the seeds' legacy pull-flow completion registrations, created on first use.
85///
86/// Matched by name, not `created_at` order: on a fresh database the only row is the Suotar push
87/// registrar seeded by a migration, and handing that one out would put fixture rows in reach of the
88/// migration's revert.
89pub async fn get_or_create_default_registrar(conn: &mut PgConnection) -> ModelResult<Uuid> {
90    let existing = sqlx::query!(
91        r#"
92        SELECT id
93        FROM study_registry_registrars
94        WHERE name = 'Default Registrar'
95          AND deleted_at IS NULL
96        ORDER BY created_at
97        LIMIT 1
98        "#,
99    )
100    .fetch_optional(&mut *conn)
101    .await?;
102
103    if let Some(row) = existing {
104        return Ok(row.id);
105    }
106
107    let inserted = sqlx::query!(
108        r#"
109        INSERT INTO study_registry_registrars (
110            name,
111            secret_key
112        )
113        VALUES (
114            'Default Registrar',
115            encode(gen_random_bytes(32), 'hex')
116        )
117        RETURNING id
118        "#,
119    )
120    .fetch_one(&mut *conn)
121    .await?;
122
123    Ok(inserted.id)
124}
125
126#[cfg(test)]
127mod tests {
128    use super::*;
129    use crate::test_helper::*;
130
131    #[tokio::test]
132    async fn secret_key_needs_to_be_long_enough() {
133        insert_data!(:tx);
134        let id_1 = Uuid::parse_str("88eff75b-4c8f-46f7-a857-9d804b5ec054").unwrap();
135        let res = insert(
136            tx.as_mut(),
137            PKeyPolicy::Fixed(id_1),
138            "test registrar",
139            "12345",
140        )
141        .await;
142        assert!(res.is_err(), "Expected too short key to produce error.");
143    }
144
145    #[tokio::test]
146    async fn secret_key_needs_to_be_unique() {
147        insert_data!(:tx);
148        let id_1 = Uuid::parse_str("88eff75b-4c8f-46f7-a857-9d804b5ec054").unwrap();
149        let res = insert(
150            tx.as_mut(),
151            PKeyPolicy::Fixed(id_1),
152            "test registrar",
153            "123456789-123456",
154        )
155        .await;
156        assert!(res.is_ok(), "Expected insertion to succeed.");
157
158        let id_2 = Uuid::parse_str("d06abb84-0cad-4372-ad2a-7f87d3c1e420").unwrap();
159        let res = insert(
160            tx.as_mut(),
161            PKeyPolicy::Fixed(id_2),
162            "test registrar 2",
163            "123456789-123456",
164        )
165        .await;
166        assert!(
167            res.is_err(),
168            "Expected insertion to fail with duplicate secret key."
169        );
170    }
171}