8.5 KiB
title | author | date | status | parent_prd |
---|---|---|---|---|
REST Post Chat Endpoint Implementation | Dallin | 2025-03-21 | Draft | optional_prompt_asset_chat.md |
REST Post Chat Endpoint Implementation
Parent Project
This is a sub-PRD of the Optional Prompt Asset Chat System project. Please refer to the parent PRD for the overall project context, goals, and implementation plan.
Problem Statement
The current REST endpoint for creating chats (post_chat_route
) expects a prompt as a required field and handles specific asset types through separate parameters (metric_id
and dashboard_id
). To support the parent project's goals, this endpoint needs to be updated to accept an optional prompt when an asset is provided and to use a more generic asset reference model.
This component will update the REST endpoint to:
- Accept requests with optional prompts when an asset is provided
- Handle generic asset references (asset_id and asset_type)
- Maintain backward compatibility where possible
- Continue to properly validate all inputs
Goals
- Update the REST API endpoint to support optional prompts with asset context
- Modify the endpoint to accept asset_id and asset_type instead of specific asset parameters
- Update validation to ensure valid combinations of parameters
- Maintain backward compatibility with existing clients
- Ensure proper error responses for invalid requests
Non-Goals
- Changing the endpoint URL or method
- Modifying the response format
- Adding new authentication/authorization mechanisms
- Implementing client-side changes to adapt to the updated API
Technical Design
Component Overview
The REST endpoint for creating chats is implemented in post_chat_route
in the file src/routes/rest/routes/chats/post_chat.rs
. It:
- Receives requests from clients
- Extracts the authenticated user from middleware
- Deserializes the request body into a
ChatCreateNewChat
struct - Calls the
post_chat_handler
with the request - Returns the response wrapped in
ApiResponse::JsonData
The updated endpoint will maintain this flow but update the request schema and validation.
graph TD
A[Client] -->|HTTP POST /chats| B[post_chat_route]
B -->|Extract| C[AuthenticatedUser]
B -->|Deserialize| D[ChatCreateNewChatRequest]
D --> E{Validate Request}
E -->|Valid| F[post_chat_handler]
E -->|Invalid| G[Error Response]
F --> H[ApiResponse::JsonData]
G --> I[ApiResponse::Error]
H --> J[Client]
I --> J
Interfaces
Exposed Interfaces
// REST endpoint handler
pub async fn post_chat_route(
Extension(user): Extension<AuthenticatedUser>,
Json(request): Json<ChatCreateNewChatRequest>,
) -> ApiResponse<ChatWithMessages> {
// Implementation
}
// Updated request structure
#[derive(Deserialize)]
pub struct ChatCreateNewChatRequest {
pub prompt: Option<String>, // Now optional
pub chat_id: Option<Uuid>,
pub message_id: Option<Uuid>,
pub asset_id: Option<Uuid>,
pub asset_type: Option<AssetType>,
// Backward compatibility fields (optional)
pub metric_id: Option<Uuid>,
pub dashboard_id: Option<Uuid>,
}
Consumed Interfaces
// Handler signature (from handler component)
pub async fn post_chat_handler(
request: ChatCreateNewChat,
user: AuthenticatedUser,
tx: Option<mpsc::Sender<Result<(BusterContainer, ThreadEvent)>>>,
) -> Result<ChatWithMessages> {
// Implementation in handler component
}
// Conversion to handler request
impl From<ChatCreateNewChatRequest> for ChatCreateNewChat {
fn from(request: ChatCreateNewChatRequest) -> Self {
// Implementation
}
}
Implementation Details
Request Validation and Conversion
// Conversion from API request to handler request
impl From<ChatCreateNewChatRequest> for ChatCreateNewChat {
fn from(request: ChatCreateNewChatRequest) -> Self {
// Check for backward compatibility
let asset_id = if request.asset_id.is_some() {
request.asset_id
} else if request.metric_id.is_some() {
request.metric_id
} else if request.dashboard_id.is_some() {
request.dashboard_id
} else {
None
};
let asset_type = if request.asset_type.is_some() {
request.asset_type
} else if request.metric_id.is_some() {
Some(AssetType::MetricFile)
} else if request.dashboard_id.is_some() {
Some(AssetType::DashboardFile)
} else {
None
};
Self {
prompt: request.prompt,
chat_id: request.chat_id,
message_id: request.message_id,
asset_id,
asset_type,
}
}
}
Updated REST Handler
pub async fn post_chat_route(
Extension(user): Extension<AuthenticatedUser>,
Json(request): Json<ChatCreateNewChatRequest>,
) -> ApiResponse<ChatWithMessages> {
// Convert REST request to handler request
let handler_request: ChatCreateNewChat = request.into();
// Validate parameters
if handler_request.asset_id.is_some() && handler_request.asset_type.is_none() {
return ApiResponse::Error(
StatusCode::BAD_REQUEST,
"asset_type must be provided when asset_id is specified".to_string(),
);
}
// Call handler
match post_chat_handler(handler_request, user, None).await {
Ok(response) => ApiResponse::JsonData(response),
Err(e) => ApiResponse::Error(
StatusCode::INTERNAL_SERVER_ERROR,
format!("Failed to create chat: {}", e),
),
}
}
File Changes
Modified Files
src/routes/rest/routes/chats/post_chat.rs
- Changes:
- Update
ChatCreateNewChatRequest
struct to make prompt optional - Add asset_id and asset_type fields
- Keep metric_id and dashboard_id for backward compatibility
- Add conversion from API request to handler request
- Update validation logic
- Update
- Purpose: REST API endpoint implementation
- Changes:
Testing Strategy
Unit Tests
-
Test
ChatCreateNewChatRequest
toChatCreateNewChat
conversion- Input: Various combinations of prompt, chat_id, asset_id, asset_type, metric_id, and dashboard_id
- Expected output: Correctly converted handler request
- Edge cases:
- Only new fields (asset_id, asset_type)
- Only old fields (metric_id, dashboard_id)
- Mix of old and new fields
- All fields None
-
Test
post_chat_route
validation- Input: Valid and invalid request combinations
- Expected output: ApiResponse::JsonData or ApiResponse::Error
- Edge cases:
- Asset_id without asset_type
- Invalid asset_type values
- No prompt but also no asset
Integration Tests
-
Test scenario: Create chat with asset but no prompt
- Components involved: post_chat_route, post_chat_handler
- Test steps:
- Create request with asset_id, asset_type, but no prompt
- Call post_chat_route
- Verify response contains expected messages
- Expected outcome: Chat created with file and text messages
-
Test scenario: Backward compatibility
- Components involved: post_chat_route, post_chat_handler
- Test steps:
- Create request with metric_id but no asset_id/asset_type
- Call post_chat_route
- Verify correct conversion and processing
- Expected outcome: Chat created with metric context
-
Test scenario: Error handling
- Components involved: post_chat_route, validation
- Test steps:
- Create invalid request (e.g., asset_id without asset_type)
- Call post_chat_route
- Verify proper error response
- Expected outcome: Error response with appropriate status code
Security Considerations
- Validate asset_type to prevent injection attacks
- Maintain user authentication and authorization checks
- Ensure proper error messages that don't leak sensitive information
- Apply rate limiting to prevent abuse
Dependencies on Other Components
Required Components
- Updated Chat Handler: Requires the handler to support optional prompts and generic assets
- Asset Type Definitions: Requires valid asset types to be defined
Concurrent Development
- WebSocket endpoint: Can be updated concurrently
- Potential conflicts: Request structure and validation logic
- Mitigation strategy: Use shared validation functions where possible
Implementation Timeline
- Update request struct: 0.5 days
- Implement conversion logic: 0.5 days
- Update validation: 0.5 days
- Testing: 0.5 days
Total estimated time: 2 days