TaskManager refactor
This commit is contained in:
356
specs/008-migration-ui-improvements/contracts/api.md
Normal file
356
specs/008-migration-ui-improvements/contracts/api.md
Normal file
@@ -0,0 +1,356 @@
|
||||
# API Contracts: Migration UI Improvements
|
||||
|
||||
**Date**: 2025-12-27 | **Status**: Draft
|
||||
|
||||
## Overview
|
||||
|
||||
This document defines the API contracts for the Migration UI Improvements feature. All endpoints follow RESTful conventions and use standard HTTP status codes.
|
||||
|
||||
## Base URL
|
||||
|
||||
`/api/` - All endpoints are relative to the API base URL
|
||||
|
||||
## Authentication
|
||||
|
||||
All endpoints require authentication using the existing session mechanism.
|
||||
|
||||
## Endpoints
|
||||
|
||||
### 1. List Migration Tasks
|
||||
|
||||
**Endpoint**: `GET /tasks`
|
||||
|
||||
**Purpose**: Retrieve a paginated list of migration tasks
|
||||
|
||||
**Parameters**:
|
||||
```
|
||||
limit: integer (query, optional) - Number of tasks to return (default: 10, max: 50)
|
||||
offset: integer (query, optional) - Pagination offset (default: 0)
|
||||
status: string (query, optional) - Filter by task status (PENDING, RUNNING, SUCCESS, FAILED, AWAITING_INPUT)
|
||||
```
|
||||
|
||||
**Response**: `200 OK`
|
||||
|
||||
**Content-Type**: `application/json`
|
||||
|
||||
**Response Body**:
|
||||
```json
|
||||
{
|
||||
"tasks": [
|
||||
{
|
||||
"id": "string (uuid)",
|
||||
"type": "string",
|
||||
"status": "string (enum)",
|
||||
"start_time": "string (iso8601)",
|
||||
"end_time": "string (iso8601) | null",
|
||||
"requires_input": "boolean"
|
||||
}
|
||||
],
|
||||
"total": "integer",
|
||||
"limit": "integer",
|
||||
"offset": "integer"
|
||||
}
|
||||
```
|
||||
|
||||
**Example Request**:
|
||||
```
|
||||
GET /api/tasks?limit=5&offset=0
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
```json
|
||||
{
|
||||
"tasks": [
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440000",
|
||||
"type": "migration",
|
||||
"status": "RUNNING",
|
||||
"start_time": "2025-12-27T09:47:12.000Z",
|
||||
"end_time": null,
|
||||
"requires_input": false
|
||||
},
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"type": "migration",
|
||||
"status": "AWAITING_INPUT",
|
||||
"start_time": "2025-12-27T09:45:00.000Z",
|
||||
"end_time": null,
|
||||
"requires_input": true
|
||||
}
|
||||
],
|
||||
"total": 2,
|
||||
"limit": 5,
|
||||
"offset": 0
|
||||
}
|
||||
```
|
||||
|
||||
**Error Responses**:
|
||||
- `401 Unauthorized` - Authentication required
|
||||
- `400 Bad Request` - Invalid parameters
|
||||
|
||||
### 2. Get Task Logs
|
||||
|
||||
**Endpoint**: `GET /tasks/{task_id}/logs`
|
||||
|
||||
**Purpose**: Retrieve detailed logs for a specific task
|
||||
|
||||
**Parameters**: None
|
||||
|
||||
**Response**: `200 OK`
|
||||
|
||||
**Content-Type**: `application/json`
|
||||
|
||||
**Response Body**:
|
||||
```json
|
||||
{
|
||||
"task_id": "string (uuid)",
|
||||
"status": "string (enum)",
|
||||
"logs": [
|
||||
{
|
||||
"timestamp": "string (iso8601)",
|
||||
"level": "string",
|
||||
"message": "string",
|
||||
"context": "object | null"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Example Request**:
|
||||
```
|
||||
GET /api/tasks/550e8400-e29b-41d4-a716-446655440001/logs
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
```json
|
||||
{
|
||||
"task_id": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"status": "AWAITING_INPUT",
|
||||
"logs": [
|
||||
{
|
||||
"timestamp": "2025-12-27T09:45:00.000Z",
|
||||
"level": "INFO",
|
||||
"message": "Starting migration",
|
||||
"context": null
|
||||
},
|
||||
{
|
||||
"timestamp": "2025-12-27T09:47:12.000Z",
|
||||
"level": "ERROR",
|
||||
"message": "API error during upload",
|
||||
"context": {
|
||||
"error": "Must provide a password for the database",
|
||||
"database": "PostgreSQL"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Error Responses**:
|
||||
- `401 Unauthorized` - Authentication required
|
||||
- `404 Not Found` - Task not found
|
||||
- `403 Forbidden` - Access denied to this task
|
||||
|
||||
### 3. Resume Task with Input
|
||||
|
||||
**Endpoint**: `POST /tasks/{task_id}/resume`
|
||||
|
||||
**Purpose**: Provide required input and resume a paused task
|
||||
|
||||
**Request Body**:
|
||||
```json
|
||||
{
|
||||
"passwords": {
|
||||
"database_name": "password"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response**: `200 OK`
|
||||
|
||||
**Content-Type**: `application/json`
|
||||
|
||||
**Response Body**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Task resumed successfully"
|
||||
}
|
||||
```
|
||||
|
||||
**Example Request**:
|
||||
```
|
||||
POST /api/tasks/550e8400-e29b-41d4-a716-446655440001/resume
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"passwords": {
|
||||
"PostgreSQL": "securepassword123"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Task resumed successfully"
|
||||
}
|
||||
```
|
||||
|
||||
**Error Responses**:
|
||||
- `401 Unauthorized` - Authentication required
|
||||
- `404 Not Found` - Task not found
|
||||
- `400 Bad Request` - Invalid request body or missing required fields
|
||||
- `409 Conflict` - Task not in AWAITING_INPUT state or already completed
|
||||
- `422 Unprocessable Entity` - Invalid password provided
|
||||
|
||||
### 4. Get Task Details (Optional)
|
||||
|
||||
**Endpoint**: `GET /tasks/{task_id}`
|
||||
|
||||
**Purpose**: Get detailed information about a specific task
|
||||
|
||||
**Parameters**: None
|
||||
|
||||
**Response**: `200 OK`
|
||||
|
||||
**Content-Type**: `application/json`
|
||||
|
||||
**Response Body**:
|
||||
```json
|
||||
{
|
||||
"id": "string (uuid)",
|
||||
"type": "string",
|
||||
"status": "string (enum)",
|
||||
"start_time": "string (iso8601)",
|
||||
"end_time": "string (iso8601) | null",
|
||||
"requires_input": "boolean",
|
||||
"input_request": "object | null"
|
||||
}
|
||||
```
|
||||
|
||||
**Example Response**:
|
||||
```json
|
||||
{
|
||||
"id": "550e8400-e29b-41d4-a716-446655440001",
|
||||
"type": "migration",
|
||||
"status": "AWAITING_INPUT",
|
||||
"start_time": "2025-12-27T09:45:00.000Z",
|
||||
"end_time": null,
|
||||
"requires_input": true,
|
||||
"input_request": {
|
||||
"type": "database_password",
|
||||
"databases": ["PostgreSQL"],
|
||||
"error_message": "Must provide a password for the database"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Data Types
|
||||
|
||||
### TaskStatus Enum
|
||||
|
||||
```
|
||||
PENDING
|
||||
RUNNING
|
||||
SUCCESS
|
||||
FAILED
|
||||
AWAITING_INPUT
|
||||
```
|
||||
|
||||
### LogLevel Enum
|
||||
|
||||
```
|
||||
INFO
|
||||
WARNING
|
||||
ERROR
|
||||
DEBUG
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Standard Error Format
|
||||
|
||||
```json
|
||||
{
|
||||
"error": {
|
||||
"code": "string",
|
||||
"message": "string",
|
||||
"details": "object | null"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Common Error Codes
|
||||
|
||||
- `invalid_task_id`: Task ID is invalid or not found
|
||||
- `task_not_awaiting_input`: Task is not in AWAITING_INPUT state
|
||||
- `invalid_password`: Provided password is invalid
|
||||
- `unauthorized`: Authentication required
|
||||
- `bad_request`: Invalid request parameters
|
||||
|
||||
## WebSocket Integration
|
||||
|
||||
### Task Status Updates
|
||||
|
||||
**Channel**: `/ws/tasks/{task_id}/status`
|
||||
|
||||
**Message Format**:
|
||||
```json
|
||||
{
|
||||
"event": "status_update",
|
||||
"task_id": "string (uuid)",
|
||||
"status": "string (enum)",
|
||||
"timestamp": "string (iso8601)"
|
||||
}
|
||||
```
|
||||
|
||||
### Task Log Updates
|
||||
|
||||
**Channel**: `/ws/tasks/{task_id}/logs`
|
||||
|
||||
**Message Format**:
|
||||
```json
|
||||
{
|
||||
"event": "log_update",
|
||||
"task_id": "string (uuid)",
|
||||
"log": {
|
||||
"timestamp": "string (iso8601)",
|
||||
"level": "string",
|
||||
"message": "string",
|
||||
"context": "object | null"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Rate Limiting
|
||||
|
||||
- Maximum 10 requests per minute per user for task list endpoint
|
||||
- Maximum 30 requests per minute per user for task details/logs endpoints
|
||||
- No rate limiting for WebSocket connections
|
||||
|
||||
## Versioning
|
||||
|
||||
All endpoints are versioned using the `Accept` header:
|
||||
- `Accept: application/vnd.api.v1+json` - Current version
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **Authentication**: All endpoints require valid session authentication
|
||||
2. **Authorization**: Users can only access their own tasks
|
||||
3. **Password Handling**: Passwords are not stored permanently, only used for immediate task resumption
|
||||
4. **Input Validation**: All inputs are validated according to defined schemas
|
||||
5. **Rate Limiting**: Prevents abuse of API endpoints
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
1. **Pagination**: Default limit of 10 tasks, maximum of 50
|
||||
2. **Sorting**: Tasks are sorted by start_time descending by default
|
||||
3. **Caching**: Task list responses can be cached for 5 seconds
|
||||
4. **WebSocket**: Use existing WebSocket infrastructure for real-time updates
|
||||
5. **Error Recovery**: Failed task resumptions can be retried with corrected input
|
||||
|
||||
## OpenAPI Specification
|
||||
|
||||
A complete OpenAPI 3.0 specification is available in the repository at `specs/008-migration-ui-improvements/contracts/openapi.yaml`.
|
||||
Reference in New Issue
Block a user