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_ids(
51    conn: &mut PgConnection,
52    ids: &[Uuid],
53) -> ModelResult<Vec<StudyRegistryRegistrar>> {
54    let res = sqlx::query_as!(
55        StudyRegistryRegistrar,
56        "
57SELECT *
58FROM study_registry_registrars
59WHERE id = ANY($1)
60  AND deleted_at IS NULL
61        ",
62        ids,
63    )
64    .fetch_all(conn)
65    .await?;
66    Ok(res)
67}
68
69pub async fn get_by_secret_key(
70    conn: &mut PgConnection,
71    secret_key: &str,
72) -> ModelResult<StudyRegistryRegistrar> {
73    let res = sqlx::query_as!(
74        StudyRegistryRegistrar,
75        "
76SELECT *
77FROM study_registry_registrars
78WHERE secret_key = $1
79  AND deleted_at IS NULL
80    ",
81        secret_key
82    )
83    .fetch_one(conn)
84    .await?;
85    Ok(res)
86}
87
88pub async fn delete(conn: &mut PgConnection, id: Uuid) -> ModelResult<()> {
89    sqlx::query!(
90        "
91UPDATE study_registry_registrars
92SET deleted_at = now()
93WHERE id = $1
94AND deleted_at IS NULL
95        ",
96        id,
97    )
98    .execute(conn)
99    .await?;
100    Ok(())
101}
102
103/// Registrar for the seeds' legacy pull-flow completion registrations, created on first use.
104///
105/// Matched by name, so a reseed reuses the same row instead of piling up registrars.
106pub async fn get_or_create_default_registrar(conn: &mut PgConnection) -> ModelResult<Uuid> {
107    let existing = sqlx::query!(
108        r#"
109        SELECT id
110        FROM study_registry_registrars
111        WHERE name = 'Default Registrar'
112          AND deleted_at IS NULL
113        ORDER BY created_at
114        LIMIT 1
115        "#,
116    )
117    .fetch_optional(&mut *conn)
118    .await?;
119
120    if let Some(row) = existing {
121        return Ok(row.id);
122    }
123
124    let inserted = sqlx::query!(
125        r#"
126        INSERT INTO study_registry_registrars (
127            name,
128            secret_key
129        )
130        VALUES (
131            'Default Registrar',
132            encode(gen_random_bytes(32), 'hex')
133        )
134        RETURNING id
135        "#,
136    )
137    .fetch_one(&mut *conn)
138    .await?;
139
140    Ok(inserted.id)
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146    use crate::test_helper::*;
147
148    #[tokio::test]
149    async fn secret_key_needs_to_be_long_enough() {
150        insert_data!(:tx);
151        let id_1 = Uuid::parse_str("88eff75b-4c8f-46f7-a857-9d804b5ec054").unwrap();
152        let res = insert(
153            tx.as_mut(),
154            PKeyPolicy::Fixed(id_1),
155            "test registrar",
156            "12345",
157        )
158        .await;
159        assert!(res.is_err(), "Expected too short key to produce error.");
160    }
161
162    #[tokio::test]
163    async fn secret_key_needs_to_be_unique() {
164        insert_data!(:tx);
165        let id_1 = Uuid::parse_str("88eff75b-4c8f-46f7-a857-9d804b5ec054").unwrap();
166        let res = insert(
167            tx.as_mut(),
168            PKeyPolicy::Fixed(id_1),
169            "test registrar",
170            "123456789-123456",
171        )
172        .await;
173        assert!(res.is_ok(), "Expected insertion to succeed.");
174
175        let id_2 = Uuid::parse_str("d06abb84-0cad-4372-ad2a-7f87d3c1e420").unwrap();
176        let res = insert(
177            tx.as_mut(),
178            PKeyPolicy::Fixed(id_2),
179            "test registrar 2",
180            "123456789-123456",
181        )
182        .await;
183        assert!(
184            res.is_err(),
185            "Expected insertion to fail with duplicate secret key."
186        );
187    }
188}