mirror of
https://github.com/readur/readur.git
synced 2026-01-12 17:49:50 -06:00
615 lines
22 KiB
Markdown
615 lines
22 KiB
Markdown
# Error System Documentation
|
|
|
|
This document describes the comprehensive error handling system implemented in readur for consistent, maintainable, and user-friendly error responses.
|
|
|
|
## Overview
|
|
|
|
The error system provides structured, typed error handling across all API endpoints with automatic integration into the existing error management and monitoring infrastructure.
|
|
|
|
## Architecture
|
|
|
|
### Core Components
|
|
|
|
1. **`src/errors/mod.rs`** - Central error module with shared traits and utilities
|
|
2. **Entity-specific error modules** - Dedicated error types for each domain:
|
|
- `src/errors/user.rs` - User management errors
|
|
- `src/errors/source.rs` - File source operation errors
|
|
- `src/errors/label.rs` - Label management errors
|
|
- `src/errors/settings.rs` - Settings configuration errors
|
|
- `src/errors/search.rs` - Search operation errors
|
|
|
|
### AppError Trait
|
|
|
|
All custom errors implement the `AppError` trait which provides:
|
|
|
|
```rust
|
|
pub trait AppError: std::error::Error + Send + Sync + 'static {
|
|
fn status_code(&self) -> StatusCode; // HTTP status code
|
|
fn user_message(&self) -> String; // User-friendly message
|
|
fn error_code(&self) -> &'static str; // Frontend error code
|
|
fn error_category(&self) -> ErrorCategory; // Error categorization
|
|
fn error_severity(&self) -> ErrorSeverity; // Severity level
|
|
fn suppression_key(&self) -> Option<String>; // For repeated error handling
|
|
fn suggested_action(&self) -> Option<String>; // Recovery suggestions
|
|
}
|
|
```
|
|
|
|
## Error Type Reference
|
|
|
|
The following table provides a comprehensive overview of all error types, when to use them, and their characteristics:
|
|
|
|
| Error Type | Module | When to Use | Common Scenarios | Example Error Codes | HTTP Status |
|
|
|------------|--------|-------------|------------------|-------------------|-------------|
|
|
| **ApiError** | `errors::mod` | Generic API errors when specific types don't apply | Rate limiting, payload validation, generic server errors | `BAD_REQUEST`, `NOT_FOUND`, `UNAUTHORIZED`, `INTERNAL_SERVER_ERROR` | 400, 401, 404, 500 |
|
|
| **UserError** | `errors::user` | User management and authentication operations | Registration, login, profile updates, permissions | `USER_NOT_FOUND`, `USER_DUPLICATE_USERNAME`, `USER_PERMISSION_DENIED` | 400, 401, 403, 404, 409 |
|
|
| **SourceError** | `errors::source` | File source operations (WebDAV, S3, Local Folder) | Source configuration, sync operations, connection issues | `SOURCE_CONNECTION_FAILED`, `SOURCE_AUTH_FAILED`, `SOURCE_SYNC_IN_PROGRESS` | 400, 401, 404, 409, 503 |
|
|
| **LabelError** | `errors::label` | Document labeling and label management | Creating/editing labels, label assignment, system labels | `LABEL_DUPLICATE_NAME`, `LABEL_IN_USE`, `LABEL_SYSTEM_MODIFICATION` | 400, 403, 404, 409 |
|
|
| **SettingsError** | `errors::settings` | Application and user settings validation | OCR configuration, preferences, validation | `SETTINGS_INVALID_LANGUAGE`, `SETTINGS_VALUE_OUT_OF_RANGE`, `SETTINGS_READ_ONLY` | 400, 403, 404 |
|
|
| **SearchError** | `errors::search` | Document search and indexing operations | Search queries, index operations, result processing | `SEARCH_QUERY_TOO_SHORT`, `SEARCH_INDEX_UNAVAILABLE`, `SEARCH_TOO_MANY_RESULTS` | 400, 503, 429 |
|
|
| **OcrError** | `ocr::error` | OCR processing operations | Tesseract operations, image processing, language detection | `OCR_NOT_INSTALLED`, `OCR_LANG_MISSING`, `OCR_TIMEOUT` | 400, 500, 503 |
|
|
| **DocumentError** | `routes::documents::crud` | Document CRUD operations | File upload, download, metadata operations | `DOCUMENT_NOT_FOUND`, `DOCUMENT_TOO_LARGE` | 400, 404, 413, 500 |
|
|
|
|
### Error Type Usage Guidelines
|
|
|
|
#### **ApiError** - Generic API Errors
|
|
- **Use when**: No specific error type applies
|
|
- **Best for**: Cross-cutting concerns, middleware errors, generic validation
|
|
- **Avoid when**: Domain-specific operations have dedicated error types
|
|
- **Example**: Request rate limiting, malformed JSON, generic server failures
|
|
|
|
#### **UserError** - User Management
|
|
- **Use when**: Operations involve user accounts, authentication, or authorization
|
|
- **Best for**: Registration, login, profile management, role/permission checks
|
|
- **Covers**: Account creation, credential validation, access control
|
|
- **Example**: Duplicate usernames, invalid passwords, insufficient permissions
|
|
|
|
#### **SourceError** - File Sources
|
|
- **Use when**: Operations involve external file sources (WebDAV, S3, etc.)
|
|
- **Best for**: Source configuration, sync operations, connection management
|
|
- **Covers**: Authentication to external systems, network connectivity, sync status
|
|
- **Example**: WebDAV connection failures, S3 credential issues, sync conflicts
|
|
|
|
#### **LabelError** - Label Management
|
|
- **Use when**: Operations involve document labels or label management
|
|
- **Best for**: Label CRUD operations, label assignment to documents
|
|
- **Covers**: Label validation, system label protection, usage tracking
|
|
- **Example**: Duplicate label names, modifying system labels, deleting used labels
|
|
|
|
#### **SettingsError** - Configuration & Settings
|
|
- **Use when**: Operations involve application or user settings
|
|
- **Best for**: Configuration validation, preference management, OCR settings
|
|
- **Covers**: Value validation, constraint checking, read-only setting protection
|
|
- **Example**: Invalid OCR languages, out-of-range values, conflicting settings
|
|
|
|
#### **SearchError** - Search Operations
|
|
- **Use when**: Operations involve document search or search index management
|
|
- **Best for**: Query validation, index operations, result processing
|
|
- **Covers**: Query syntax, performance limits, index availability
|
|
- **Example**: Short queries, syntax errors, index rebuilding, too many results
|
|
|
|
#### **OcrError** - OCR Processing
|
|
- **Use when**: Operations involve OCR processing with Tesseract
|
|
- **Best for**: OCR-specific failures, Tesseract configuration issues
|
|
- **Covers**: Installation checks, language data, processing errors
|
|
- **Example**: Missing Tesseract, language data not found, processing timeouts
|
|
|
|
#### **DocumentError** - Document Operations
|
|
- **Use when**: Operations involve document CRUD operations
|
|
- **Best for**: File upload/download, document metadata, storage operations
|
|
- **Covers**: File validation, storage limits, document lifecycle
|
|
- **Example**: File too large, unsupported format, document not found
|
|
|
|
### Error Severity Mapping
|
|
|
|
| Error Type | Typical Severity | Reasoning |
|
|
|------------|------------------|-----------|
|
|
| **ApiError** | Minor to Critical | Depends on specific error - server errors are Critical, validation is Minor |
|
|
| **UserError** | Minor to Important | Auth failures are Important, validation errors are Minor |
|
|
| **SourceError** | Minor to Important | Connection issues are Important, config errors are Minor |
|
|
| **LabelError** | Minor | Usually user input validation, rarely system-critical |
|
|
| **SettingsError** | Minor | Configuration errors, typically user-correctable |
|
|
| **SearchError** | Minor to Important | Index unavailable is Important, query errors are Minor |
|
|
| **OcrError** | Minor to Critical | Missing installation is Critical, processing errors are Minor |
|
|
| **DocumentError** | Minor to Important | Storage failures are Important, validation is Minor |
|
|
|
|
### Error Type Decision Tree
|
|
|
|
Use this decision tree to choose the appropriate error type:
|
|
|
|
```
|
|
Is this a user account/authentication operation?
|
|
├─ YES → UserError
|
|
└─ NO → Is this a file source operation (WebDAV, S3, Local)?
|
|
├─ YES → SourceError
|
|
└─ NO → Is this a search/indexing operation?
|
|
├─ YES → SearchError
|
|
└─ NO → Is this an OCR/Tesseract operation?
|
|
├─ YES → OcrError
|
|
└─ NO → Is this a document upload/download/CRUD operation?
|
|
├─ YES → DocumentError
|
|
└─ NO → Is this a label management operation?
|
|
├─ YES → LabelError
|
|
└─ NO → Is this a settings/configuration operation?
|
|
├─ YES → SettingsError
|
|
└─ NO → Use ApiError for generic cases
|
|
```
|
|
|
|
### Quick Reference Checklist
|
|
|
|
**Before choosing an error type, ask:**
|
|
|
|
- [ ] Does the operation primarily involve user accounts, roles, or authentication? → **UserError**
|
|
- [ ] Does the operation connect to external file sources? → **SourceError**
|
|
- [ ] Does the operation search documents or manage search index? → **SearchError**
|
|
- [ ] Does the operation use Tesseract for OCR processing? → **OcrError**
|
|
- [ ] Does the operation upload, download, or manage document files? → **DocumentError**
|
|
- [ ] Does the operation create, modify, or assign labels? → **LabelError**
|
|
- [ ] Does the operation validate or modify application settings? → **SettingsError**
|
|
- [ ] None of the above apply? → **ApiError**
|
|
|
|
### Common Error Mapping Patterns
|
|
|
|
| Operation Type | Recommended Error Type | Example Operations |
|
|
|----------------|----------------------|-------------------|
|
|
| User registration/login | UserError | `/api/auth/register`, `/api/auth/login` |
|
|
| File source sync | SourceError | `/api/sources/{id}/sync`, `/api/sources/{id}/test` |
|
|
| Document search | SearchError | `/api/search`, `/api/documents/search` |
|
|
| OCR processing | OcrError | `/api/documents/{id}/ocr`, `/api/ocr/languages` |
|
|
| Document management | DocumentError | `/api/documents`, `/api/documents/{id}` |
|
|
| Label operations | LabelError | `/api/labels`, `/api/documents/{id}/labels` |
|
|
| Settings management | SettingsError | `/api/settings`, `/api/users/{id}/settings` |
|
|
| Generic API operations | ApiError | Rate limiting, payload validation, CORS |
|
|
|
|
## Error Types
|
|
|
|
### UserError
|
|
|
|
Handles user management operations:
|
|
|
|
```rust
|
|
// Examples
|
|
UserError::NotFound
|
|
UserError::DuplicateUsername { username: "john_doe" }
|
|
UserError::PermissionDenied { reason: "Admin access required" }
|
|
UserError::InvalidCredentials
|
|
UserError::DeleteRestricted { id, reason: "Cannot delete your own account" }
|
|
```
|
|
|
|
**Error Codes**: `USER_NOT_FOUND`, `USER_DUPLICATE_USERNAME`, `USER_PERMISSION_DENIED`, etc.
|
|
|
|
### SourceError
|
|
|
|
Handles file source operations (WebDAV, Local Folder, S3):
|
|
|
|
```rust
|
|
// Examples
|
|
SourceError::ConnectionFailed { details: "Network timeout" }
|
|
SourceError::InvalidPath { path: "/invalid/path" }
|
|
SourceError::AuthenticationFailed { name: "my-webdav", reason: "Invalid credentials" }
|
|
SourceError::SyncInProgress { name: "backup-source" }
|
|
SourceError::ConfigurationInvalid { details: "Missing server URL" }
|
|
```
|
|
|
|
**Error Codes**: `SOURCE_CONNECTION_FAILED`, `SOURCE_AUTH_FAILED`, `SOURCE_CONFIG_INVALID`, etc.
|
|
|
|
### LabelError
|
|
|
|
Handles label management:
|
|
|
|
```rust
|
|
// Examples
|
|
LabelError::DuplicateName { name: "Important" }
|
|
LabelError::SystemLabelModification { name: "system-label" }
|
|
LabelError::InvalidColor { color: "#gggggg" }
|
|
LabelError::LabelInUse { document_count: 42 }
|
|
LabelError::MaxLabelsReached { max_labels: 100 }
|
|
```
|
|
|
|
**Error Codes**: `LABEL_DUPLICATE_NAME`, `LABEL_SYSTEM_MODIFICATION`, `LABEL_IN_USE`, etc.
|
|
|
|
### SettingsError
|
|
|
|
Handles settings validation and management:
|
|
|
|
```rust
|
|
// Examples
|
|
SettingsError::InvalidLanguage { language: "xx", available_languages: "en,es,fr" }
|
|
SettingsError::ValueOutOfRange { setting_name: "timeout", value: 3600, min: 1, max: 300 }
|
|
SettingsError::InvalidOcrConfiguration { details: "DPI too high" }
|
|
SettingsError::ConflictingSettings { setting1: "auto_detect", setting2: "fixed_language" }
|
|
```
|
|
|
|
**Error Codes**: `SETTINGS_INVALID_LANGUAGE`, `SETTINGS_VALUE_OUT_OF_RANGE`, etc.
|
|
|
|
### SearchError
|
|
|
|
Handles search operations:
|
|
|
|
```rust
|
|
// Examples
|
|
SearchError::QueryTooShort { length: 1, min_length: 2 }
|
|
SearchError::TooManyResults { result_count: 15000, max_results: 10000 }
|
|
SearchError::IndexUnavailable { reason: "Rebuilding index" }
|
|
SearchError::InvalidPagination { offset: -1, limit: 0 }
|
|
```
|
|
|
|
**Error Codes**: `SEARCH_QUERY_TOO_SHORT`, `SEARCH_TOO_MANY_RESULTS`, etc.
|
|
|
|
## Integration Features
|
|
|
|
### Error Management System
|
|
|
|
All errors automatically integrate with the sophisticated error management system in `src/monitoring/error_management.rs`:
|
|
|
|
- **Categorization**: Errors are categorized (Auth, Database, Network, etc.)
|
|
- **Severity Levels**: Critical, Important, Minor, Expected
|
|
- **Intelligent Suppression**: Prevents spam from repeated errors
|
|
- **Structured Logging**: Consistent log format with context
|
|
|
|
### HTTP Response Format
|
|
|
|
All errors return consistent JSON responses:
|
|
|
|
```json
|
|
{
|
|
"error": "User-friendly error message",
|
|
"code": "ERROR_CODE_FOR_FRONTEND",
|
|
"status": 400
|
|
}
|
|
```
|
|
|
|
### Frontend Error Codes
|
|
|
|
Each error provides a stable error code for frontend handling:
|
|
|
|
```typescript
|
|
// Frontend can handle specific errors
|
|
switch (error.code) {
|
|
case 'USER_DUPLICATE_USERNAME':
|
|
showUsernameAlreadyExistsMessage();
|
|
break;
|
|
case 'SOURCE_CONNECTION_FAILED':
|
|
showNetworkErrorDialog();
|
|
break;
|
|
case 'SEARCH_QUERY_TOO_SHORT':
|
|
highlightSearchInput();
|
|
break;
|
|
}
|
|
```
|
|
|
|
## Usage Examples
|
|
|
|
### In Route Handlers
|
|
|
|
```rust
|
|
use crate::errors::user::UserError;
|
|
|
|
async fn create_user(
|
|
auth_user: AuthUser,
|
|
State(state): State<Arc<AppState>>,
|
|
Json(user_data): Json<CreateUser>,
|
|
) -> Result<Json<UserResponse>, UserError> {
|
|
require_admin(&auth_user)?;
|
|
|
|
let user = state
|
|
.db
|
|
.create_user(user_data)
|
|
.await
|
|
.map_err(|e| {
|
|
let error_msg = e.to_string();
|
|
if error_msg.contains("username") && error_msg.contains("unique") {
|
|
UserError::duplicate_username(&user_data.username)
|
|
} else if error_msg.contains("email") && error_msg.contains("unique") {
|
|
UserError::duplicate_email(&user_data.email)
|
|
} else {
|
|
UserError::internal_server_error(format!("Database error: {}", e))
|
|
}
|
|
})?;
|
|
|
|
Ok(Json(user.into()))
|
|
}
|
|
```
|
|
|
|
### Convenience Methods
|
|
|
|
All error types provide convenience constructors:
|
|
|
|
```rust
|
|
// Instead of verbose enum construction
|
|
UserError::DuplicateUsername { username: username.clone() }
|
|
|
|
// Use convenience methods
|
|
UserError::duplicate_username(&username)
|
|
UserError::permission_denied("Admin access required")
|
|
UserError::not_found_by_id(user_id)
|
|
```
|
|
|
|
### Error Context and Suggestions
|
|
|
|
Errors include contextual information and recovery suggestions:
|
|
|
|
```rust
|
|
LabelError::invalid_color("#gggggg")
|
|
// Returns: "Invalid color format - use hex format like #0969da"
|
|
// Suggestion: "Use a valid hex color format like #0969da or #ff5722"
|
|
|
|
SourceError::rate_limit_exceeded("my-source", 300)
|
|
// Returns: "Rate limit exceeded, try again in 300 seconds"
|
|
// Suggestion: "Wait 300 seconds before retrying"
|
|
```
|
|
|
|
## Best Practices
|
|
|
|
### 1. Use Specific Error Types
|
|
|
|
```rust
|
|
// Good
|
|
return Err(UserError::duplicate_username(&username));
|
|
|
|
// Avoid
|
|
return Err(UserError::BadRequest { message: "Username exists".to_string() });
|
|
```
|
|
|
|
### 2. Provide Context
|
|
|
|
```rust
|
|
// Good
|
|
SourceError::connection_failed(format!("Failed to connect to {}: {}", url, e))
|
|
|
|
// Less helpful
|
|
SourceError::connection_failed("Connection failed")
|
|
```
|
|
|
|
### 3. Handle Database Errors Thoughtfully
|
|
|
|
```rust
|
|
.map_err(|e| {
|
|
let error_msg = e.to_string();
|
|
if error_msg.contains("unique constraint") {
|
|
UserError::duplicate_username(&username)
|
|
} else if error_msg.contains("not found") {
|
|
UserError::not_found()
|
|
} else {
|
|
UserError::internal_server_error(format!("Database error: {}", e))
|
|
}
|
|
})?
|
|
```
|
|
|
|
### 4. Use Suppression Keys Wisely
|
|
|
|
```rust
|
|
impl AppError for SearchError {
|
|
fn suppression_key(&self) -> Option<String> {
|
|
match self {
|
|
// Suppress repeated "no results" errors
|
|
SearchError::NoResults => Some("search_no_results".to_string()),
|
|
// Don't suppress validation errors - users need to see them
|
|
SearchError::QueryTooShort { .. } => None,
|
|
// Suppress by specific source for connection errors
|
|
SearchError::IndexUnavailable { .. } => Some("search_index_unavailable".to_string()),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Migration from Generic Errors
|
|
|
|
When updating existing endpoints:
|
|
|
|
1. **Add error type import**:
|
|
```rust
|
|
use crate::errors::user::UserError;
|
|
```
|
|
|
|
2. **Update function signature**:
|
|
```rust
|
|
// Before
|
|
async fn my_handler() -> Result<Json<Response>, StatusCode>
|
|
|
|
// After
|
|
async fn my_handler() -> Result<Json<Response>, UserError>
|
|
```
|
|
|
|
**Replace generic error mapping:** Replace basic HTTP status codes with structured error responses.
|
|
```rust
|
|
// Before
|
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
|
|
|
// After
|
|
.map_err(|e| UserError::internal_server_error(format!("Operation failed: {}", e)))?
|
|
```
|
|
|
|
**Update tests:** Modify existing tests to expect the new structured error format instead of simple status codes.
|
|
```rust
|
|
// Before
|
|
assert_eq!(response.status(), StatusCode::CONFLICT);
|
|
|
|
// After - check JSON response
|
|
let error: serde_json::Value = response.json().await;
|
|
assert_eq!(error["code"], "USER_DELETE_RESTRICTED");
|
|
```
|
|
|
|
## Testing
|
|
|
|
The error system includes comprehensive testing to ensure:
|
|
|
|
- Correct HTTP status codes
|
|
- Proper JSON response format
|
|
- Error code consistency
|
|
- Integration with error management
|
|
- Suppression behavior
|
|
|
|
Run error-specific tests:
|
|
```bash
|
|
cargo test user_error
|
|
cargo test source_error
|
|
cargo test error_integration
|
|
```
|
|
|
|
## Error Code Conventions
|
|
|
|
All error types follow consistent naming conventions for error codes:
|
|
|
|
### Naming Pattern
|
|
```
|
|
{TYPE}_{SPECIFIC_ERROR}
|
|
```
|
|
|
|
### Examples by Type
|
|
| Error Type | Prefix | Example Codes |
|
|
|------------|--------|---------------|
|
|
| **ApiError** | None | `BAD_REQUEST`, `NOT_FOUND`, `UNAUTHORIZED` |
|
|
| **UserError** | `USER_` | `USER_NOT_FOUND`, `USER_DUPLICATE_USERNAME`, `USER_PERMISSION_DENIED` |
|
|
| **SourceError** | `SOURCE_` | `SOURCE_CONNECTION_FAILED`, `SOURCE_AUTH_FAILED`, `SOURCE_CONFIG_INVALID` |
|
|
| **LabelError** | `LABEL_` | `LABEL_DUPLICATE_NAME`, `LABEL_IN_USE`, `LABEL_SYSTEM_MODIFICATION` |
|
|
| **SettingsError** | `SETTINGS_` | `SETTINGS_INVALID_LANGUAGE`, `SETTINGS_VALUE_OUT_OF_RANGE` |
|
|
| **SearchError** | `SEARCH_` | `SEARCH_QUERY_TOO_SHORT`, `SEARCH_INDEX_UNAVAILABLE` |
|
|
| **OcrError** | `OCR_` | `OCR_NOT_INSTALLED`, `OCR_LANG_MISSING`, `OCR_TIMEOUT` |
|
|
| **DocumentError** | `DOCUMENT_` | `DOCUMENT_NOT_FOUND`, `DOCUMENT_TOO_LARGE` |
|
|
|
|
### Code Style Guidelines
|
|
- Use `SCREAMING_SNAKE_CASE` for all error codes
|
|
- Start with error type prefix (except ApiError)
|
|
- Be descriptive but concise
|
|
- Avoid abbreviations unless commonly understood
|
|
- Group related errors with consistent sub-prefixes
|
|
|
|
## Practical Examples
|
|
|
|
### Complete Implementation Examples
|
|
|
|
#### User Registration Endpoint
|
|
```rust
|
|
use crate::errors::user::UserError;
|
|
|
|
async fn register_user(
|
|
State(state): State<Arc<AppState>>,
|
|
Json(user_data): Json<CreateUser>,
|
|
) -> Result<Json<UserResponse>, UserError> {
|
|
// Validate input
|
|
if user_data.username.len() < 3 {
|
|
return Err(UserError::invalid_username(
|
|
&user_data.username,
|
|
"Username must be at least 3 characters"
|
|
));
|
|
}
|
|
|
|
// Create user
|
|
let user = state.db.create_user(user_data).await
|
|
.map_err(|e| {
|
|
let error_msg = e.to_string();
|
|
if error_msg.contains("username") && error_msg.contains("unique") {
|
|
UserError::duplicate_username(&user_data.username)
|
|
} else if error_msg.contains("email") && error_msg.contains("unique") {
|
|
UserError::duplicate_email(&user_data.email)
|
|
} else {
|
|
UserError::internal_server_error(format!("Database error: {}", e))
|
|
}
|
|
})?;
|
|
|
|
Ok(Json(user.into()))
|
|
}
|
|
```
|
|
|
|
#### WebDAV Source Test Endpoint
|
|
```rust
|
|
use crate::errors::source::SourceError;
|
|
|
|
async fn test_webdav_connection(
|
|
State(state): State<Arc<AppState>>,
|
|
Path(source_id): Path<Uuid>,
|
|
auth_user: AuthUser,
|
|
) -> Result<Json<ConnectionTestResponse>, SourceError> {
|
|
let source = state.db.get_source(source_id).await
|
|
.map_err(|_| SourceError::not_found_by_id(source_id))?;
|
|
|
|
// Check ownership
|
|
if source.user_id != auth_user.user.id {
|
|
return Err(SourceError::permission_denied(
|
|
"You can only test your own sources"
|
|
));
|
|
}
|
|
|
|
// Test connection
|
|
match test_webdav_connection_internal(&source).await {
|
|
Ok(()) => Ok(Json(ConnectionTestResponse { success: true })),
|
|
Err(e) if e.to_string().contains("authentication") => {
|
|
Err(SourceError::authentication_failed(&source.name, &e.to_string()))
|
|
},
|
|
Err(e) if e.to_string().contains("timeout") => {
|
|
Err(SourceError::network_timeout(&source.url, 30))
|
|
},
|
|
Err(e) => {
|
|
Err(SourceError::connection_failed(format!("Connection test failed: {}", e)))
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Search Query Endpoint
|
|
```rust
|
|
use crate::errors::search::SearchError;
|
|
|
|
async fn search_documents(
|
|
State(state): State<Arc<AppState>>,
|
|
Query(params): Query<SearchParams>,
|
|
auth_user: AuthUser,
|
|
) -> Result<Json<SearchResponse>, SearchError> {
|
|
// Validate query length
|
|
if params.query.len() < 2 {
|
|
return Err(SearchError::query_too_short(params.query.len(), 2));
|
|
}
|
|
|
|
if params.query.len() > 500 {
|
|
return Err(SearchError::query_too_long(params.query.len(), 500));
|
|
}
|
|
|
|
// Check pagination
|
|
if params.limit > 1000 {
|
|
return Err(SearchError::invalid_pagination(params.offset, params.limit));
|
|
}
|
|
|
|
// Perform search
|
|
let results = state.search_service.search(¶ms, auth_user.user.id).await
|
|
.map_err(|e| match e {
|
|
SearchServiceError::IndexUnavailable => {
|
|
SearchError::index_unavailable("Search index is being rebuilt")
|
|
},
|
|
SearchServiceError::TooManyResults(count) => {
|
|
SearchError::too_many_results(count, 10000)
|
|
},
|
|
SearchServiceError::InvalidSyntax(details) => {
|
|
SearchError::invalid_syntax(details)
|
|
},
|
|
_ => SearchError::internal_error(format!("Search failed: {}", e)),
|
|
})?;
|
|
|
|
Ok(Json(results))
|
|
}
|
|
```
|
|
|
|
## Monitoring
|
|
|
|
Error metrics are automatically tracked:
|
|
|
|
- **Error rates** by type and endpoint
|
|
- **Suppression statistics** for repeated errors
|
|
- **Severity distribution** across the application
|
|
- **Recovery suggestions** utilization
|
|
|
|
View error dashboards in Grafana or check Prometheus metrics at `/metrics`.
|
|
|
|
## Future Enhancements
|
|
|
|
Planned improvements to the error system:
|
|
|
|
1. **Internationalization** - Multi-language error messages
|
|
2. **Error Analytics** - Advanced error pattern detection
|
|
3. **Auto-Recovery** - Suggested API retry strategies
|
|
4. **Enhanced Suppression** - Time-based and pattern-based suppression
|
|
5. **Error Documentation** - Auto-generated API error documentation
|
|
|
|
## References
|
|
|
|
- [API Reference](../api-reference.md) |