Skip to main content

docbox_database/models/
tenant.rs

1use crate::{DbExecutor, DbResult};
2use serde::{Deserialize, Serialize};
3use sqlx::prelude::FromRow;
4use uuid::Uuid;
5
6pub type TenantId = Uuid;
7
8use crate::utils::update_if_some;
9
10#[derive(Debug, Clone, FromRow, Serialize, PartialEq, Eq)]
11pub struct Tenant {
12    /// Unique ID for the tenant
13    pub id: TenantId,
14    /// Name for the tenant
15    pub name: String,
16    /// Name of the tenant database
17    pub db_name: String,
18    /// Name for the AWS secret used for the database user if
19    /// using secret based authentication
20    pub db_secret_name: Option<String>,
21    /// Name for the database user username if using IAM based
22    /// authentication
23    #[sqlx(default)]
24    pub db_iam_user_name: Option<String>,
25    /// Name of the tenant s3 bucket
26    pub s3_name: String,
27    /// Name of the tenant search index
28    pub os_index_name: String,
29    /// Environment for the tenant
30    pub env: String,
31    /// Optional event queue (SQS) to send docbox events to
32    pub event_queue_url: Option<String>,
33}
34
35/// Structure for fields required when creating a
36/// tenant within the database
37pub struct CreateTenant {
38    pub id: TenantId,
39    pub name: String,
40    pub db_name: String,
41    pub db_iam_user_name: Option<String>,
42    pub db_secret_name: Option<String>,
43    pub s3_name: String,
44    pub os_index_name: String,
45    pub event_queue_url: Option<String>,
46    pub env: String,
47}
48
49/// Bulk update for tenant fields
50#[derive(Debug, Default, Clone, Serialize, Deserialize)]
51#[serde(default)]
52pub struct UpdateTenant {
53    pub id: Option<TenantId>,
54    pub name: Option<String>,
55    pub db_name: Option<String>,
56    pub db_secret_name: Option<Option<String>>,
57    pub db_iam_user_name: Option<Option<String>>,
58    pub s3_name: Option<String>,
59    pub os_index_name: Option<String>,
60    pub env: Option<String>,
61    pub event_queue_url: Option<Option<String>>,
62}
63
64impl Tenant {
65    /// Create a new tenant
66    pub async fn create(db: impl DbExecutor<'_>, create: CreateTenant) -> DbResult<Tenant> {
67        sqlx::query(
68            r#"
69            INSERT INTO "docbox_tenants" (
70                "id",
71                "name",
72                "db_name",
73                "db_iam_user_name",
74                "db_secret_name",
75                "s3_name",
76                "os_index_name",
77                "env",
78                "event_queue_url"
79            )
80            VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
81        "#,
82        )
83        .bind(create.id)
84        .bind(create.name.as_str())
85        .bind(create.db_name.as_str())
86        .bind(create.db_iam_user_name.clone())
87        .bind(create.db_secret_name.clone())
88        .bind(create.s3_name.as_str())
89        .bind(create.os_index_name.as_str())
90        .bind(create.env.as_str())
91        .bind(create.event_queue_url.as_ref())
92        .execute(db)
93        .await?;
94
95        Ok(Tenant {
96            id: create.id,
97            name: create.name,
98            db_name: create.db_name,
99            db_iam_user_name: create.db_iam_user_name,
100            db_secret_name: create.db_secret_name,
101            s3_name: create.s3_name,
102            os_index_name: create.os_index_name,
103            env: create.env,
104            event_queue_url: create.event_queue_url,
105        })
106    }
107
108    /// Update the "db_iam_user_name" property of the tenant
109    pub async fn update(
110        &mut self,
111        db: impl DbExecutor<'_>,
112        UpdateTenant {
113            id,
114            name,
115            db_name,
116            db_secret_name,
117            db_iam_user_name,
118            s3_name,
119            os_index_name,
120            env,
121            event_queue_url,
122        }: UpdateTenant,
123    ) -> DbResult<()> {
124        sqlx::query(
125            r#"
126            UPDATE "docbox_tenants"
127            SET
128                "id" = COALESCE($3, "id"),
129                "name" = COALESCE($4, "name"),
130                "db_name" = COALESCE($5, "db_name"),
131                "db_secret_name" = COALESCE($6, "db_secret_name"),
132                "db_iam_user_name" = COALESCE($7, "db_iam_user_name"),
133                "s3_name" = COALESCE($8, "s3_name"),
134                "os_index_name" = COALESCE($9, "os_index_name"),
135                "env" = COALESCE($10, "env"),
136                "event_queue_url" = COALESCE($11, "event_queue_url")
137            WHERE "id" = $1 AND "env" = $2
138            "#,
139        )
140        //
141        .bind(self.id)
142        .bind(self.env.clone())
143        //
144        .bind(id)
145        .bind(name.clone())
146        .bind(db_name.clone())
147        .bind(db_secret_name.clone())
148        .bind(db_iam_user_name.clone())
149        .bind(s3_name.clone())
150        .bind(os_index_name.clone())
151        .bind(env.clone())
152        .bind(event_queue_url.clone())
153        .fetch_optional(db)
154        .await?;
155
156        update_if_some!(
157            self,
158            id,
159            name,
160            db_name,
161            db_secret_name,
162            db_iam_user_name,
163            s3_name,
164            os_index_name,
165            env,
166            event_queue_url,
167        );
168
169        Ok(())
170    }
171
172    /// Find a tenant by `id` within a specific `env`
173    pub async fn find_by_id(
174        db: impl DbExecutor<'_>,
175        id: TenantId,
176        env: &str,
177    ) -> DbResult<Option<Tenant>> {
178        sqlx::query_as(r#"SELECT * FROM "docbox_tenants" WHERE "id" = $1 AND "env" = $2"#)
179            .bind(id)
180            .bind(env)
181            .fetch_optional(db)
182            .await
183    }
184
185    /// Find a tenant using its S3 bucket
186    pub async fn find_by_bucket(db: impl DbExecutor<'_>, bucket: &str) -> DbResult<Option<Tenant>> {
187        sqlx::query_as(r#"SELECT * FROM "docbox_tenants" WHERE "s3_name" = $1"#)
188            .bind(bucket)
189            .fetch_optional(db)
190            .await
191    }
192
193    /// Finds all tenants for the specified environment
194    pub async fn find_by_env(db: impl DbExecutor<'_>, env: &str) -> DbResult<Vec<Tenant>> {
195        sqlx::query_as(r#"SELECT * FROM "docbox_tenants" WHERE "env" = $1 ORDER BY "name""#)
196            .bind(env)
197            .fetch_all(db)
198            .await
199    }
200
201    /// Finds all tenants
202    pub async fn all(db: impl DbExecutor<'_>) -> DbResult<Vec<Tenant>> {
203        sqlx::query_as(r#"SELECT * FROM "docbox_tenants" ORDER BY "name""#)
204            .fetch_all(db)
205            .await
206    }
207
208    /// Deletes the tenant
209    pub async fn delete(self, db: impl DbExecutor<'_>) -> DbResult<()> {
210        sqlx::query(r#"DELETE FROM "docbox_tenants" WHERE "id" = $1 AND "env" = $2"#)
211            .bind(self.id)
212            .bind(&self.env)
213            .execute(db)
214            .await?;
215        Ok(())
216    }
217}