Files
mtgonline/backend/API_DOCUMENTATION.md
akadmin c42d7ca0e1 feat: implement user data schema and API endpoints
- Add Alembic migration setup with async configuration
- Create 16 user data models (users, decks, cards, replays, etc.)
- Implement comprehensive API endpoints with JWT auth
- Add replay, card collection, group, network, preferences, and activity log routers
- Include API documentation and migration test plan
- Update Dockerfile to run migrations on startup
2026-07-23 03:23:54 +00:00

853 lines
15 KiB
Markdown

# User Data API Endpoints - Complete Documentation
## Overview
The User Data API provides comprehensive CRUD operations for:
- **Session Management** - User authentication sessions
- **Deck Versions** - Deck version history and rollback
- **Game Replays** - Game recording and playback
- **Game Outcomes** - Win/loss tracking with ratings
- **User Statistics** - Denormalized stats (games, wins, streaks)
- **Card Collection** - User-owned cards with condition/language
- **Wishlist** - Cards users want to acquire
- **Groups** - User groups with roles and chat
- **Networks** - Extended social connections
- **Preferences** - User settings and preferences
- **Activity Log** - Audit trail with JSONB metadata
## Base URL
```
/api/v1/user-data
```
## Authentication
All endpoints require a valid JWT token in the `Authorization` header:
```
Authorization: Bearer <your-jwt-token>
```
---
## 1. Session Management
### Get Active Sessions
```http
GET /sessions/me
```
**Response:**
```json
[
{
"cleaned_count": 2,
"message": "Found 2 active sessions"
}
]
```
### Cleanup Expired Sessions
```http
DELETE /sessions/cleanup
```
**Response:**
```json
{
"message": "Cleaned 5 expired sessions"
}
```
### Logout Current Session
```http
POST /sessions/logout
```
**Response:**
```json
{
"message": "Logged out successfully"
}
```
---
## 2. Deck Versions
### Create Deck Version
```http
POST /decks/{deck_id}/versions
Content-Type: application/json
{
"content": "4x Thoughtseize, 4x Lightning Bolt, ...",
"status": "DRAFT",
"comment": "Updated for meta change"
}
```
**Response:**
```json
{
"id": 1,
"deck_id": 42,
"version_number": 3,
"content": "4x Thoughtseize, ...",
"status": "DRAFT",
"comment": "Updated for meta change",
"created_at": "2026-01-01T12:00:00Z"
}
```
### Get Deck Versions
```http
GET /decks/{deck_id}/versions?page=1&page_size=50
```
**Response:**
```json
{
"versions": [...],
"total": 10,
"page": 1,
"page_size": 50,
"total_pages": 1
}
```
### Update Deck Version
```http
PATCH /decks/{deck_id}/versions/{version_id}
Content-Type: application/json
{
"status": "FINAL",
"comment": "Ready for tournament"
}
```
### Delete Deck Version
```http
DELETE /decks/{deck_id}/versions/{version_id}
```
---
## 3. Game Replays
### Create Game Replay
```http
POST /replays
Content-Type: application/json
{
"game_uuid": "550e8400-e29b-41d4-a716-446655440000",
"room_id": 1,
"game_type": "Draft",
"format": "Standard",
"duration_seconds": 1800,
"start_time": "2026-01-01T12:00:00Z",
"end_time": "2026-01-01T12:30:00Z",
"status": "COMPLETED",
"replay_data": {
"turns": [...],
"deck": {...}
}
}
```
**Response:**
```json
{
"id": 1,
"game_uuid": "550e8400-...",
"room_id": 1,
"game_type": "Draft",
"format": "Standard",
"duration_seconds": 1800,
"start_time": "2026-01-01T12:00:00Z",
"end_time": "2026-01-01T12:30:00Z",
"status": "COMPLETED",
"replay_data": {...},
"created_at": "2026-01-01T12:30:00Z",
"updated_at": "2026-01-01T12:30:00Z",
"players": [...]
}
```
### Get Game Replays
```http
GET /replays?page=1&page_size=50&user_id=42&status_filter=COMPLETED
```
**Response:**
```json
{
"replays": [...],
"total": 100,
"page": 1,
"page_size": 50,
"total_pages": 2
}
```
### Get Replay Players
```http
GET /replays/{replay_id}/players
```
**Response:**
```json
[
{
"id": 1,
"user_id": 42,
"deck_id": 10,
"position": 1,
"won": true,
"lost": false,
"concession": false,
"turn_one": false
}
]
```
---
## 4. Game Outcomes
### Create Game Outcome
```http
POST /outcomes
Content-Type: application/json
{
"game_uuid": "550e8400-e29b-41d4-a716-446655440000",
"outcome": "WIN",
"opponent_id": 99,
"format": "Standard",
"rating_before": 1500,
"rating_after": 1525,
"rating_change": 25
}
```
**Response:**
```json
{
"id": 1,
"user_id": 42,
"game_uuid": "550e8400-...",
"outcome": "WIN",
"opponent_id": 99,
"format": "Standard",
"rating_before": 1500,
"rating_after": 1525,
"rating_change": 25,
"created_at": "2026-01-01T12:30:00Z"
}
```
### Get Game Outcomes
```http
GET /outcomes?page=1&page_size=50&user_id=42
```
---
## 5. User Statistics
### Get User Statistics
```http
GET /statistics/{user_id}
```
**Response:**
```json
{
"user_id": 42,
"total_games": 150,
"total_wins": 90,
"total_losses": 55,
"total_concessions": 5,
"win_rate": 60.0,
"current_streak": 3,
"best_streak": 8,
"average_rating": 1450.5,
"last_game_date": "2026-01-01T12:00:00Z",
"updated_at": "2026-01-01T12:30:00Z"
}
```
### Update User Statistics
```http
POST /statistics/update
Content-Type: application/json
{
"user_id": 42,
"outcome": "WIN"
}
```
**Response:**
```json
{
"user_id": 42,
"total_games": 151,
"total_wins": 91,
"total_losses": 55,
"win_rate": 60.26,
"current_streak": 4,
"updated_at": "2026-01-01T12:35:00Z"
}
```
---
## 6. Card Collection
### Add Card to Collection
```http
POST /collection
Content-Type: application/json
{
"card_id": 12345,
"quantity": 4,
"condition": "NEAR_MINT",
"language": "EN",
"is_foil": true,
"is_alt_art": false,
"acquired_date": "2026-01-01T00:00:00Z",
"acquisition_method": "Bought",
"notes": "From card shop"
}
```
**Response:**
```json
{
"id": 1,
"user_id": 42,
"card_id": 12345,
"quantity": 4,
"condition": "NEAR_MINT",
"language": "EN",
"is_foil": true,
"is_alt_art": false,
"acquired_date": "2026-01-01T00:00:00Z",
"acquisition_method": "Bought",
"notes": "From card shop",
"created_at": "2026-01-01T12:00:00Z",
"updated_at": "2026-01-01T12:00:00Z"
}
```
### Get Card Collection
```http
GET /collection?page=1&page_size=50&is_foil=true&is_alt_art=false
```
**Response:**
```json
{
"cards": [...],
"total": 500,
"page": 1,
"page_size": 50,
"total_pages": 10
}
```
### Update Card in Collection
```http
PATCH /collection/{card_id}
Content-Type: application/json
{
"quantity": 3,
"condition": "EX",
"notes": "Slightly worn"
}
```
### Remove Card from Collection
```http
DELETE /collection/{card_id}
```
---
## 7. Wishlist
### Add to Wishlist
```http
POST /wishlist
Content-Type: application/json
{
"card_id": 12345,
"max_price": 50.00,
"notes": "Looking for foil version"
}
```
**Response:**
```json
{
"id": 1,
"user_id": 42,
"card_id": 12345,
"max_price": 50.00,
"notes": "Looking for foil version",
"created_at": "2026-01-01T12:00:00Z"
}
```
### Get Wishlist
```http
GET /wishlist?page=1&page_size=50
```
### Update Wishlist Item
```http
PATCH /wishlist/{item_id}
Content-Type: application/json
{
"max_price": 75.00,
"notes": "Willing to pay more"
}
```
### Remove from Wishlist
```http
DELETE /wishlist/{item_id}
```
---
## 8. Groups
### Create Group
```http
POST /groups
Content-Type: application/json
{
"name": "Standard Players",
"description": "Casual Standard players",
"is_public": true,
"max_members": 50
}
```
**Response:**
```json
{
"id": 1,
"name": "Standard Players",
"description": "Casual Standard players",
"owner_id": 42,
"is_public": true,
"max_members": 50,
"created_at": "2026-01-01T12:00:00Z",
"updated_at": "2026-01-01T12:00:00Z",
"member_count": 1,
"is_member": true
}
```
### Get User Groups
```http
GET /groups?page=1&page_size=50&is_public=true
```
**Response:**
```json
{
"groups": [...],
"total": 5,
"page": 1,
"page_size": 50,
"total_pages": 1
}
```
### Update Group
```http
PATCH /groups/{group_id}
Content-Type: application/json
{
"description": "Updated description",
"max_members": 100
}
```
### Delete Group
```http
DELETE /groups/{group_id}
```
---
## 9. Group Members
### Add Group Member
```http
POST /groups/{group_id}/members
Content-Type: application/json
{
"user_id": 99,
"role": "MEMBER"
}
```
**Response:**
```json
{
"message": "Member added",
"member_id": 5
}
```
### Update Group Member Role
```http
PATCH /groups/{group_id}/members/{member_id}
Content-Type: application/json
{
"role": "ADMIN"
}
```
**Response:**
```json
{
"message": "Member role updated"
}
```
### Remove Group Member
```http
DELETE /groups/{group_id}/members/{member_id}
```
---
## 10. Group Chat Messages
### Send Group Message
```http
POST /groups/{group_id}/messages
Content-Type: application/json
{
"message": "Hey everyone! Ready for a game?"
}
```
**Response:**
```json
{
"id": 1,
"group_id": 1,
"sender_id": 42,
"sender_username": "player42",
"message": "Hey everyone! Ready for a game?",
"created_at": "2026-01-01T12:00:00Z"
}
```
### Get Group Messages
```http
GET /groups/{group_id}/messages?page=1&page_size=50
```
**Response:**
```json
{
"messages": [...],
"total": 25,
"page": 1,
"page_size": 50,
"total_pages": 1
}
```
---
## 11. Networks
### Create Network
```http
POST /networks
Content-Type: application/json
{
"name": "MTG Enthusiasts",
"description": "Friends who play Magic",
"is_public": true
}
```
**Response:**
```json
{
"id": 1,
"name": "MTG Enthusiasts",
"description": "Friends who play Magic",
"creator_id": 42,
"is_public": true,
"created_at": "2026-01-01T12:00:00Z",
"member_count": 1,
"is_member": true
}
```
### Get User Networks
```http
GET /networks?page=1&page_size=50
```
### Update Network
```http
PATCH /networks/{network_id}
Content-Type: application/json
{
"description": "Updated network description"
}
```
### Delete Network
```http
DELETE /networks/{network_id}
```
---
## 12. Network Members
### Add Network Member
```http
POST /networks/{network_id}/members
Content-Type: application/json
{
"user_id": 99,
"role": "MEMBER"
}
```
**Response:**
```json
{
"message": "Member added",
"member_id": 3
}
```
---
## 13. User Preferences
### Get User Preferences
```http
GET /preferences
```
**Response:**
```json
{
"user_id": 42,
"theme": "light",
"notifications_enabled": true,
"email_notifications": true,
"auto_save_decks": true,
"default_format": "standard",
"language": "EN",
"updated_at": "2026-01-01T12:00:00Z"
}
```
### Update User Preferences
```http
PATCH /preferences
Content-Type: application/json
{
"theme": "dark",
"notifications_enabled": false,
"default_format": "modern"
}
```
**Response:**
```json
{
"user_id": 42,
"theme": "dark",
"notifications_enabled": false,
"email_notifications": true,
"auto_save_decks": true,
"default_format": "modern",
"language": "EN",
"updated_at": "2026-01-01T12:05:00Z"
}
```
---
## 14. Activity Log
### Get Activity Log
```http
GET /activity?page=1&page_size=50&activity_type=LOGIN
```
**Response:**
```json
{
"entries": [
{
"id": 1,
"user_id": 42,
"activity_type": "LOGIN",
"activity_data": {"ip": "192.168.1.1"},
"ip_address": "192.168.1.1",
"created_at": "2026-01-01T12:00:00Z"
}
],
"total": 100,
"page": 1,
"page_size": 50,
"total_pages": 2
}
```
---
## Error Responses
All endpoints return consistent error responses:
```json
{
"detail": "Error message"
}
```
### Common Error Codes
| Status Code | Description |
|------------|-------------|
| 400 | Bad Request - Invalid input |
| 401 | Unauthorized - Missing or invalid token |
| 403 | Forbidden - Insufficient permissions |
| 404 | Not Found - Resource doesn't exist |
| 409 | Conflict - Resource already exists |
| 500 | Internal Server Error |
---
## Testing with cURL
### Example: Create a Deck Version
```bash
curl -X POST "http://localhost:8000/api/v1/user-data/decks/42/versions" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"content": "4x Thoughtseize, 4x Lightning Bolt, ...",
"status": "DRAFT",
"comment": "Updated for meta"
}'
```
### Example: Get Card Collection
```bash
curl "http://localhost:8000/api/v1/user-data/collection?page=1&page_size=10" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
```
### Example: Add to Wishlist
```bash
curl -X POST "http://localhost:8000/api/v1/user-data/wishlist" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"card_id": 12345,
"max_price": 50.00,
"notes": "Looking for foil"
}'
```
---
## Available Endpoints Summary
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/sessions/me` | Get active sessions |
| DELETE | `/sessions/cleanup` | Cleanup expired sessions |
| POST | `/sessions/logout` | Logout current session |
| POST | `/decks/{id}/versions` | Create deck version |
| GET | `/decks/{id}/versions` | Get deck versions |
| PATCH | `/decks/{id}/versions/{vid}` | Update deck version |
| DELETE | `/decks/{id}/versions/{vid}` | Delete deck version |
| POST | `/replays` | Create game replay |
| GET | `/replays` | Get game replays |
| GET | `/replays/{id}` | Get specific replay |
| PATCH | `/replays/{id}` | Update replay |
| DELETE | `/replays/{id}` | Delete replay |
| POST | `/replays/{id}/players` | Add player to replay |
| GET | `/replays/{id}/players` | Get replay players |
| POST | `/outcomes` | Create game outcome |
| GET | `/outcomes` | Get game outcomes |
| GET | `/statistics/{id}` | Get user statistics |
| POST | `/statistics/update` | Update user statistics |
| POST | `/collection` | Add card to collection |
| GET | `/collection` | Get card collection |
| PATCH | `/collection/{id}` | Update card |
| DELETE | `/collection/{id}` | Remove card |
| POST | `/wishlist` | Add to wishlist |
| GET | `/wishlist` | Get wishlist |
| PATCH | `/wishlist/{id}` | Update wishlist item |
| DELETE | `/wishlist/{id}` | Remove from wishlist |
| POST | `/groups` | Create group |
| GET | `/groups` | Get user groups |
| GET | `/groups/{id}` | Get specific group |
| PATCH | `/groups/{id}` | Update group |
| DELETE | `/groups/{id}` | Delete group |
| POST | `/groups/{id}/members` | Add group member |
| PATCH | `/groups/{id}/members/{mid}` | Update member role |
| DELETE | `/groups/{id}/members/{mid}` | Remove member |
| POST | `/groups/{id}/messages` | Send group message |
| GET | `/groups/{id}/messages` | Get group messages |
| POST | `/networks` | Create network |
| GET | `/networks` | Get user networks |
| GET | `/networks/{id}` | Get specific network |
| PATCH | `/networks/{id}` | Update network |
| DELETE | `/networks/{id}` | Delete network |
| POST | `/networks/{id}/members` | Add network member |
| GET | `/preferences` | Get user preferences |
| PATCH | `/preferences` | Update preferences |
| GET | `/activity` | Get activity log |
---
## Next Steps
1. **Test endpoints** with curl or Postman
2. **Create integration tests** for each endpoint
3. **Add rate limiting** for production
4. **Implement pagination optimization** for large datasets
5. **Add search functionality** for cards and decks
6. **Create webhook endpoints** for real-time notifications