Enforce bounded operation guardrails
Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a028de-e2c8-7732-8521-46a7fc5db82f
This commit is contained in:
parent
c384f60530
commit
26934e25b9
51 changed files with 1843 additions and 472 deletions
|
|
@ -1,6 +1,8 @@
|
|||
"""FastAPI REST API for activity-core.
|
||||
|
||||
T30: CRUD for ActivityDefinition + manual one-shot trigger.
|
||||
T30: basic row administration for ActivityDefinition + manual one-shot trigger.
|
||||
The REST schema does not round-trip markdown-authored rules or instructions;
|
||||
use source sync for the complete definition contract.
|
||||
ACTIVITY-WP-0024: operator automation console under /ops.
|
||||
|
||||
Endpoints:
|
||||
|
|
@ -40,6 +42,7 @@ from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_asyn
|
|||
from temporalio.api.workflowservice.v1 import GetSystemInfoRequest
|
||||
from temporalio.client import Client
|
||||
|
||||
from activity_core.bounded_operations import validate_bounded_operations
|
||||
from activity_core.models import ActivityDefinition, CronTriggerConfig
|
||||
from activity_core.execution_api import router as execution_router
|
||||
from activity_core.ops_api import bind_ops_deps, router as ops_router
|
||||
|
|
@ -83,7 +86,14 @@ async def lifespan(app: FastAPI): # type: ignore[type-arg]
|
|||
await engine.dispose()
|
||||
|
||||
|
||||
app = FastAPI(title="activity-core API", lifespan=lifespan)
|
||||
app = FastAPI(
|
||||
title="activity-core API",
|
||||
description=(
|
||||
"Automation operations plus basic ActivityDefinition row administration. "
|
||||
"Markdown source sync owns the full rules/instructions contract."
|
||||
),
|
||||
lifespan=lifespan,
|
||||
)
|
||||
app.include_router(webhook_router)
|
||||
app.include_router(ops_router)
|
||||
app.include_router(ops_runs_router)
|
||||
|
|
@ -152,6 +162,16 @@ def _row_to_response(row: ActivityDefinitionRow) -> ActivityDefinitionResponse:
|
|||
)
|
||||
|
||||
|
||||
def _validate_context_admission(
|
||||
context_sources: list[dict[str, Any]],
|
||||
instructions: list[dict[str, Any]] | None = None,
|
||||
) -> None:
|
||||
try:
|
||||
validate_bounded_operations(context_sources, instructions or [])
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=422, detail=str(exc)) from exc
|
||||
|
||||
|
||||
async def _upsert_schedule_if_cron(row: ActivityDefinitionRow) -> None:
|
||||
"""Upsert a Temporal Schedule for the row if it uses a cron trigger."""
|
||||
try:
|
||||
|
|
@ -197,7 +217,8 @@ async def get_definition(definition_id: uuid.UUID) -> ActivityDefinitionResponse
|
|||
|
||||
@app.post("/activity-definitions/", response_model=ActivityDefinitionResponse, status_code=201)
|
||||
async def create_definition(body: ActivityDefinitionCreate) -> ActivityDefinitionResponse:
|
||||
"""Create a new ActivityDefinition. Upserts a Temporal Schedule if trigger_type='cron'."""
|
||||
"""Create a basic definition row; source sync owns rules and instructions."""
|
||||
_validate_context_admission(body.context_sources)
|
||||
trigger_type = body.trigger_config.get("trigger_type", "")
|
||||
row = ActivityDefinitionRow(
|
||||
id=uuid.uuid4(),
|
||||
|
|
@ -222,13 +243,20 @@ async def create_definition(body: ActivityDefinitionCreate) -> ActivityDefinitio
|
|||
async def update_definition(
|
||||
definition_id: uuid.UUID, body: ActivityDefinitionUpdate
|
||||
) -> ActivityDefinitionResponse:
|
||||
"""Update an ActivityDefinition. Re-upserts the Temporal Schedule if trigger_type='cron'."""
|
||||
"""Update basic row fields; source sync owns rules and instructions."""
|
||||
Session = _get_db()
|
||||
async with Session() as session:
|
||||
row = await session.get(ActivityDefinitionRow, definition_id)
|
||||
if row is None:
|
||||
raise HTTPException(status_code=404, detail="ActivityDefinition not found")
|
||||
|
||||
_validate_context_admission(
|
||||
body.context_sources
|
||||
if body.context_sources is not None
|
||||
else list(row.context_sources or []),
|
||||
list(row.instructions_json or []),
|
||||
)
|
||||
|
||||
if body.name is not None:
|
||||
row.name = body.name
|
||||
if body.enabled is not None:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue