diff --git a/backend/src/error.rs b/backend/src/error.rs index 689780c..0c272bb 100644 --- a/backend/src/error.rs +++ b/backend/src/error.rs @@ -12,6 +12,7 @@ pub type AppResult = Result; pub struct AppError { status: StatusCode, message: String, + code: Option, } impl AppError { @@ -19,6 +20,7 @@ impl AppError { Self { status, message: message.into(), + code: None, } } @@ -26,6 +28,10 @@ impl AppError { Self::new(StatusCode::BAD_REQUEST, message) } + pub fn conflict(message: impl Into) -> Self { + Self::new(StatusCode::CONFLICT, message) + } + pub fn unauthorized() -> Self { Self::new(StatusCode::UNAUTHORIZED, "unauthorized") } @@ -37,6 +43,11 @@ impl AppError { pub fn internal(error: E) -> Self { Self::new(StatusCode::INTERNAL_SERVER_ERROR, error.to_string()) } + + pub fn with_code(mut self, code: impl Into) -> Self { + self.code = Some(code.into()); + self + } } impl IntoResponse for AppError { @@ -44,6 +55,7 @@ impl IntoResponse for AppError { let status = self.status; let body = Json(ErrorResponse { error: self.message, + code: self.code, }); (status, body).into_response() } @@ -52,6 +64,8 @@ impl IntoResponse for AppError { #[derive(Serialize)] struct ErrorResponse { error: String, + #[serde(skip_serializing_if = "Option::is_none")] + code: Option, } impl From for AppError { diff --git a/backend/src/routes/documents.rs b/backend/src/routes/documents.rs index 2ca83cc..5eac634 100644 --- a/backend/src/routes/documents.rs +++ b/backend/src/routes/documents.rs @@ -1151,9 +1151,10 @@ pub async fn update_document( match update_result.execute(&mut conn) { Ok(_) => {} Err(diesel::result::Error::DatabaseError(DatabaseErrorKind::UniqueViolation, _)) => { - return Err(AppError::bad_request( + return Err(AppError::conflict( "another document in this folder already uses that filename", - )); + ) + .with_code("duplicate_filename")); } Err(err) => return Err(AppError::from(err)), } @@ -1822,7 +1823,7 @@ async fn process_upload( let (document, version) = { let mut conn = state.db_for_tenant(tenant_id)?; - conn.transaction(|conn| { + let transaction_result = conn.transaction(|conn| { let new_document = NewDocument { id: doc_id, filename: stored_filename.clone(), @@ -1859,7 +1860,18 @@ async fn process_upload( let version: DocumentVersion = document_versions::table.find(version_id).first(conn)?; Ok::<_, diesel::result::Error>((document, version)) - })? + }); + + match transaction_result { + Ok(result) => result, + Err(diesel::result::Error::DatabaseError(DatabaseErrorKind::UniqueViolation, _)) => { + return Err(AppError::conflict( + "another document in this folder already uses that filename", + ) + .with_code("duplicate_filename")) + } + Err(err) => return Err(AppError::from(err)), + } }; let detail = {