"""
Content Handler - Revisions, Versions, Comments
Phase J.4: 10 tools
- Revisions: list, get (2)
- Versions: list, get, create, update, delete, promote (6)
- Comments: list, create (2)
"""
import json
from typing import Any
from plugins.directus.client import DirectusClient
def _parse_json_param(value: Any, param_name: str = "parameter") -> Any:
"""Parse a parameter that may be a JSON string or already a native type."""
if value is None:
return None
if isinstance(value, (dict, list)):
return value
if isinstance(value, str):
stripped = value.strip()
if stripped.startswith("{") or stripped.startswith("["):
try:
return json.loads(stripped)
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON in '{param_name}': {e}")
return value
def get_tool_specifications() -> list[dict[str, Any]]:
"""Return tool specifications for ToolGenerator (10 tools)"""
return [
# =====================
# REVISIONS (2)
# =====================
{
"name": "list_revisions",
"method_name": "list_revisions",
"description": "List revisions (history) of items.",
"schema": {
"type": "object",
"properties": {
"filter": {
"anyOf": [{"type": "object"}, {"type": "null"}],
"description": 'Filter object (e.g., {"collection": {"_eq": "posts"}})',
},
"sort": {
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
"description": "Sort fields (default: ['-activity'])",
},
"limit": {
"type": "integer",
"description": "Maximum revisions to return",
"default": 100,
},
},
"required": [],
},
"scope": "read",
},
{
"name": "get_revision",
"method_name": "get_revision",
"description": "Get revision details by ID.",
"schema": {
"type": "object",
"properties": {"id": {"type": "string", "description": "Revision ID"}},
"required": ["id"],
},
"scope": "read",
},
# =====================
# VERSIONS (6)
# =====================
{
"name": "list_versions",
"method_name": "list_versions",
"description": "List content versions (drafts).",
"schema": {
"type": "object",
"properties": {
"filter": {
"anyOf": [{"type": "object"}, {"type": "null"}],
"description": "Filter object",
},
"sort": {
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
"description": "Sort fields",
},
"limit": {
"type": "integer",
"description": "Maximum versions to return",
"default": 100,
},
},
"required": [],
},
"scope": "read",
},
{
"name": "get_version",
"method_name": "get_version",
"description": "Get version details by ID.",
"schema": {
"type": "object",
"properties": {"id": {"type": "string", "description": "Version UUID"}},
"required": ["id"],
},
"scope": "read",
},
{
"name": "create_version",
"method_name": "create_version",
"description": "Create a new content version (draft).",
"schema": {
"type": "object",
"properties": {
"name": {"type": "string", "description": "Version name"},
"collection": {"type": "string", "description": "Collection name"},
"item": {"type": "string", "description": "Item ID"},
"key": {"type": "string", "description": "Version key (unique identifier)"},
},
"required": ["name", "collection", "item", "key"],
},
"scope": "write",
},
{
"name": "update_version",
"method_name": "update_version",
"description": "Update a version.",
"schema": {
"type": "object",
"properties": {
"id": {"type": "string", "description": "Version UUID"},
"data": {"type": "object", "description": "Fields to update (name, delta)"},
},
"required": ["id", "data"],
},
"scope": "write",
},
{
"name": "delete_version",
"method_name": "delete_version",
"description": "Delete a version.",
"schema": {
"type": "object",
"properties": {"id": {"type": "string", "description": "Version UUID to delete"}},
"required": ["id"],
},
"scope": "write",
},
{
"name": "promote_version",
"method_name": "promote_version",
"description": "Promote a version to main (apply changes to item).",
"schema": {
"type": "object",
"properties": {
"id": {"type": "string", "description": "Version UUID to promote"},
"mainHash": {
"anyOf": [{"type": "string"}, {"type": "null"}],
"description": "Expected main content hash (for conflict detection)",
},
},
"required": ["id"],
},
"scope": "write",
},
# =====================
# COMMENTS (2)
# =====================
{
"name": "list_comments",
"method_name": "list_comments",
"description": "List comments on items.",
"schema": {
"type": "object",
"properties": {
"filter": {
"anyOf": [{"type": "object"}, {"type": "null"}],
"description": 'Filter object (e.g., {"collection": {"_eq": "posts"}})',
},
"sort": {
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
"description": "Sort fields",
},
"limit": {
"type": "integer",
"description": "Maximum comments to return",
"default": 100,
},
},
"required": [],
},
"scope": "read",
},
{
"name": "create_comment",
"method_name": "create_comment",
"description": "Create a comment on an item.",
"schema": {
"type": "object",
"properties": {
"collection": {"type": "string", "description": "Collection name"},
"item": {"type": "string", "description": "Item ID"},
"comment": {"type": "string", "description": "Comment text"},
},
"required": ["collection", "item", "comment"],
},
"scope": "write",
},
]
# =====================
# HANDLER FUNCTIONS
# =====================
async def list_revisions(
client: DirectusClient,
filter: dict | None = None,
sort: list[str] | None = None,
limit: int = 100,
) -> str:
"""List revisions."""
try:
# Parse JSON string parameters
parsed_filter = _parse_json_param(filter, "filter")
parsed_sort = _parse_json_param(sort, "sort")
result = await client.list_revisions(filter=parsed_filter, sort=parsed_sort, limit=limit)
revisions = result.get("data", [])
return json.dumps(
{"success": True, "total": len(revisions), "revisions": revisions},
indent=2,
ensure_ascii=False,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_revision(client: DirectusClient, id: str) -> str:
"""Get revision by ID."""
try:
result = await client.get_revision(id)
return json.dumps(
{"success": True, "revision": result.get("data")}, indent=2, ensure_ascii=False
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def list_versions(
client: DirectusClient,
filter: dict | None = None,
sort: list[str] | None = None,
limit: int = 100,
) -> str:
"""List versions."""
try:
# Parse JSON string parameters
parsed_filter = _parse_json_param(filter, "filter")
parsed_sort = _parse_json_param(sort, "sort")
result = await client.list_versions(filter=parsed_filter, sort=parsed_sort, limit=limit)
versions = result.get("data", [])
return json.dumps(
{"success": True, "total": len(versions), "versions": versions},
indent=2,
ensure_ascii=False,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def get_version(client: DirectusClient, id: str) -> str:
"""Get version by ID."""
try:
result = await client.get_version(id)
return json.dumps(
{"success": True, "version": result.get("data")}, indent=2, ensure_ascii=False
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def create_version(
client: DirectusClient, name: str, collection: str, item: str, key: str
) -> str:
"""Create a new version."""
try:
result = await client.create_version(name=name, collection=collection, item=item, key=key)
return json.dumps(
{
"success": True,
"message": f"Version '{name}' created",
"version": result.get("data"),
},
indent=2,
ensure_ascii=False,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def update_version(client: DirectusClient, id: str, data: dict[str, Any]) -> str:
"""Update version."""
try:
# Parse JSON string parameter
parsed_data = _parse_json_param(data, "data")
result = await client.update_version(id, parsed_data)
return json.dumps(
{"success": True, "message": "Version updated", "version": result.get("data")},
indent=2,
ensure_ascii=False,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def delete_version(client: DirectusClient, id: str) -> str:
"""Delete a version."""
try:
await client.delete_version(id)
return json.dumps({"success": True, "message": f"Version {id} deleted"}, indent=2)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def promote_version(client: DirectusClient, id: str, mainHash: str | None = None) -> str:
"""Promote version to main."""
try:
result = await client.promote_version(id, mainHash)
return json.dumps(
{
"success": True,
"message": f"Version {id} promoted to main",
"result": result.get("data") if result else None,
},
indent=2,
ensure_ascii=False,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def list_comments(
client: DirectusClient,
filter: dict | None = None,
sort: list[str] | None = None,
limit: int = 100,
) -> str:
"""List comments."""
try:
# Parse JSON string parameters
parsed_filter = _parse_json_param(filter, "filter")
parsed_sort = _parse_json_param(sort, "sort")
result = await client.list_comments(filter=parsed_filter, sort=parsed_sort, limit=limit)
comments = result.get("data", [])
return json.dumps(
{"success": True, "total": len(comments), "comments": comments},
indent=2,
ensure_ascii=False,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)
async def create_comment(client: DirectusClient, collection: str, item: str, comment: str) -> str:
"""Create a comment."""
try:
result = await client.create_comment(collection, item, comment)
return json.dumps(
{"success": True, "message": "Comment created", "comment": result.get("data")},
indent=2,
ensure_ascii=False,
)
except Exception as e:
return json.dumps({"success": False, "error": str(e)}, indent=2)