Skip to main content

haste_repository/pg/
scope.rs

1use crate::{
2    admin::ProjectModelAdmin,
3    pg::{PGConnection, StoreError},
4    types::scope::{CreateScope, Scope, ScopeKey, ScopeSearchClaims, UpdateScope},
5};
6use haste_fhir_operation_error::OperationOutcomeError;
7use haste_jwt::{ProjectId, TenantId};
8use sqlx::{PgExecutor, QueryBuilder};
9
10async fn create_scope<'a, 'e, E>(
11    executor: E,
12    tenant: &'a TenantId,
13    project: &'a ProjectId,
14    scope: CreateScope,
15) -> Result<Scope, OperationOutcomeError>
16where
17    E: PgExecutor<'e>,
18{
19    let scope = sqlx::query_as::<_, Scope>(
20        r"
21            INSERT INTO authorization_scopes(tenant, project, client, user_, scope)
22            VALUES ($1, $2, $3, $4, $5)
23            ON CONFLICT (tenant, project, client, user_)
24            DO UPDATE SET scope = $5
25            RETURNING client, user_, scope, created_at
26        ",
27    )
28    .bind(tenant.as_ref())
29    .bind(project.as_ref())
30    .bind(scope.client.as_ref())
31    .bind(scope.user_.as_ref())
32    .bind(scope.scope)
33    .fetch_one(executor)
34    .await
35    .map_err(StoreError::SQLXError)?;
36
37    Ok(scope)
38}
39
40async fn update_scope<'a, 'e, E>(
41    executor: E,
42    tenant: &'a TenantId,
43    project: &'a ProjectId,
44    model: UpdateScope,
45) -> Result<Scope, OperationOutcomeError>
46where
47    E: PgExecutor<'e>,
48{
49    let mut query_builder = QueryBuilder::new(
50        r"
51            UPDATE authorization_scopes SET
52        ",
53    );
54
55    let mut set_statements = query_builder.separated(", ");
56
57    set_statements
58        .push(" scope = ")
59        .push_bind_unseparated(model.scope);
60
61    query_builder.push(" WHERE ");
62
63    let mut where_statements = query_builder.separated(" AND ");
64    where_statements
65        .push(" tenant = ")
66        .push_bind_unseparated(tenant.as_ref())
67        .push(" project = ")
68        .push_bind_unseparated(project.as_ref())
69        .push(" client = ")
70        .push_bind_unseparated(model.client.as_ref())
71        .push(" user_ = ")
72        .push_bind_unseparated(model.user_.as_ref());
73
74    query_builder.push(r" RETURNING client, user_, scope, created_at");
75
76    let query = query_builder.build_query_as::<Scope>();
77
78    let scope = query
79        .fetch_one(executor)
80        .await
81        .map_err(StoreError::SQLXError)?;
82
83    Ok(scope)
84}
85
86async fn read_scope<'a, 'e, E>(
87    executor: E,
88    tenant: &'a TenantId,
89    project: &'a ProjectId,
90    id: &'a ScopeKey,
91) -> Result<Option<Scope>, OperationOutcomeError>
92where
93    E: PgExecutor<'e>,
94{
95    let scope = sqlx::query_as::<_, Scope>(
96        r"
97            SELECT user_, client, scope, created_at
98            FROM authorization_scopes
99            WHERE tenant = $1 AND project = $2 AND client = $3 AND user_ = $4
100        ",
101    )
102    .bind(tenant.as_ref())
103    .bind(project.as_ref())
104    .bind(String::from(id.0.clone()))
105    .bind(String::from(id.1.clone()))
106    .fetch_optional(executor)
107    .await
108    .map_err(StoreError::SQLXError)?;
109
110    Ok(scope)
111}
112
113async fn delete_scope<'a, 'e, E>(
114    executor: E,
115    tenant: &'a TenantId,
116    project: &'a ProjectId,
117    key: &'a ScopeKey,
118) -> Result<(), OperationOutcomeError>
119where
120    E: PgExecutor<'e>,
121{
122    sqlx::query(
123        r"
124            DELETE FROM authorization_scopes
125            WHERE tenant = $1 AND project = $2 AND client = $3 AND user_ = $4
126        ",
127    )
128    .bind(tenant.as_ref())
129    .bind(project.as_ref())
130    .bind(key.0.as_ref())
131    .bind(key.1.as_ref())
132    .execute(executor)
133    .await
134    .map_err(StoreError::SQLXError)?;
135
136    Ok(())
137}
138
139async fn search_scopes<'a, 'e, E>(
140    executor: E,
141    tenant: &'a TenantId,
142    project: &'a ProjectId,
143    clauses: &'a ScopeSearchClaims,
144) -> Result<Vec<Scope>, OperationOutcomeError>
145where
146    E: PgExecutor<'e>,
147{
148    let mut query_builder: QueryBuilder<sqlx::Postgres> = QueryBuilder::new(
149        r"SELECT user_, client, scope, created_at FROM authorization_scopes WHERE ",
150    );
151
152    let mut seperator = query_builder.separated(" AND ");
153    seperator
154        .push(" tenant = ")
155        .push_bind_unseparated(tenant.as_ref())
156        .push(" project = ")
157        .push_bind_unseparated(project.as_ref());
158
159    if let Some(user_id) = clauses.user_.as_ref() {
160        seperator
161            .push(" user_ = ")
162            .push_bind_unseparated(user_id.as_ref());
163    }
164
165    if let Some(client) = clauses.client.as_ref() {
166        seperator
167            .push(" client = ")
168            .push_bind_unseparated(client.as_ref());
169    }
170
171    let query = query_builder.build_query_as::<Scope>();
172
173    let scopes: Vec<Scope> = query.fetch_all(executor).await.map_err(StoreError::from)?;
174
175    Ok(scopes)
176}
177
178impl ProjectModelAdmin<CreateScope, Scope, ScopeSearchClaims, UpdateScope, ScopeKey>
179    for PGConnection
180{
181    async fn create(
182        &self,
183        tenant: &TenantId,
184        project: &ProjectId,
185        new_scope: CreateScope,
186    ) -> Result<Scope, OperationOutcomeError> {
187        match self {
188            PGConnection::Pool(pool, _) => create_scope(pool, tenant, project, new_scope).await,
189            PGConnection::Transaction(tx, _) => {
190                let mut tx = tx.lock().await;
191                create_scope(&mut **tx, tenant, project, new_scope).await
192            }
193        }
194    }
195
196    async fn read(
197        &self,
198        tenant: &TenantId,
199        project: &ProjectId,
200        key: &ScopeKey,
201    ) -> Result<Option<Scope>, OperationOutcomeError> {
202        match self {
203            PGConnection::Pool(pool, _) => read_scope(pool, tenant, project, key).await,
204            PGConnection::Transaction(tx, _) => {
205                let mut tx = tx.lock().await;
206                read_scope(&mut **tx, tenant, project, key).await
207            }
208        }
209    }
210
211    async fn update(
212        &self,
213        tenant: &TenantId,
214        project: &ProjectId,
215        model: UpdateScope,
216    ) -> Result<Scope, OperationOutcomeError> {
217        match self {
218            PGConnection::Pool(pool, _) => update_scope(pool, tenant, project, model).await,
219            PGConnection::Transaction(tx, _) => {
220                let mut tx = tx.lock().await;
221                update_scope(&mut **tx, tenant, project, model).await
222            }
223        }
224    }
225
226    async fn delete(
227        &self,
228        tenant: &TenantId,
229        project: &ProjectId,
230        key: &ScopeKey,
231    ) -> Result<(), OperationOutcomeError> {
232        match self {
233            PGConnection::Pool(pool, _) => delete_scope(pool, tenant, project, key).await,
234            PGConnection::Transaction(tx, _) => {
235                let mut tx = tx.lock().await;
236                delete_scope(&mut **tx, tenant, project, key).await
237            }
238        }
239    }
240
241    async fn search(
242        &self,
243        tenant: &TenantId,
244        project: &ProjectId,
245        clauses: &ScopeSearchClaims,
246    ) -> Result<Vec<Scope>, OperationOutcomeError> {
247        match self {
248            PGConnection::Pool(pool, _) => search_scopes(pool, tenant, project, clauses).await,
249            PGConnection::Transaction(tx, _) => {
250                let mut tx = tx.lock().await;
251                search_scopes(&mut **tx, tenant, project, clauses).await
252            }
253        }
254    }
255}