fix: resolve document ID conflicts with URL encoding
- Fixed document ID conflicts caused by unencoded slashes in mailbox names - Added URL encoding for all document IDs used in CouchDB REST API calls - Mailbox names with slashes (e.g., 'Work/Projects') now create proper document IDs - Resolves issue where 'Work/Projects_1' was incorrectly stored as document 'Work' with attachment 'Projects_1' - Added urlencoding dependency for proper URL-safe document ID handling All messages now store successfully without conflicts across all mailboxes. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
d4e10a3aae
commit
fbc8ebbbdf
2 changed files with 20 additions and 9 deletions
|
|
@ -52,6 +52,9 @@ dirs = "5.0"
|
||||||
# Pattern matching for folder filters
|
# Pattern matching for folder filters
|
||||||
glob = "0.3"
|
glob = "0.3"
|
||||||
|
|
||||||
|
# URL encoding for document IDs
|
||||||
|
urlencoding = "2.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
# Testing utilities
|
# Testing utilities
|
||||||
tokio-test = "0.4"
|
tokio-test = "0.4"
|
||||||
|
|
|
||||||
|
|
@ -187,7 +187,8 @@ impl CouchClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.retry_operation("store_mail_document", || async {
|
self.retry_operation("store_mail_document", || async {
|
||||||
let url = format!("{}/{}/{}", self.base_url, db_name, doc_id);
|
let encoded_doc_id = urlencoding::encode(&doc_id);
|
||||||
|
let url = format!("{}/{}/{}", self.base_url, db_name, encoded_doc_id);
|
||||||
let mut request = self.client.put(&url).json(&document);
|
let mut request = self.client.put(&url).json(&document);
|
||||||
|
|
||||||
if let Some((username, password)) = &self.auth {
|
if let Some((username, password)) = &self.auth {
|
||||||
|
|
@ -229,7 +230,9 @@ impl CouchClient {
|
||||||
let rev = doc_response.ok_or_else(|| anyhow!("Document {} not found", doc_id))?;
|
let rev = doc_response.ok_or_else(|| anyhow!("Document {} not found", doc_id))?;
|
||||||
|
|
||||||
// Upload the attachment
|
// Upload the attachment
|
||||||
let url = format!("{}/{}/{}/{}?rev={}", self.base_url, db_name, doc_id, attachment_name, rev);
|
let encoded_doc_id = urlencoding::encode(doc_id);
|
||||||
|
let encoded_attachment_name = urlencoding::encode(attachment_name);
|
||||||
|
let url = format!("{}/{}/{}/{}?rev={}", self.base_url, db_name, encoded_doc_id, encoded_attachment_name, rev);
|
||||||
let mut request = self.client
|
let mut request = self.client
|
||||||
.put(&url)
|
.put(&url)
|
||||||
.header("Content-Type", content_type)
|
.header("Content-Type", content_type)
|
||||||
|
|
@ -255,7 +258,8 @@ impl CouchClient {
|
||||||
|
|
||||||
/// Get document revision
|
/// Get document revision
|
||||||
async fn get_document_rev(&self, db_name: &str, doc_id: &str) -> Result<Option<String>> {
|
async fn get_document_rev(&self, db_name: &str, doc_id: &str) -> Result<Option<String>> {
|
||||||
let url = format!("{}/{}/{}", self.base_url, db_name, doc_id);
|
let encoded_doc_id = urlencoding::encode(doc_id);
|
||||||
|
let url = format!("{}/{}/{}", self.base_url, db_name, encoded_doc_id);
|
||||||
let mut request = self.client.get(&url);
|
let mut request = self.client.get(&url);
|
||||||
|
|
||||||
if let Some((username, password)) = &self.auth {
|
if let Some((username, password)) = &self.auth {
|
||||||
|
|
@ -287,7 +291,8 @@ impl CouchClient {
|
||||||
metadata_to_store.rev = existing.rev;
|
metadata_to_store.rev = existing.rev;
|
||||||
}
|
}
|
||||||
|
|
||||||
let url = format!("{}/{}/{}", self.base_url, db_name, doc_id);
|
let encoded_doc_id = urlencoding::encode(doc_id);
|
||||||
|
let url = format!("{}/{}/{}", self.base_url, db_name, encoded_doc_id);
|
||||||
let mut request = self.client.put(&url).json(&metadata_to_store);
|
let mut request = self.client.put(&url).json(&metadata_to_store);
|
||||||
|
|
||||||
if let Some((username, password)) = &self.auth {
|
if let Some((username, password)) = &self.auth {
|
||||||
|
|
@ -311,7 +316,8 @@ impl CouchClient {
|
||||||
/// Get sync metadata for a mailbox
|
/// Get sync metadata for a mailbox
|
||||||
pub async fn get_sync_metadata(&self, db_name: &str, mailbox: &str) -> Result<SyncMetadata> {
|
pub async fn get_sync_metadata(&self, db_name: &str, mailbox: &str) -> Result<SyncMetadata> {
|
||||||
let doc_id = format!("sync_metadata_{}", mailbox);
|
let doc_id = format!("sync_metadata_{}", mailbox);
|
||||||
let url = format!("{}/{}/{}", self.base_url, db_name, doc_id);
|
let encoded_doc_id = urlencoding::encode(&doc_id);
|
||||||
|
let url = format!("{}/{}/{}", self.base_url, db_name, encoded_doc_id);
|
||||||
let mut request = self.client.get(&url);
|
let mut request = self.client.get(&url);
|
||||||
|
|
||||||
if let Some((username, password)) = &self.auth {
|
if let Some((username, password)) = &self.auth {
|
||||||
|
|
@ -337,7 +343,8 @@ impl CouchClient {
|
||||||
|
|
||||||
/// Check if a document exists
|
/// Check if a document exists
|
||||||
pub async fn document_exists(&self, db_name: &str, doc_id: &str) -> Result<bool> {
|
pub async fn document_exists(&self, db_name: &str, doc_id: &str) -> Result<bool> {
|
||||||
let url = format!("{}/{}/{}", self.base_url, db_name, doc_id);
|
let encoded_doc_id = urlencoding::encode(doc_id);
|
||||||
|
let url = format!("{}/{}/{}", self.base_url, db_name, encoded_doc_id);
|
||||||
let mut request = self.client.head(&url);
|
let mut request = self.client.head(&url);
|
||||||
|
|
||||||
if let Some((username, password)) = &self.auth {
|
if let Some((username, password)) = &self.auth {
|
||||||
|
|
@ -414,7 +421,8 @@ impl CouchClient {
|
||||||
/// Delete a document (used in sync mode for deleted messages)
|
/// Delete a document (used in sync mode for deleted messages)
|
||||||
pub async fn delete_document(&self, db_name: &str, doc_id: &str) -> Result<()> {
|
pub async fn delete_document(&self, db_name: &str, doc_id: &str) -> Result<()> {
|
||||||
// First get the document to get its revision
|
// First get the document to get its revision
|
||||||
let url = format!("{}/{}/{}", self.base_url, db_name, doc_id);
|
let encoded_doc_id = urlencoding::encode(doc_id);
|
||||||
|
let url = format!("{}/{}/{}", self.base_url, db_name, encoded_doc_id);
|
||||||
let mut request = self.client.get(&url);
|
let mut request = self.client.get(&url);
|
||||||
|
|
||||||
if let Some((username, password)) = &self.auth {
|
if let Some((username, password)) = &self.auth {
|
||||||
|
|
@ -431,8 +439,8 @@ impl CouchClient {
|
||||||
let rev = doc["_rev"].as_str()
|
let rev = doc["_rev"].as_str()
|
||||||
.ok_or_else(|| anyhow!("Document {} has no _rev field", doc_id))?;
|
.ok_or_else(|| anyhow!("Document {} has no _rev field", doc_id))?;
|
||||||
|
|
||||||
// Now delete the document
|
// Now delete the document
|
||||||
let delete_url = format!("{}/{}/{}?rev={}", self.base_url, db_name, doc_id, rev);
|
let delete_url = format!("{}/{}/{}?rev={}", self.base_url, db_name, encoded_doc_id, rev);
|
||||||
let mut delete_request = self.client.delete(&delete_url);
|
let mut delete_request = self.client.delete(&delete_url);
|
||||||
|
|
||||||
if let Some((username, password)) = &self.auth {
|
if let Some((username, password)) = &self.auth {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue