"""Client for Nextcloud Notes app operations.""" from typing import Dict, List, Any, Optional import logging from .base_client import BaseNextcloudClient logger = logging.getLogger(__name__) class NotesClient(BaseNextcloudClient): """Client for Nextcloud Notes app operations.""" async def get_settings(self) -> Dict[str, Any]: """Get Notes app settings.""" response = await self._make_request("GET", "/apps/notes/api/v1/settings") return response.json() async def get_all_notes(self) -> List[Dict[str, Any]]: """Get all notes.""" response = await self._make_request("GET", "/apps/notes/api/v1/notes") return response.json() async def get_note(self, note_id: int) -> Dict[str, Any]: """Get a specific note by ID.""" response = await self._make_request("GET", f"/apps/notes/api/v1/notes/{note_id}") return response.json() async def create_note( self, title: Optional[str] = None, content: Optional[str] = None, category: Optional[str] = None, ) -> Dict[str, Any]: """Create a new note.""" body = {} if title: body["title"] = title if content: body["content"] = content if category: body["category"] = category response = await self._make_request("POST", "/apps/notes/api/v1/notes", json=body) return response.json() async def update( self, note_id: int, etag: str, title: Optional[str] = None, content: Optional[str] = None, category: Optional[str] = None, ) -> Dict[str, Any]: """Update an existing note.""" # Get current note details to check for category change old_note = None try: if category is not None: old_note = await self.get_note(note_id) old_category = old_note.get("category", "") logger.info(f"Current category for note {note_id}: '{old_category}'") except Exception as e: logger.warning(f"Could not fetch current note {note_id} details before update: {e}") old_note = None # Prepare update body body = {} if title: body["title"] = title if content: body["content"] = content if category: body["category"] = category logger.info(f"Attempting to update note {note_id} with etag {etag}. Body: {body}") response = await self._make_request( "PUT", f"/apps/notes/api/v1/notes/{note_id}", json=body, headers={"If-Match": f'"{etag}"'} ) logger.info(f"Update response for note {note_id}: Status {response.status_code}") updated_note = response.json() # Check for category change and cleanup old attachment directory if needed if old_note and category is not None and old_note.get("category", "") != category: logger.info(f"Category changed from '{old_note.get('category', '')}' to '{category}' - cleaning up old attachment directory") try: # Import here to avoid circular imports from .webdav_client import WebDAVClient webdav_client = WebDAVClient(self._client, self.username) await webdav_client.cleanup_old_attachment_directory( note_id=note_id, old_category=old_note.get("category", "") ) except Exception as e: logger.error(f"Error cleaning up old attachment directory for note {note_id}: {e}") return updated_note async def delete_note(self, note_id: int) -> Dict[str, Any]: """Delete a note and its attachments.""" # Fetch note details first to get category for cleanup try: note_details = await self.get_note(note_id) category = note_details.get("category", "") # Determine potential categories for cleanup potential_categories = [] if category: potential_categories.append(category) if category != "": potential_categories.append("") # Empty category logger.info(f"Note {note_id} has category: '{category}', will check attachment directories in: {potential_categories}") except Exception as e: logger.warning(f"Could not fetch note {note_id} details before deletion: {e}") potential_categories = ["", "Unknown"] # Try common categories # Delete the note via API logger.info(f"Deleting note {note_id} via API") response = await self._make_request("DELETE", f"/apps/notes/api/v1/notes/{note_id}") logger.info(f"Note {note_id} deleted successfully via API") json_response = response.json() # Clean up attachment directories try: from .webdav_client import WebDAVClient webdav_client = WebDAVClient(self._client, self.username) for cat in potential_categories: try: await webdav_client.cleanup_note_attachments(note_id, cat) except Exception as e: logger.warning(f"Failed to cleanup attachments for category '{cat}': {e}") except Exception as e: logger.warning(f"Error during attachment cleanup: {e}") return json_response async def append_content(self, note_id: int, content: str) -> Dict[str, Any]: """Append content to an existing note with a separator.""" logger.info(f"Appending content to note {note_id}") # Get current note current_note = await self.get_note(note_id) # Use fixed separator for consistency separator = "\n---\n" # Combine content existing_content = current_note.get("content", "") if existing_content: new_content = existing_content + separator + content else: new_content = content # No separator needed for empty notes logger.info(f"Combining existing content ({len(existing_content)} chars) with new content ({len(content)} chars)") # Update with combined content return await self.update( note_id=note_id, etag=current_note["etag"], content=new_content, title=None, # Keep existing title category=None, # Keep existing category )