docbox_core/document_box/
create_document_box.rs

1use crate::events::{TenantEventMessage, TenantEventPublisher};
2use docbox_database::{
3    DbErr, DbPool, DbResult, DbTransaction,
4    models::{
5        document_box::{DocumentBox, DocumentBoxScopeRaw},
6        folder::{CreateFolder, Folder},
7        user::UserId,
8    },
9};
10use std::ops::DerefMut;
11use thiserror::Error;
12
13const ROOT_FOLDER_NAME: &str = "Root";
14
15#[derive(Debug, Error)]
16pub enum CreateDocumentBoxError {
17    #[error("document box with matching scope already exists")]
18    ScopeAlreadyExists,
19
20    /// Database error occurred
21    #[error(transparent)]
22    Database(#[from] DbErr),
23}
24
25#[derive(Debug)]
26pub struct CreateDocumentBox {
27    pub scope: String,
28    pub created_by: Option<String>,
29}
30
31/// Create a new document box
32pub async fn create_document_box(
33    db: &DbPool,
34    events: &TenantEventPublisher,
35    create: CreateDocumentBox,
36) -> Result<(DocumentBox, Folder), CreateDocumentBoxError> {
37    // Enter a database transaction
38    let mut transaction = db.begin().await?;
39
40    let document_box: DocumentBox =
41        create_document_box_entry(&mut transaction, create.scope).await?;
42    let root = create_root_folder(
43        &mut transaction,
44        document_box.scope.clone(),
45        create.created_by,
46    )
47    .await?;
48
49    transaction.commit().await?;
50
51    // Publish an event
52    events.publish_event(TenantEventMessage::DocumentBoxCreated(document_box.clone()));
53
54    Ok((document_box, root))
55}
56
57/// Create the database entry for the document box itself
58async fn create_document_box_entry(
59    db: &mut DbTransaction<'_>,
60    scope: DocumentBoxScopeRaw,
61) -> Result<DocumentBox, CreateDocumentBoxError> {
62    DocumentBox::create(db.deref_mut(), scope)
63        .await
64        .map_err(|cause| {
65            if let Some(db_err) = cause.as_database_error() {
66                // Handle attempts at a duplicate scope creation
67                if db_err.is_unique_violation() {
68                    return CreateDocumentBoxError::ScopeAlreadyExists;
69                }
70            }
71
72            tracing::error!(?cause, "failed to create document box");
73            CreateDocumentBoxError::from(cause)
74        })
75}
76
77/// Create the "root" folder within the document box that all
78/// the contents will be stored within.
79async fn create_root_folder(
80    db: &mut DbTransaction<'_>,
81    document_box: DocumentBoxScopeRaw,
82    created_by: Option<UserId>,
83) -> DbResult<Folder> {
84    Folder::create(
85        db.deref_mut(),
86        CreateFolder {
87            name: ROOT_FOLDER_NAME.to_string(),
88            document_box,
89            folder_id: None,
90            created_by,
91        },
92    )
93    .await
94    .inspect_err(|error| tracing::error!(?error, "failed to create document box root folder"))
95}