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)
390 lines
12 KiB
Markdown
390 lines
12 KiB
Markdown
# 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.
|