mirror of
https://github.com/turnstonelabs/turnstone.git
synced 2026-08-12 23:12:23 -06:00
refactor(server): lift close handler into shared session_routes body
Stage 2 Priority 0 Step 0.2 body-convergence — second verb.
``make_close_handler(audit_emit=..., supports_close_reason=...)``
factory in ``turnstone/core/session_routes.py`` produces the lifted
body; both interactive and coord pass their kind-specific audit
emitter at app construction.
The two body-keyed close URL aliases on the interactive side reach
the same lifted body:
- ``POST /v1/api/workstreams/{ws_id}/close`` (new, path-keyed)
via ``register_session_routes(handlers.close=...)``.
- ``POST /v1/api/workstreams/close`` (legacy, body-keyed) via
``make_legacy_body_keyed_adapter(close_handler)``.
Coord exposes only the path-keyed shape.
Behavior gains:
- ``supports_close_reason=True`` (interactive only) keeps the 512-
byte UTF-8 cap + credential redaction + ``workstream_config``
persistence path. Coord stays at ``False``; if coord ever wants
close-reason metadata, flipping the flag is a one-line change.
- ``audit_emit`` is per-kind so each owns its detail dict shape
(``{kind, parent_ws_id, reason}`` vs ``{coord_ws_id, src}``) and
audit action name (``workstream.closed`` vs ``coordinator.close``).
- Standardizes the close-failure status code to 404 across both
kinds. The coord code previously returned 500 on a
``mgr.close()`` race-loss, which was overly pessimistic — the
semantic is "the ws was popped between .get() and .close()", i.e.
not-found.
Coord-side test fixtures (``test_coordinator_endpoints``,
``test_coordinator_end_to_end``) swap the imported
``coordinator_close`` for the lifted handler + a local audit_emit
adapter so the tests exercise the same code path the live console
does.
ruff + mypy + 4366 pytest pass. Live console smoke against
``POST /v1/api/workstreams/abc/close`` returns 503 (no coord_mgr
loaded in the smoke env) — proves the lifted handler is reachable
+ the manager_lookup callable fires correctly.
Two verbs converged so far (``approve`` + ``close``); the remaining
pairs (``send``, ``cancel``, ``open``, ``events``, ``create``,
``list``, ``saved``, ``history``, ``detail``) have substantive
behavior divergence that doesn't factor cleanly into the
SessionEndpointConfig + factory-handler pattern — see the
session_routes module docstring for the per-verb status.
This commit is contained in:
committed by
Patrick Buckley
parent
6415eeb91e
commit
06c91294a4
@@ -8,6 +8,7 @@ enforcement, and lazy rehydration on GET /{ws_id}.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import httpx
|
||||
@@ -28,12 +29,12 @@ from tests._coord_test_helpers import (
|
||||
)
|
||||
from turnstone.console.coordinator_ui import ConsoleCoordinatorUI
|
||||
from turnstone.console.server import (
|
||||
_auth_user_id,
|
||||
_require_admin_coordinator,
|
||||
_require_coord_mgr,
|
||||
cluster_ws_detail,
|
||||
coordinator_cancel,
|
||||
coordinator_children,
|
||||
coordinator_close,
|
||||
coordinator_create,
|
||||
coordinator_detail,
|
||||
coordinator_history,
|
||||
@@ -43,10 +44,37 @@ from turnstone.console.server import (
|
||||
coordinator_send,
|
||||
coordinator_tasks,
|
||||
)
|
||||
from turnstone.core.audit import record_audit
|
||||
from turnstone.core.auth import AuthResult
|
||||
from turnstone.core.session_routes import SessionEndpointConfig, make_approve_handler
|
||||
from turnstone.core.session_routes import (
|
||||
SessionEndpointConfig,
|
||||
make_approve_handler,
|
||||
make_close_handler,
|
||||
)
|
||||
from turnstone.core.storage._sqlite import SQLiteBackend
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from turnstone.core.workstream import Workstream
|
||||
|
||||
|
||||
def _audit_close_coordinator_for_test(
|
||||
request,
|
||||
ws_id: str,
|
||||
ws_before: Workstream, # noqa: ARG001
|
||||
reason: str, # noqa: ARG001
|
||||
) -> None:
|
||||
storage = request.app.state.auth_storage
|
||||
record_audit(
|
||||
storage,
|
||||
_auth_user_id(request),
|
||||
"coordinator.close",
|
||||
"workstream",
|
||||
ws_id,
|
||||
{"coord_ws_id": ws_id, "src": "coordinator"},
|
||||
request.client.host if request.client else "",
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Fixtures
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -97,7 +125,10 @@ def _make_client(
|
||||
),
|
||||
Route(
|
||||
"/v1/api/workstreams/{ws_id}/close",
|
||||
coordinator_close,
|
||||
make_close_handler(
|
||||
audit_emit=_audit_close_coordinator_for_test,
|
||||
supports_close_reason=False,
|
||||
),
|
||||
methods=["POST"],
|
||||
),
|
||||
Route(
|
||||
|
||||
Reference in New Issue
Block a user