feat(api): implement inventory API endpoints

Add REST API endpoints for character inventory management:
- GET /api/v1/characters/<id>/inventory - Get inventory and equipped items
- POST /api/v1/characters/<id>/inventory/equip - Equip item to slot
- POST /api/v1/characters/<id>/inventory/unequip - Unequip from slot
- POST /api/v1/characters/<id>/inventory/use - Use consumable item
- DELETE /api/v1/characters/<id>/inventory/<item_id> - Drop item

All endpoints include:
- Authentication via @require_auth decorator
- Ownership validation through CharacterService
- Comprehensive error handling with proper HTTP status codes
- Full logging for debugging

Includes 25 integration tests covering authentication requirements,
URL patterns, and response formats.

Task 2.4 of Phase 4 Combat Implementation complete.
This commit is contained in:
2025-11-26 18:54:33 -06:00
parent 76f67c4a22
commit 4ced1b04df
5 changed files with 1157 additions and 148 deletions

View File

@@ -1498,166 +1498,68 @@ character.inventory.append(generated_item.to_dict()) # Store full item data
---
#### Task 2.4: Inventory API Endpoints (1 day / 8 hours)
#### Task 2.4: Inventory API Endpoints (1 day / 8 hours) ✅ COMPLETE
**Objective:** REST API for inventory management
**File:** `/api/app/api/inventory.py`
**Files Implemented:**
- `/api/app/api/inventory.py` - API blueprint (530 lines)
- `/api/tests/test_inventory_api.py` - Integration tests (25 tests)
**Endpoints:**
**Endpoints Implemented:**
```python
"""
Inventory API Blueprint
| Method | Endpoint | Description |
|--------|----------|-------------|
| GET | `/api/v1/characters/<id>/inventory` | Get inventory + equipped items |
| POST | `/api/v1/characters/<id>/inventory/equip` | Equip item to slot |
| POST | `/api/v1/characters/<id>/inventory/unequip` | Unequip from slot |
| POST | `/api/v1/characters/<id>/inventory/use` | Use consumable item |
| DELETE | `/api/v1/characters/<id>/inventory/<item_id>` | Drop/remove item |
Endpoints:
- GET /api/v1/characters/<id>/inventory - Get inventory
- POST /api/v1/characters/<id>/inventory/equip - Equip item
- POST /api/v1/characters/<id>/inventory/unequip - Unequip item
- POST /api/v1/characters/<id>/inventory/use - Use consumable
- DELETE /api/v1/characters/<id>/inventory/<item_id> - Drop item
"""
**Exception Handling:**
- `CharacterNotFound` → 404 Not Found
- `ItemNotFoundError` → 404 Not Found
- `InvalidSlotError` → 422 Validation Error
- `CannotEquipError` → 400 Bad Request
- `CannotUseItemError` → 400 Bad Request
- `InventoryFullError` → 400 Bad Request
from flask import Blueprint, request, g
**Response Examples:**
from app.services.inventory_service import InventoryService, InventoryError
from app.services.character_service import get_character_service
from app.services.appwrite_service import get_appwrite_service
from app.utils.response import success_response, error_response, not_found_response
from app.utils.auth import require_auth
from app.utils.logging import get_logger
```json
// GET /api/v1/characters/{id}/inventory
{
"result": {
"inventory": [{"item_id": "...", "name": "...", ...}],
"equipped": {
"weapon": {...},
"helmet": null,
...
},
"inventory_count": 5,
"max_inventory": 100
}
}
logger = get_logger(__file__)
inventory_bp = Blueprint('inventory', __name__)
@inventory_bp.route('/<character_id>/inventory', methods=['GET'])
@require_auth
def get_inventory(character_id: str):
"""Get character inventory."""
char_service = get_character_service()
character = char_service.get_character(character_id, g.user_id)
inventory_service = InventoryService(get_appwrite_service())
items = inventory_service.get_inventory(character)
return success_response({
"inventory": [item.to_dict() for item in items],
"equipped": character.equipped
})
@inventory_bp.route('/<character_id>/inventory/equip', methods=['POST'])
@require_auth
def equip_item(character_id: str):
"""
Equip item.
Request JSON:
{
"item_id": "iron_sword",
"slot": "weapon"
}
"""
data = request.get_json()
item_id = data.get('item_id')
slot = data.get('slot')
if not item_id or not slot:
return error_response("item_id and slot required", 400)
try:
char_service = get_character_service()
character = char_service.get_character(character_id, g.user_id)
inventory_service = InventoryService(get_appwrite_service())
inventory_service.equip_item(character, item_id, slot)
# Save character
char_service.update_character(character)
return success_response({
"equipped": character.equipped,
"message": f"Equipped {item_id} to {slot}"
})
except InventoryError as e:
return error_response(str(e), 400)
@inventory_bp.route('/<character_id>/inventory/unequip', methods=['POST'])
@require_auth
def unequip_item(character_id: str):
"""
Unequip item.
Request JSON:
{
"slot": "weapon"
}
"""
data = request.get_json()
slot = data.get('slot')
if not slot:
return error_response("slot required", 400)
char_service = get_character_service()
character = char_service.get_character(character_id, g.user_id)
inventory_service = InventoryService(get_appwrite_service())
inventory_service.unequip_item(character, slot)
# Save character
char_service.update_character(character)
return success_response({
"equipped": character.equipped,
"message": f"Unequipped item from {slot}"
})
@inventory_bp.route('/<character_id>/inventory/use', methods=['POST'])
@require_auth
def use_item(character_id: str):
"""
Use consumable item.
Request JSON:
{
"item_id": "health_potion_small"
}
"""
data = request.get_json()
item_id = data.get('item_id')
if not item_id:
return error_response("item_id required", 400)
try:
char_service = get_character_service()
character = char_service.get_character(character_id, g.user_id)
inventory_service = InventoryService(get_appwrite_service())
result = inventory_service.use_consumable(character, item_id)
# Save character
char_service.update_character(character)
return success_response(result)
except InventoryError as e:
return error_response(str(e), 400)
// POST /api/v1/characters/{id}/inventory/equip
{
"result": {
"message": "Equipped Flaming Dagger to weapon slot",
"equipped": {...},
"unequipped_item": null
}
}
```
**Register blueprint in `/api/app/__init__.py`**
**Blueprint registered in `/api/app/__init__.py`**
**Acceptance Criteria:**
- All inventory endpoints functional
- Authentication required
- Ownership validation enforced
- Errors handled gracefully
**Tests:** 25 passing (`/api/tests/test_inventory_api.py`)
**Acceptance Criteria:** ✅ MET
- [x] All inventory endpoints functional
- [x] Authentication required on all endpoints
- [x] Ownership validation enforced
- [x] Errors handled gracefully with proper HTTP status codes
---