diff --git a/src/random_access/database.py b/src/random_access/database.py index ea5b0de..f002b31 100644 --- a/src/random_access/database.py +++ b/src/random_access/database.py @@ -35,7 +35,7 @@ EXPECTED_SCHEMAS = { }, "items": { "required_fields": ["Name", "Type", "Level", "Rarity"], - "optional_fields": ["Description", "Game Name (from Games)"], + "optional_fields": ["Description", "Game Name (from Games)", "Image"], }, "item_instances": { "required_fields": ["ID", "User", "Item"], diff --git a/src/random_access/routes/items.py b/src/random_access/routes/items.py index a8550be..b145faf 100644 --- a/src/random_access/routes/items.py +++ b/src/random_access/routes/items.py @@ -97,6 +97,30 @@ class CreateItemResponse(BaseModel): ) +class ItemImageAttachment(BaseModel): + """Model for item image attachment details.""" + + url: str = Field(..., description="Direct URL to the PNG image file") + filename: Optional[str] = Field(None, description="Original filename of the uploaded image") + type: Optional[str] = Field(None, description="MIME type of the image file") + size: Optional[int] = Field(None, description="File size in bytes") + + +class DetailedItemResponse(BaseModel): + """Response model for detailed item data including image.""" + + id: str = Field(..., description="Unique identifier for the item") + name: str = Field(..., description="Display name of the item") + type: str = Field(..., description="Category or type of the item") + level: int = Field(..., description="Required level to use this item") + rarity: str = Field( + ..., description="Rarity classification (common, rare, epic, legendary, etc.)" + ) + game_name: Optional[str] = Field(None, description="Name of the game this item belongs to") + description: Optional[str] = Field(None, description="Description of the item") + image: Optional[ItemImageAttachment] = Field(None, description="PNG image attachment details") + + def create_items_router( sessions_table, users_table, items_table, item_addons_table, item_instances_table ) -> APIRouter: @@ -245,6 +269,98 @@ The item will be added to the user's inventory and associated with their current detail=error_response["detail"], ) + @router.get( + "/{item_id}", + response_model=DetailedItemResponse, + summary="Get detailed information for a specific item", + description="""Get complete details for a specific item including image thumbnail. + +Returns all available information about the item including name, type, level, rarity, game, description, and base64 encoded image thumbnail. + +**Authentication:** None required""", + responses={ + 200: { + "description": "Item details retrieved successfully", + "content": { + "application/json": { + "example": { + "id": "rec123", + "name": "Iron Sword", + "type": "weapon", + "level": 5, + "rarity": "common", + "game_name": "Adventure Quest", + "description": "A sturdy iron sword perfect for beginning adventurers", + "image": { + "url": "https://v5.airtableusercontent.com/v1/15/15/1704067200000/xyz123/iron-sword.png", + "filename": "iron-sword.png", + "type": "image/png", + "size": 45321 + } + } + } + } + }, + 404: {"description": "Item not found"}, + 429: {"description": "Rate limit exceeded"} + } + ) + @limiter.limit(f"{settings.rate_limit_requests}/minute") + async def get_item(request: Request, item_id: str): + """Get detailed information for a specific item including image.""" + try: + # Validate the item ID format + validate_airtable_id(item_id) + + # Get the item + item = await get_item_by_id(item_id, items_table) + if not item: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Item not found" + ) + + # Extract fields + fields = item.get("fields", {}) + + # Process image attachment if present + image_data = None + if "Image" in fields and fields["Image"]: + # Airtable attachments are arrays of attachment objects + attachments = fields["Image"] + if isinstance(attachments, list) and len(attachments) > 0: + # Take the first attachment + attachment = attachments[0] + image_data = ItemImageAttachment( + url=attachment.get("url", ""), + filename=attachment.get("filename"), + type=attachment.get("type"), + size=attachment.get("size") + ) + + # Build response with all available fields + response_data = { + "id": item["id"], + "name": fields.get("Name", ""), + "type": fields.get("Type", ""), + "level": fields.get("Level", 0), + "rarity": str(fields.get("Rarity", "")) if fields.get("Rarity") is not None else "", + "game_name": fields.get("Game Name (from Games)", [None])[0] if fields.get("Game Name (from Games)") else None, + "description": fields.get("Description"), + "image": image_data + } + + return response_data + + except HTTPException: + raise + except Exception as e: + error_response = create_safe_error_response(e, "Failed to retrieve item") + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=error_response["detail"] + ) + return router