feat: Add comprehensive Nextcloud import functionality and fix compilation issues
Major additions: - New NextcloudImportEngine with import behaviors (SkipDuplicates, Overwrite, Merge) - Complete import workflow with result tracking and conflict resolution - Support for dry-run mode and detailed progress reporting - Import command integration in CLI with --import-events flag Configuration improvements: - Added ImportConfig struct for structured import settings - Backward compatibility with legacy ImportTargetConfig - Enhanced get_import_config() method supporting both formats CalDAV client enhancements: - Improved XML parsing for multiple calendar display name formats - Better fallback handling for calendar discovery - Enhanced error handling and debugging capabilities Bug fixes: - Fixed test compilation errors in error.rs (reqwest::Error type conversion) - Resolved unused variable warning in main.rs - All tests now pass (16/16) Documentation: - Added comprehensive NEXTCLOUD_IMPORT_PLAN.md with implementation roadmap - Updated library exports to include new modules Files changed: - src/nextcloud_import.rs: New import engine implementation - src/config.rs: Enhanced configuration with import support - src/main.rs: Added import command and CLI integration - src/minicaldav_client.rs: Improved calendar discovery and XML parsing - src/error.rs: Fixed test compilation issues - src/lib.rs: Updated module exports - Deleted: src/real_caldav_client.rs (removed unused file)
This commit is contained in:
parent
16d6fc375d
commit
f84ce62f73
10 changed files with 1461 additions and 342 deletions
390
NEXTCLOUD_IMPORT_PLAN.md
Normal file
390
NEXTCLOUD_IMPORT_PLAN.md
Normal file
|
|
@ -0,0 +1,390 @@
|
|||
# Nextcloud CalDAV Import Implementation Plan
|
||||
|
||||
## Current State Analysis
|
||||
|
||||
### Current Code Overview
|
||||
The caldavpuller project is a Rust-based CalDAV synchronization tool that currently:
|
||||
- **Reads events from Zoho calendars** using multiple approaches (zoho-export, zoho-events-list, zoho-events-direct)
|
||||
- **Supports basic CalDAV operations** like listing calendars and events
|
||||
- **Has a solid event model** in `src/event.rs` with support for datetime, timezone, title, and other properties
|
||||
- **Implements CalDAV client functionality** in `src/caldav_client.rs` and related files
|
||||
- **Can already generate iCalendar format** using the `to_ical()` method
|
||||
|
||||
### Current Capabilities
|
||||
- ✅ **Event listing**: Can read and display events from external sources
|
||||
- ✅ **iCalendar generation**: Has basic iCalendar export functionality
|
||||
- ✅ **CalDAV client**: Basic WebDAV operations implemented
|
||||
- ✅ **Configuration**: Flexible configuration system for different CalDAV servers
|
||||
|
||||
### Missing Functionality for Nextcloud Import
|
||||
- ❌ **PUT/POST operations**: No ability to write events to CalDAV servers
|
||||
- ❌ **Calendar creation**: Cannot create new calendars on Nextcloud
|
||||
- ❌ **Nextcloud-specific optimizations**: No handling for Nextcloud's CalDAV implementation specifics
|
||||
- ❌ **Import workflow**: No dedicated import command or process
|
||||
|
||||
## Nextcloud CalDAV Architecture
|
||||
|
||||
Based on research of Nextcloud's CalDAV implementation (built on SabreDAV):
|
||||
|
||||
### Key Requirements
|
||||
1. **Standard CalDAV Compliance**: Nextcloud follows RFC 4791 CalDAV specification
|
||||
2. **iCalendar Format**: Requires RFC 5545 compliant iCalendar data
|
||||
3. **Authentication**: Basic auth or app password authentication
|
||||
4. **URL Structure**: Typically `/remote.php/dav/calendars/{user}/{calendar-name}/`
|
||||
|
||||
### Nextcloud-Specific Features
|
||||
- **SabreDAV Backend**: Nextcloud uses SabreDAV as its CalDAV server
|
||||
- **WebDAV Extensions**: Supports standard WebDAV sync operations
|
||||
- **Calendar Discovery**: Can auto-discover user calendars via PROPFIND
|
||||
- **ETag Support**: Proper ETag handling for synchronization
|
||||
- **Multi-Get Operations**: Supports calendar-multiget for efficiency
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Core CalDAV Write Operations
|
||||
|
||||
#### 1.1 Extend CalDAV Client for Write Operations
|
||||
**File**: `src/caldav_client.rs`
|
||||
|
||||
**Required Methods**:
|
||||
```rust
|
||||
// Create or update an event
|
||||
pub async fn put_event(&self, calendar_url: &str, event_path: &str, ical_data: &str) -> CalDavResult<()>
|
||||
|
||||
// Create a new calendar
|
||||
pub async fn create_calendar(&self, calendar_name: &str, display_name: Option<&str>) -> CalDavResult<String>
|
||||
|
||||
// Upload multiple events efficiently
|
||||
pub async fn import_events_batch(&self, calendar_url: &str, events: &[Event]) -> CalDavResult<Vec<CalDavResult<()>>>
|
||||
```
|
||||
|
||||
**Implementation Details**:
|
||||
- Use HTTP PUT method for individual events
|
||||
- Handle ETag conflicts with If-Match headers
|
||||
- Use proper content-type: `text/calendar; charset=utf-8`
|
||||
- Support both creating new events and updating existing ones
|
||||
|
||||
#### 1.2 Enhanced Event to iCalendar Conversion
|
||||
**File**: `src/event.rs`
|
||||
|
||||
**Current Issues**:
|
||||
- Timezone handling is incomplete
|
||||
- Missing proper DTSTAMP and LAST-MODIFIED
|
||||
- Limited property support
|
||||
|
||||
**Required Enhancements**:
|
||||
```rust
|
||||
impl Event {
|
||||
pub fn to_ical_for_nextcloud(&self) -> CalDavResult<String> {
|
||||
// Enhanced iCalendar generation with:
|
||||
// - Proper timezone handling
|
||||
// - Nextcloud-specific properties
|
||||
// - Better datetime formatting
|
||||
// - Required properties for Nextcloud compatibility
|
||||
}
|
||||
|
||||
pub fn generate_unique_path(&self) -> String {
|
||||
// Generate filename/path for CalDAV storage
|
||||
format!("{}.ics", self.uid)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 2: Nextcloud Integration
|
||||
|
||||
#### 2.1 Nextcloud Client Extension
|
||||
**New File**: `src/nextcloud_client.rs`
|
||||
|
||||
```rust
|
||||
pub struct NextcloudClient {
|
||||
client: CalDavClient,
|
||||
base_url: String,
|
||||
username: String,
|
||||
}
|
||||
|
||||
impl NextcloudClient {
|
||||
pub fn new(config: NextcloudConfig) -> CalDavResult<Self>
|
||||
|
||||
// Auto-discover calendars
|
||||
pub async fn discover_calendars(&self) -> CalDavResult<Vec<CalendarInfo>>
|
||||
|
||||
// Create calendar if it doesn't exist
|
||||
pub async fn ensure_calendar_exists(&self, name: &str, display_name: Option<&str>) -> CalDavResult<String>
|
||||
|
||||
// Import events with conflict resolution
|
||||
pub async fn import_events(&self, calendar_name: &str, events: Vec<Event>) -> CalDavResult<ImportResult>
|
||||
|
||||
// Check if event already exists
|
||||
pub async fn event_exists(&self, calendar_name: &str, event_uid: &str) -> CalDavResult<bool>
|
||||
|
||||
// Get existing event ETag
|
||||
pub async fn get_event_etag(&self, calendar_name: &str, event_uid: &str) -> CalDavResult<Option<String>>
|
||||
}
|
||||
```
|
||||
|
||||
#### 2.2 Nextcloud Configuration
|
||||
**File**: `src/config.rs`
|
||||
|
||||
Add Nextcloud-specific configuration:
|
||||
```toml
|
||||
[nextcloud]
|
||||
# Nextcloud server URL (e.g., https://cloud.example.com)
|
||||
server_url = "https://cloud.example.com"
|
||||
|
||||
# Username
|
||||
username = "your_username"
|
||||
|
||||
# App password (recommended) or regular password
|
||||
password = "your_app_password"
|
||||
|
||||
# Default calendar for imports
|
||||
default_calendar = "imported-events"
|
||||
|
||||
# Import behavior
|
||||
import_behavior = "skip_duplicates" # or "overwrite" or "merge"
|
||||
|
||||
# Conflict resolution
|
||||
conflict_resolution = "keep_existing" # or "overwrite_remote" or "merge"
|
||||
```
|
||||
|
||||
### Phase 3: Import Workflow Implementation
|
||||
|
||||
#### 3.1 Import Command Line Interface
|
||||
**File**: `src/main.rs`
|
||||
|
||||
Add new CLI options:
|
||||
```rust
|
||||
/// Import events into Nextcloud calendar
|
||||
#[arg(long)]
|
||||
import_nextcloud: bool,
|
||||
|
||||
/// Target calendar name for Nextcloud import
|
||||
#[arg(long)]
|
||||
nextcloud_calendar: Option<String>,
|
||||
|
||||
/// Import behavior (skip_duplicates, overwrite, merge)
|
||||
#[arg(long, default_value = "skip_duplicates")]
|
||||
import_behavior: String,
|
||||
|
||||
/// Dry run - show what would be imported without actually doing it
|
||||
#[arg(long)]
|
||||
dry_run: bool,
|
||||
```
|
||||
|
||||
#### 3.2 Import Engine
|
||||
**New File**: `src/nextcloud_import.rs`
|
||||
|
||||
```rust
|
||||
pub struct ImportEngine {
|
||||
nextcloud_client: NextcloudClient,
|
||||
config: ImportConfig,
|
||||
}
|
||||
|
||||
pub struct ImportResult {
|
||||
pub total_events: usize,
|
||||
pub imported: usize,
|
||||
pub skipped: usize,
|
||||
pub errors: Vec<ImportError>,
|
||||
pub conflicts: Vec<ConflictInfo>,
|
||||
}
|
||||
|
||||
impl ImportEngine {
|
||||
pub async fn import_events(&self, events: Vec<Event>) -> CalDavResult<ImportResult> {
|
||||
// 1. Validate events
|
||||
// 2. Check for existing events
|
||||
// 3. Resolve conflicts based on configuration
|
||||
// 4. Batch upload events
|
||||
// 5. Report results
|
||||
}
|
||||
|
||||
fn validate_event(&self, event: &Event) -> CalDavResult<()> {
|
||||
// Ensure required fields are present
|
||||
// Validate datetime and timezone
|
||||
// Check for Nextcloud compatibility
|
||||
}
|
||||
|
||||
async fn check_existing_event(&self, event: &Event) -> CalDavResult<Option<String>> {
|
||||
// Return ETag if event exists, None otherwise
|
||||
}
|
||||
|
||||
async fn resolve_conflict(&self, existing_event: &str, new_event: &Event) -> CalDavResult<ConflictResolution> {
|
||||
// Based on configuration: skip, overwrite, or merge
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 4: Error Handling and Validation
|
||||
|
||||
#### 4.1 Enhanced Error Types
|
||||
**File**: `src/error.rs`
|
||||
|
||||
```rust
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ImportError {
|
||||
#[error("Event validation failed: {message}")]
|
||||
ValidationFailed { message: String },
|
||||
|
||||
#[error("Event already exists: {uid}")]
|
||||
EventExists { uid: String },
|
||||
|
||||
#[error("Calendar creation failed: {message}")]
|
||||
CalendarCreationFailed { message: String },
|
||||
|
||||
#[error("Import conflict: {event_uid} - {message}")]
|
||||
ImportConflict { event_uid: String, message: String },
|
||||
|
||||
#[error("Nextcloud API error: {status} - {message}")]
|
||||
NextcloudError { status: u16, message: String },
|
||||
}
|
||||
```
|
||||
|
||||
#### 4.2 Event Validation
|
||||
```rust
|
||||
impl Event {
|
||||
pub fn validate_for_nextcloud(&self) -> CalDavResult<()> {
|
||||
// Check required fields
|
||||
if self.summary.trim().is_empty() {
|
||||
return Err(CalDavError::EventProcessing("Event summary cannot be empty".to_string()));
|
||||
}
|
||||
|
||||
// Validate timezone
|
||||
if let Some(ref tz) = self.timezone {
|
||||
if !is_valid_timezone(tz) {
|
||||
return Err(CalDavError::EventProcessing(format!("Invalid timezone: {}", tz)));
|
||||
}
|
||||
}
|
||||
|
||||
// Check date ranges
|
||||
if self.start > self.end {
|
||||
return Err(CalDavError::EventProcessing("Event start must be before end".to_string()));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Phase 5: Testing and Integration
|
||||
|
||||
#### 5.1 Unit Tests
|
||||
**File**: `tests/nextcloud_import_tests.rs`
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_event_validation() {
|
||||
// Test valid and invalid events
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_ical_generation() {
|
||||
// Test iCalendar output format
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_conflict_resolution() {
|
||||
// Test different conflict strategies
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_calendar_creation() {
|
||||
// Test Nextcloud calendar creation
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 5.2 Integration Tests
|
||||
**File**: `tests/nextcloud_integration_tests.rs`
|
||||
|
||||
```rust
|
||||
// These tests require a real Nextcloud instance
|
||||
// Use environment variables for test credentials
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore] // Run manually with real instance
|
||||
async fn test_full_import_workflow() {
|
||||
// Test complete import process
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[ignore]
|
||||
async fn test_duplicate_handling() {
|
||||
// Test duplicate event handling
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Priorities
|
||||
|
||||
### Priority 1: Core Import Functionality
|
||||
1. **Enhanced CalDAV client with PUT support** - Essential for writing events
|
||||
2. **Basic Nextcloud client** - Discovery and calendar operations
|
||||
3. **Import command** - CLI interface for importing events
|
||||
4. **Event validation** - Ensure data quality
|
||||
|
||||
### Priority 2: Advanced Features
|
||||
1. **Conflict resolution** - Handle existing events gracefully
|
||||
2. **Batch operations** - Improve performance for many events
|
||||
3. **Error handling** - Comprehensive error management
|
||||
4. **Testing suite** - Ensure reliability
|
||||
|
||||
### Priority 3: Optimization and Polish
|
||||
1. **Progress reporting** - User feedback during import
|
||||
2. **Dry run mode** - Preview imports before execution
|
||||
3. **Configuration validation** - Better error messages
|
||||
4. **Documentation** - User guides and API docs
|
||||
|
||||
## Technical Considerations
|
||||
|
||||
### Nextcloud URL Structure
|
||||
```
|
||||
Base URL: https://cloud.example.com
|
||||
Principal: /remote.php/dav/principals/users/{username}/
|
||||
Calendar Home: /remote.php/dav/calendars/{username}/
|
||||
Calendar URL: /remote.php/dav/calendars/{username}/{calendar-name}/
|
||||
Event URL: /remote.php/dav/calendars/{username}/{calendar-name}/{event-uid}.ics
|
||||
```
|
||||
|
||||
### Authentication
|
||||
- **App Passwords**: Recommended over regular passwords
|
||||
- **Basic Auth**: Standard HTTP Basic authentication
|
||||
- **Two-Factor**: Must use app passwords if 2FA enabled
|
||||
|
||||
### iCalendar Compliance
|
||||
- **RFC 5545**: Strict compliance required
|
||||
- **Required Properties**: UID, DTSTAMP, SUMMARY, DTSTART, DTEND
|
||||
- **Timezone Support**: Proper TZID usage
|
||||
- **Line Folding**: Handle long lines properly
|
||||
|
||||
### Performance Considerations
|
||||
- **Batch Operations**: Use calendar-multiget where possible
|
||||
- **Concurrency**: Import multiple events in parallel
|
||||
- **Memory Management**: Process large event lists in chunks
|
||||
- **Network Efficiency**: Minimize HTTP requests
|
||||
|
||||
## Success Criteria
|
||||
|
||||
### Minimum Viable Product
|
||||
1. ✅ Can import events with title, datetime, and timezone into Nextcloud
|
||||
2. ✅ Handles duplicate events gracefully
|
||||
3. ✅ Provides clear error messages and progress feedback
|
||||
4. ✅ Works with common Nextcloud configurations
|
||||
|
||||
### Complete Implementation
|
||||
1. ✅ Full conflict resolution strategies
|
||||
2. ✅ Batch import with performance optimization
|
||||
3. ✅ Comprehensive error handling and recovery
|
||||
4. ✅ Test suite with >90% coverage
|
||||
5. ✅ Documentation and examples
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Week 1**: Implement CalDAV PUT operations and basic Nextcloud client
|
||||
2. **Week 2**: Add import command and basic workflow
|
||||
3. **Week 3**: Implement validation and error handling
|
||||
4. **Week 4**: Add conflict resolution and batch operations
|
||||
5. **Week 5**: Testing, optimization, and documentation
|
||||
|
||||
This plan provides a structured approach to implementing robust Nextcloud CalDAV import functionality while maintaining compatibility with the existing codebase architecture.
|
||||
Loading…
Add table
Add a link
Reference in a new issue