buster/api/prds/active/api_post_chat_rest_endpoint.md

267 lines
8.5 KiB
Markdown
Raw Normal View History

2025-03-22 06:05:30 +08:00
---
title: REST Post Chat Endpoint Implementation
author: Dallin
date: 2025-03-21
status: Draft
parent_prd: optional_prompt_asset_chat.md
---
# REST Post Chat Endpoint Implementation
## Parent Project
This is a sub-PRD of the [Optional Prompt Asset Chat System](optional_prompt_asset_chat.md) 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:
1. Accept requests with optional prompts when an asset is provided
2. Handle generic asset references (asset_id and asset_type)
3. Maintain backward compatibility where possible
4. Continue to properly validate all inputs
## Goals
1. Update the REST API endpoint to support optional prompts with asset context
2. Modify the endpoint to accept asset_id and asset_type instead of specific asset parameters
3. Update validation to ensure valid combinations of parameters
4. Maintain backward compatibility with existing clients
5. Ensure proper error responses for invalid requests
## Non-Goals
1. Changing the endpoint URL or method
2. Modifying the response format
3. Adding new authentication/authorization mechanisms
4. 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:
1. Receives requests from clients
2. Extracts the authenticated user from middleware
3. Deserializes the request body into a `ChatCreateNewChat` struct
4. Calls the `post_chat_handler` with the request
5. Returns the response wrapped in `ApiResponse::JsonData`
The updated endpoint will maintain this flow but update the request schema and validation.
```mermaid
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
```rust
// 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
```rust
// 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
```rust
// 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
```rust
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
- Purpose: REST API endpoint implementation
## Testing Strategy
### Unit Tests
- Test `ChatCreateNewChatRequest` to `ChatCreateNewChat` 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:
1. Create request with asset_id, asset_type, but no prompt
2. Call post_chat_route
3. 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:
1. Create request with metric_id but no asset_id/asset_type
2. Call post_chat_route
3. Verify correct conversion and processing
- Expected outcome: Chat created with metric context
- Test scenario: Error handling
- Components involved: post_chat_route, validation
- Test steps:
1. Create invalid request (e.g., asset_id without asset_type)
2. Call post_chat_route
3. 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