feat: add PATCH endpoint for session scratchpad
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,7 +10,7 @@ from app.core.database import get_db
|
||||
from app.models.tree import Tree
|
||||
from app.models.session import Session
|
||||
from app.models.user import User
|
||||
from app.schemas.session import SessionCreate, SessionUpdate, SessionResponse, SessionExport
|
||||
from app.schemas.session import SessionCreate, SessionUpdate, SessionResponse, SessionExport, ScratchpadUpdate
|
||||
from app.api.deps import get_current_user
|
||||
|
||||
router = APIRouter(prefix="/sessions", tags=["sessions"])
|
||||
@@ -183,6 +183,35 @@ async def complete_session(
|
||||
return session
|
||||
|
||||
|
||||
@router.patch("/{session_id}/scratchpad", response_model=SessionResponse)
|
||||
async def update_scratchpad(
|
||||
session_id: UUID,
|
||||
data: ScratchpadUpdate,
|
||||
db: Annotated[AsyncSession, Depends(get_db)],
|
||||
current_user: Annotated[User, Depends(get_current_user)]
|
||||
):
|
||||
"""Update session scratchpad. Accepts updates on both active and completed sessions."""
|
||||
result = await db.execute(select(Session).where(Session.id == session_id))
|
||||
session = result.scalar_one_or_none()
|
||||
|
||||
if not session:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Session not found"
|
||||
)
|
||||
|
||||
if session.user_id != current_user.id:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail="You don't have access to this session"
|
||||
)
|
||||
|
||||
session.scratchpad = data.scratchpad
|
||||
await db.commit()
|
||||
await db.refresh(session)
|
||||
return session
|
||||
|
||||
|
||||
@router.post("/{session_id}/export")
|
||||
async def export_session(
|
||||
session_id: UUID,
|
||||
|
||||
@@ -363,3 +363,101 @@ class TestSessions:
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["scratchpad"] == update_data["scratchpad"]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_patch_scratchpad(
|
||||
self, client: AsyncClient, auth_headers: dict, test_tree: dict
|
||||
):
|
||||
"""Test the dedicated PATCH scratchpad endpoint."""
|
||||
# Create session
|
||||
create_response = await client.post(
|
||||
"/api/v1/sessions",
|
||||
json={"tree_id": test_tree["id"]},
|
||||
headers=auth_headers
|
||||
)
|
||||
session_id = create_response.json()["id"]
|
||||
|
||||
# Patch scratchpad
|
||||
response = await client.patch(
|
||||
f"/api/v1/sessions/{session_id}/scratchpad",
|
||||
json={"scratchpad": "- IP: 10.0.0.1\n- User: jsmith"},
|
||||
headers=auth_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.json()
|
||||
assert data["scratchpad"] == "- IP: 10.0.0.1\n- User: jsmith"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_patch_scratchpad_not_found(
|
||||
self, client: AsyncClient, auth_headers: dict
|
||||
):
|
||||
"""Test PATCH scratchpad with invalid session ID."""
|
||||
import uuid
|
||||
fake_id = str(uuid.uuid4())
|
||||
|
||||
response = await client.patch(
|
||||
f"/api/v1/sessions/{fake_id}/scratchpad",
|
||||
json={"scratchpad": "test"},
|
||||
headers=auth_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 404
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_patch_scratchpad_empty_string(
|
||||
self, client: AsyncClient, auth_headers: dict, test_tree: dict
|
||||
):
|
||||
"""Test PATCH scratchpad with empty string (clear scratchpad)."""
|
||||
# Create session and set scratchpad
|
||||
create_response = await client.post(
|
||||
"/api/v1/sessions",
|
||||
json={"tree_id": test_tree["id"]},
|
||||
headers=auth_headers
|
||||
)
|
||||
session_id = create_response.json()["id"]
|
||||
|
||||
# Set scratchpad
|
||||
await client.patch(
|
||||
f"/api/v1/sessions/{session_id}/scratchpad",
|
||||
json={"scratchpad": "some notes"},
|
||||
headers=auth_headers
|
||||
)
|
||||
|
||||
# Clear scratchpad
|
||||
response = await client.patch(
|
||||
f"/api/v1/sessions/{session_id}/scratchpad",
|
||||
json={"scratchpad": ""},
|
||||
headers=auth_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["scratchpad"] == ""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_patch_scratchpad_completed_session(
|
||||
self, client: AsyncClient, auth_headers: dict, test_tree: dict
|
||||
):
|
||||
"""Test that scratchpad can still be updated on completed sessions."""
|
||||
# Create and complete session
|
||||
create_response = await client.post(
|
||||
"/api/v1/sessions",
|
||||
json={"tree_id": test_tree["id"]},
|
||||
headers=auth_headers
|
||||
)
|
||||
session_id = create_response.json()["id"]
|
||||
|
||||
await client.post(
|
||||
f"/api/v1/sessions/{session_id}/complete",
|
||||
headers=auth_headers
|
||||
)
|
||||
|
||||
# Should still be able to update scratchpad on completed sessions
|
||||
response = await client.patch(
|
||||
f"/api/v1/sessions/{session_id}/scratchpad",
|
||||
json={"scratchpad": "post-resolution notes"},
|
||||
headers=auth_headers
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.json()["scratchpad"] == "post-resolution notes"
|
||||
|
||||
Reference in New Issue
Block a user