buster/api/prds/active/api_collections_rest_endpoi...

15 KiB

API Collections REST Endpoints

Problem Statement

Currently, the collections functionality in our API is only accessible via WebSocket endpoints. This creates several limitations:

  1. Clients must maintain WebSocket connections to interact with collections
  2. REST API clients cannot access collections functionality
  3. Business logic is tightly coupled with WebSocket-specific code
  4. Code reuse between different transport mechanisms is limited

These limitations impact our API usability and maintainability:

Current Limitations

  • Collections can only be accessed via WebSocket connections
  • Business logic is mixed with transport-specific code
  • Duplicate code may be introduced when implementing new transport mechanisms
  • Testing is more complex due to WebSocket dependencies

Impact

  • User Impact: Clients that prefer or require REST APIs cannot access collections functionality
  • System Impact: Code maintenance is more difficult due to tight coupling
  • Business Impact: Limited API access patterns may reduce API adoption and usage

Requirements

Functional Requirements

Core Functionality

  • Migrate WebSocket collections business logic to transport-agnostic handlers

    • Details: Extract business logic from WebSocket handlers to reusable handlers
    • Acceptance Criteria: Handlers can be used by both WebSocket and REST endpoints
    • Dependencies: Existing WebSocket collections implementation
  • Implement REST endpoints for collections CRUD operations

    • Details: Create REST endpoints that use the new handlers
    • Acceptance Criteria: All collection operations available via REST API
    • Dependencies: New handler implementations

API Endpoints

  • GET /users/collections

    • Details: List collections with pagination and filtering
    • Acceptance Criteria: Same functionality as WebSocket list endpoint
    • Dependencies: List collections handler
  • GET /users/collections/{id}

    • Details: Get a single collection by ID
    • Acceptance Criteria: Same functionality as WebSocket get endpoint
    • Dependencies: Get collection handler
  • POST /users/collections

    • Details: Create a new collection
    • Acceptance Criteria: Same functionality as WebSocket post endpoint
    • Dependencies: Create collection handler
  • PUT /users/collections/{id}

    • Details: Update an existing collection
    • Acceptance Criteria: Same functionality as WebSocket update endpoint
    • Dependencies: Update collection handler
  • DELETE /users/collections/{id}

    • Details: Delete a collection
    • Acceptance Criteria: Same functionality as WebSocket delete endpoint
    • Dependencies: Delete collection handler

Non-Functional Requirements

  • Performance Requirements

    • REST endpoints should have comparable performance to WebSocket endpoints
    • Handlers should efficiently reuse database connections and queries
  • Maintainability Requirements

    • Code should follow the handler guidelines in .cursor/rules/handlers.mdc
    • Business logic should be separated from transport-specific code
    • Types should be shared between handlers and endpoints where possible
  • Compatibility Requirements

    • Existing WebSocket endpoints must continue to function without changes to client code
    • REST endpoints should use consistent naming and patterns with other REST APIs

Technical Design

System Architecture

The new architecture separates business logic from transport-specific code:

graph TD
    WS[WebSocket Endpoints] --> H[Handlers]
    REST[REST Endpoints] --> H
    H --> DB[Database]
    H --> Utils[Utility Functions]

Core Components

Component 1: Collection Handlers

The handlers will implement the core business logic for collections operations:

// libs/handlers/src/collections/list_collections_handler.rs
pub async fn list_collections_handler(
    user_id: &Uuid,
    request: ListCollectionsRequest,
) -> Result<Vec<ListCollectionsCollection>> {
    // Implementation of list collections logic
    // Extracted from existing WebSocket handler
}

// Request and response types
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ListCollectionsRequest {
    pub page: Option<i64>,
    pub page_size: Option<i64>,
    #[serde(flatten)]
    pub filters: Option<ListCollectionsFilter>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ListCollectionsCollection {
    pub id: Uuid,
    pub name: String,
    pub last_edited: DateTime<Utc>,
    pub created_at: DateTime<Utc>,
    pub owner: ListCollectionsUser,
    pub is_shared: bool,
}

Component 2: REST Endpoints

The REST endpoints will use the handlers to implement the API:

// src/routes/rest/routes/users/collections/list.rs
pub async fn list_collections(
    Query(query): Query<ListCollectionsRequest>,
    user: AuthenticatedUser,
) -> Result<Json<Vec<ListCollectionsCollection>>, AppError> {
    let result = collections::list_collections_handler(&user.id, query).await?;
    Ok(Json(result))
}

Component 3: Updated WebSocket Handlers

The WebSocket handlers will be updated to use the new handlers:

// src/routes/ws/collections/list_collections.rs
pub async fn list_collections(user: &AuthenticatedUser, req: ListCollectionsRequest) -> Result<()> {
    let list_collections_res = match collections::list_collections_handler(&user.id, req).await {
        Ok(res) => res,
        Err(e) => {
            // Error handling
            return Err(e);
        }
    };

    // WebSocket-specific response handling
    // ...
}

API Changes

New REST Endpoints

// GET /users/collections
struct ListCollectionsRequest {
    page: Option<i64>,         // Pagination page number
    page_size: Option<i64>,    // Items per page
    shared_with_me: Option<bool>, // Filter for collections shared with user
    owned_by_me: Option<bool>, // Filter for collections owned by user
}

// Response: Vec<ListCollectionsCollection>

// GET /users/collections/{id}
// Path parameter: id: Uuid
// Response: CollectionState

// POST /users/collections
struct CreateCollectionRequest {
    name: String,              // Collection name
    // Other fields as needed
}
// Response: Collection

// PUT /users/collections/{id}
struct UpdateCollectionRequest {
    name: Option<String>,      // Updated collection name
    // Other fields as needed
}
// Response: Collection

// DELETE /users/collections/{id}
// Path parameter: id: Uuid
// Response: ()

File Changes

New Files

  • libs/handlers/src/collections/mod.rs

    • Purpose: Re-export handlers and types
    • Key components: Module exports
    • Dependencies: Handler implementations
  • libs/handlers/src/collections/types.rs

    • Purpose: Shared types for collections
    • Key components: Request and response types
    • Dependencies: None
  • libs/handlers/src/collections/list_collections_handler.rs

    • Purpose: Handler for listing collections
    • Key components: list_collections_handler function
    • Dependencies: Database, types
  • libs/handlers/src/collections/get_collection_handler.rs

    • Purpose: Handler for getting a single collection
    • Key components: get_collection_handler function
    • Dependencies: Database, types
  • libs/handlers/src/collections/create_collection_handler.rs

    • Purpose: Handler for creating a collection
    • Key components: create_collection_handler function
    • Dependencies: Database, types
  • libs/handlers/src/collections/update_collection_handler.rs

    • Purpose: Handler for updating a collection
    • Key components: update_collection_handler function
    • Dependencies: Database, types
  • libs/handlers/src/collections/delete_collection_handler.rs

    • Purpose: Handler for deleting a collection
    • Key components: delete_collection_handler function
    • Dependencies: Database, types
  • src/routes/rest/routes/users/collections/mod.rs

    • Purpose: Re-export REST routes
    • Key components: Module exports
    • Dependencies: Route implementations
  • src/routes/rest/routes/users/collections/list.rs

    • Purpose: REST endpoint for listing collections
    • Key components: list_collections function
    • Dependencies: Handlers, authentication
  • src/routes/rest/routes/users/collections/get.rs

    • Purpose: REST endpoint for getting a collection
    • Key components: get_collection function
    • Dependencies: Handlers, authentication
  • src/routes/rest/routes/users/collections/create.rs

    • Purpose: REST endpoint for creating a collection
    • Key components: create_collection function
    • Dependencies: Handlers, authentication
  • src/routes/rest/routes/users/collections/update.rs

    • Purpose: REST endpoint for updating a collection
    • Key components: update_collection function
    • Dependencies: Handlers, authentication
  • src/routes/rest/routes/users/collections/delete.rs

    • Purpose: REST endpoint for deleting a collection
    • Key components: delete_collection function
    • Dependencies: Handlers, authentication

Modified Files

  • src/routes/ws/collections/list_collections.rs

    • Changes: Update to use new handler
    • Purpose: Maintain WebSocket functionality
  • src/routes/ws/collections/get_collection.rs

    • Changes: Update to use new handler
    • Purpose: Maintain WebSocket functionality
  • src/routes/ws/collections/post_collection.rs

    • Changes: Update to use new handler
    • Purpose: Maintain WebSocket functionality
  • src/routes/ws/collections/update_collection.rs

    • Changes: Update to use new handler
    • Purpose: Maintain WebSocket functionality
  • src/routes/ws/collections/delete_collection.rs

    • Changes: Update to use new handler
    • Purpose: Maintain WebSocket functionality
  • src/routes/rest/routes/mod.rs

    • Changes: Add collections routes
    • Purpose: Register new REST endpoints

Implementation Plan

Phase 1: Create Handler Structure

  1. Create directory structure for handlers

    • Create libs/handlers/src/collections/ directory
    • Create libs/handlers/src/collections/mod.rs
    • Create libs/handlers/src/collections/types.rs
  2. Define shared types

    • Extract types from WebSocket handlers
    • Define request and response types
    • Ensure compatibility with both WebSocket and REST

Phase 2: Implement List Collections

  1. Create list collections handler

    • Create libs/handlers/src/collections/list_collections_handler.rs
    • Extract business logic from WebSocket handler
    • Implement handler function
  2. Update WebSocket handler

    • Update src/routes/ws/collections/list_collections.rs to use new handler
    • Ensure backward compatibility
  3. Create REST endpoint

    • Create src/routes/rest/routes/users/collections/list.rs
    • Implement REST endpoint using the handler
    • Add route to router

Phase 3: Implement Get Collection 🔜

  1. Create get collection handler

    • Create libs/handlers/src/collections/get_collection_handler.rs
    • Extract business logic from WebSocket handler
    • Implement handler function
  2. Update WebSocket handler

    • Update src/routes/ws/collections/get_collection.rs to use new handler
    • Ensure backward compatibility
  3. Create REST endpoint

    • Create src/routes/rest/routes/users/collections/get.rs
    • Implement REST endpoint using the handler
    • Add route to router

Phase 4: Implement Create Collection 🔜

  1. Create create collection handler

    • Create libs/handlers/src/collections/create_collection_handler.rs
    • Extract business logic from WebSocket handler
    • Implement handler function
  2. Update WebSocket handler

    • Update src/routes/ws/collections/post_collection.rs to use new handler
    • Ensure backward compatibility
  3. Create REST endpoint

    • Create src/routes/rest/routes/users/collections/create.rs
    • Implement REST endpoint using the handler
    • Add route to router

Phase 5: Implement Update Collection 🔜

  1. Create update collection handler

    • Create libs/handlers/src/collections/update_collection_handler.rs
    • Extract business logic from WebSocket handler
    • Implement handler function
  2. Update WebSocket handler

    • Update src/routes/ws/collections/update_collection.rs to use new handler
    • Ensure backward compatibility
  3. Create REST endpoint

    • Create src/routes/rest/routes/users/collections/update.rs
    • Implement REST endpoint using the handler
    • Add route to router

Phase 6: Implement Delete Collection 🔜

  1. Create delete collection handler

    • Create libs/handlers/src/collections/delete_collection_handler.rs
    • Extract business logic from WebSocket handler
    • Implement handler function
  2. Update WebSocket handler

    • Update src/routes/ws/collections/delete_collection.rs to use new handler
    • Ensure backward compatibility
  3. Create REST endpoint

    • Create src/routes/rest/routes/users/collections/delete.rs
    • Implement REST endpoint using the handler
    • Add route to router

Phase 7: Testing and Documentation 🔜

  1. Write tests

    • Unit tests for handlers
    • Integration tests for REST endpoints
    • Verify WebSocket backward compatibility
  2. Update documentation

    • Add API documentation for new endpoints
    • Update internal documentation

Testing Strategy

Unit Tests

  • Test each handler function with various inputs
  • Test error handling and edge cases
  • Mock database connections and external dependencies
#[tokio::test]
async fn test_list_collections_handler() {
    // Setup test data
    let user_id = Uuid::new_v4();
    let request = ListCollectionsRequest {
        page: Some(0),
        page_size: Some(10),
        filters: None,
    };
    
    // Call handler
    let result = list_collections_handler(&user_id, request).await;
    
    // Assert expectations
    assert!(result.is_ok());
    let collections = result.unwrap();
    // Additional assertions
}

Integration Tests

  • Test REST endpoints with HTTP requests
  • Verify response formats and status codes
  • Test pagination, filtering, and error handling
#[tokio::test]
async fn test_list_collections_endpoint() {
    // Setup test app
    let app = test_app().await;
    
    // Make request
    let response = app
        .get("/users/collections")
        .header("Authorization", "Bearer test_token")
        .send()
        .await;
    
    // Assert expectations
    assert_eq!(response.status(), 200);
    // Additional assertions
}

Backward Compatibility Tests

  • Verify that WebSocket endpoints continue to work as expected
  • Test with existing client code

Success Criteria

  • All REST endpoints are implemented and working correctly
  • WebSocket endpoints continue to work as before
  • Code follows best practices and guidelines
  • All tests pass

Dependencies

  • Existing WebSocket handlers
  • Database schema and models
  • Authentication middleware

Checklist Before Submission

  • All template sections completed
  • Technical design is detailed and complete
  • File changes are documented
  • Implementation phases are clear
  • Testing strategy is defined
  • Security considerations addressed
  • Dependencies listed
  • File references included