diff --git a/backend/sync_server/src/server/create_document.rs b/backend/sync_server/src/server/create_document.rs index e432cb5a..4d17effc 100644 --- a/backend/sync_server/src/server/create_document.rs +++ b/backend/sync_server/src/server/create_document.rs @@ -16,7 +16,7 @@ use super::{ requests::{CreateDocumentVersion, CreateDocumentVersionMultipart}, }; use crate::{ - database::models::{DocumentVersionWithoutContent, StoredDocumentVersion, VaultId}, + database::models::{DocumentId, DocumentVersionWithoutContent, StoredDocumentVersion, VaultId}, errors::{SyncServerError, client_error, server_error}, utils::sanitize_path, }; @@ -43,6 +43,7 @@ pub async fn create_document_multipart( auth_header, state, vault_id, + request.document_id, request.relative_path, request.content.contents.to_vec(), ) @@ -67,6 +68,7 @@ pub async fn create_document_json( auth_header, state, vault_id, + request.document_id, request.relative_path, content_bytes, ) @@ -77,6 +79,7 @@ async fn internal_create_document( auth_header: Authorization, state: AppState, vault_id: VaultId, + document_id: Option, relative_path: String, content: Vec, ) -> Result, SyncServerError> { @@ -88,6 +91,25 @@ async fn internal_create_document( .await .map_err(server_error)?; + let document_id = match document_id { + Some(document_id) => { + let existing_version = state + .database + .get_latest_document(&vault_id, &document_id, Some(&mut transaction)) + .await + .map_err(server_error)?; + + if existing_version.is_some() { + return Err(client_error(anyhow::anyhow!( + "Document with the same ID already exists" + ))); + } + + document_id + } + None => uuid::Uuid::new_v4(), + }; + let last_update_id = state .database .get_max_update_id_in_vault(&vault_id, Some(&mut transaction)) @@ -99,7 +121,7 @@ async fn internal_create_document( let new_version = StoredDocumentVersion { vault_id, vault_update_id: last_update_id + 1, - document_id: uuid::Uuid::new_v4(), + document_id, relative_path: sanitized_relative_path, content, updated_date: chrono::Utc::now(), diff --git a/backend/sync_server/src/server/requests.rs b/backend/sync_server/src/server/requests.rs index b55d1c47..3c888266 100644 --- a/backend/sync_server/src/server/requests.rs +++ b/backend/sync_server/src/server/requests.rs @@ -4,17 +4,23 @@ use axum_typed_multipart::TryFromMultipart; use schemars::JsonSchema; use serde::{self, Deserialize}; -use crate::database::models::VaultUpdateId; +use crate::database::models::{DocumentId, VaultUpdateId}; #[derive(Debug, Deserialize, JsonSchema)] #[serde(rename_all = "camelCase")] pub struct CreateDocumentVersion { + /// The client can decide the document id (if it wishes to) in order + /// to help with syncing. If the client does not provide a document id, + /// the server will generate one. If the client provides a document id + /// it must not already exist in the database. + pub document_id: Option, pub relative_path: String, pub content_base64: String, } #[derive(Debug, TryFromMultipart, JsonSchema)] pub struct CreateDocumentVersionMultipart { + pub document_id: Option, pub relative_path: String, #[form_data(limit = "unlimited")] pub content: FieldData, diff --git a/frontend/sync-client/src/services/types.ts b/frontend/sync-client/src/services/types.ts index eba3d9f0..642fd6c2 100644 --- a/frontend/sync-client/src/services/types.ts +++ b/frontend/sync-client/src/services/types.ts @@ -452,10 +452,14 @@ export interface components { Array_of_uint8: number[]; CreateDocumentVersion: { contentBase64: string; + /** Format: uuid */ + documentId?: string | null; relativePath: string; }; CreateDocumentVersionMultipart: { content: components["schemas"]["Array_of_uint8"]; + /** Format: uuid */ + document_id?: string | null; relative_path: string; }; DeleteDocumentVersion: {