fix(us3): align dataset review contracts and acceptance gates
This commit is contained in:
@@ -17,7 +17,7 @@ from __future__ import annotations
|
||||
# [DEF:DatasetReviewApi.imports:Block]
|
||||
import json
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Response, status
|
||||
from pydantic import BaseModel, Field
|
||||
@@ -233,15 +233,22 @@ class BatchApproveMappingRequest(BaseModel):
|
||||
|
||||
# [DEF:PreviewEnqueueResultResponse:Class]
|
||||
# @COMPLEXITY: 2
|
||||
# @PURPOSE: Synchronous preview trigger response aligned with preview-status contract.
|
||||
# @PURPOSE: Contract-compliant async preview trigger response exposing only enqueue state.
|
||||
class PreviewEnqueueResultResponse(BaseModel):
|
||||
session_id: str
|
||||
preview_status: str
|
||||
task_id: Optional[str] = None
|
||||
preview: Optional[CompiledPreviewDto] = None
|
||||
# [/DEF:PreviewEnqueueResultResponse:Class]
|
||||
|
||||
|
||||
# [DEF:MappingCollectionResponse:Class]
|
||||
# @COMPLEXITY: 2
|
||||
# @PURPOSE: Contract-compliant wrapper for execution mapping list responses.
|
||||
class MappingCollectionResponse(BaseModel):
|
||||
items: List[ExecutionMappingDto]
|
||||
# [/DEF:MappingCollectionResponse:Class]
|
||||
|
||||
|
||||
# [DEF:UpdateExecutionMappingRequest:Class]
|
||||
# @COMPLEXITY: 2
|
||||
# @PURPOSE: Request DTO for one manual execution-mapping override update without introducing unrelated bulk mutation semantics.
|
||||
@@ -254,10 +261,10 @@ class UpdateExecutionMappingRequest(BaseModel):
|
||||
|
||||
# [DEF:LaunchDatasetResponse:Class]
|
||||
# @COMPLEXITY: 2
|
||||
# @PURPOSE: Response DTO for one completed launch submission handoff with updated session and persisted run context.
|
||||
# @PURPOSE: Contract-compliant launch result exposing audited run context and SQL Lab redirect target.
|
||||
class LaunchDatasetResponse(BaseModel):
|
||||
session: SessionSummary
|
||||
run_context: DatasetRunContextDto
|
||||
redirect_url: str
|
||||
# [/DEF:LaunchDatasetResponse:Class]
|
||||
|
||||
|
||||
@@ -560,6 +567,8 @@ def _map_candidate_provenance(candidate: SemanticCandidate) -> FieldProvenance:
|
||||
# [DEF:_resolve_candidate_source_version:Function]
|
||||
# @COMPLEXITY: 3
|
||||
# @PURPOSE: Resolve the semantic source version for one accepted candidate from the loaded session aggregate.
|
||||
# @RELATION: [DEPENDS_ON] ->[SemanticFieldEntry]
|
||||
# @RELATION: [DEPENDS_ON] ->[SemanticSource]
|
||||
def _resolve_candidate_source_version(field: SemanticFieldEntry, source_id: Optional[str]) -> Optional[str]:
|
||||
if not source_id:
|
||||
return None
|
||||
@@ -661,6 +670,27 @@ def _serialize_run_context(run_context) -> DatasetRunContextDto:
|
||||
# [/DEF:_serialize_run_context:Function]
|
||||
|
||||
|
||||
# [DEF:_build_sql_lab_redirect_url:Function]
|
||||
# @COMPLEXITY: 3
|
||||
# @PURPOSE: Build a stable SQL Lab redirect URL from the configured Superset environment and persisted run context reference.
|
||||
# @RELATION: [DEPENDS_ON] ->[DatasetRunContextDto]
|
||||
def _build_sql_lab_redirect_url(environment_url: str, sql_lab_session_ref: str) -> str:
|
||||
base_url = str(environment_url or "").rstrip("/")
|
||||
session_ref = str(sql_lab_session_ref or "").strip()
|
||||
if not base_url:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="Superset environment URL is not configured",
|
||||
)
|
||||
if not session_ref:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail="SQL Lab session reference is missing",
|
||||
)
|
||||
return f"{base_url}/superset/sqllab?queryId={session_ref}"
|
||||
# [/DEF:_build_sql_lab_redirect_url:Function]
|
||||
|
||||
|
||||
# [DEF:_build_documentation_export:Function]
|
||||
# @COMPLEXITY: 3
|
||||
# @PURPOSE: Produce session documentation export content from current persisted review state.
|
||||
@@ -1365,12 +1395,12 @@ async def approve_batch_semantic_fields(
|
||||
# @PURPOSE: Return the current mapping-review set for one accessible session.
|
||||
# @RELATION: [CALLS] ->[_get_owned_session_or_404]
|
||||
# @PRE: Session is ownership-accessible to the authenticated user and execution feature is enabled.
|
||||
# @POST: Returns the persisted mapping review set for the requested session without mutating approval state.
|
||||
# @POST: Returns the persisted mapping review set for the requested session wrapped in the contract collection shape without mutating approval state.
|
||||
# @SIDE_EFFECT: none.
|
||||
# @DATA_CONTRACT: Input[session_id:str] -> Output[List[ExecutionMappingDto]]
|
||||
# @DATA_CONTRACT: Input[session_id:str] -> Output[MappingCollectionResponse]
|
||||
@router.get(
|
||||
"/sessions/{session_id}/mappings",
|
||||
response_model=List[ExecutionMappingDto],
|
||||
response_model=MappingCollectionResponse,
|
||||
dependencies=[
|
||||
Depends(_require_auto_review_flag),
|
||||
Depends(_require_execution_flag),
|
||||
@@ -1384,7 +1414,9 @@ async def list_execution_mappings(
|
||||
):
|
||||
with belief_scope("dataset_review.list_execution_mappings"):
|
||||
session = _get_owned_session_or_404(repository, session_id, current_user)
|
||||
return [_serialize_execution_mapping(item) for item in session.execution_mappings]
|
||||
return MappingCollectionResponse(
|
||||
items=[_serialize_execution_mapping(item) for item in session.execution_mappings]
|
||||
)
|
||||
# [/DEF:list_execution_mappings:Function]
|
||||
|
||||
|
||||
@@ -1579,12 +1611,12 @@ async def approve_batch_execution_mappings(
|
||||
# @PURPOSE: Trigger Superset-side preview compilation for the current owned execution context.
|
||||
# @RELATION: [CALLS] ->[DatasetReviewOrchestrator.prepare_launch_preview]
|
||||
# @PRE: Session belongs to the current owner and required mapping inputs are available.
|
||||
# @POST: Returns the persisted preview artifact produced from Superset or a deterministic conflict error.
|
||||
# @POST: Returns the compiled preview directly for synchronous success or enqueue-state shape when preview generation remains pending.
|
||||
# @SIDE_EFFECT: Persists preview attempt and updates readiness state.
|
||||
# @DATA_CONTRACT: Input[session_id:str] -> Output[PreviewEnqueueResultResponse]
|
||||
# @DATA_CONTRACT: Input[session_id:str] -> Output[CompiledPreviewDto | PreviewEnqueueResultResponse]
|
||||
@router.post(
|
||||
"/sessions/{session_id}/preview",
|
||||
response_model=PreviewEnqueueResultResponse,
|
||||
response_model=Union[CompiledPreviewDto, PreviewEnqueueResultResponse],
|
||||
dependencies=[
|
||||
Depends(_require_auto_review_flag),
|
||||
Depends(_require_execution_flag),
|
||||
@@ -1593,6 +1625,7 @@ async def approve_batch_execution_mappings(
|
||||
)
|
||||
async def trigger_preview_generation(
|
||||
session_id: str,
|
||||
response: Response,
|
||||
orchestrator: DatasetReviewOrchestrator = Depends(_get_orchestrator),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
@@ -1613,12 +1646,16 @@ async def trigger_preview_generation(
|
||||
)
|
||||
raise HTTPException(status_code=status_code, detail=detail) from exc
|
||||
|
||||
return PreviewEnqueueResultResponse(
|
||||
session_id=result.session.session_id,
|
||||
preview_status=result.preview.preview_status.value,
|
||||
task_id=None,
|
||||
preview=CompiledPreviewDto.model_validate(result.preview, from_attributes=True),
|
||||
)
|
||||
if result.preview.preview_status == PreviewStatus.PENDING:
|
||||
response.status_code = status.HTTP_202_ACCEPTED
|
||||
return PreviewEnqueueResultResponse(
|
||||
session_id=result.session.session_id,
|
||||
preview_status=result.preview.preview_status.value,
|
||||
task_id=None,
|
||||
)
|
||||
|
||||
response.status_code = status.HTTP_200_OK
|
||||
return CompiledPreviewDto.model_validate(result.preview, from_attributes=True)
|
||||
# [/DEF:trigger_preview_generation:Function]
|
||||
|
||||
|
||||
@@ -1627,21 +1664,23 @@ async def trigger_preview_generation(
|
||||
# @PURPOSE: Execute the current owned session launch handoff through the orchestrator and return audited SQL Lab run context.
|
||||
# @RELATION: [CALLS] ->[DatasetReviewOrchestrator.launch_dataset]
|
||||
# @PRE: Session belongs to the current owner, execution feature is enabled, and launch gates are satisfied or a deterministic conflict is returned.
|
||||
# @POST: Returns updated session summary plus persisted run context when launch handoff is accepted.
|
||||
# @POST: Returns persisted run context plus redirect URL when launch handoff is accepted.
|
||||
# @SIDE_EFFECT: Persists launch audit snapshot and may trigger SQL Lab session creation.
|
||||
# @DATA_CONTRACT: Input[session_id:str] -> Output[LaunchDatasetResponse]
|
||||
@router.post(
|
||||
"/sessions/{session_id}/launch",
|
||||
response_model=LaunchDatasetResponse,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
dependencies=[
|
||||
Depends(_require_auto_review_flag),
|
||||
Depends(_require_execution_flag),
|
||||
Depends(has_permission("dataset:session", "MANAGE")),
|
||||
Depends(has_permission("dataset:execution:launch", "EXECUTE")),
|
||||
],
|
||||
)
|
||||
async def launch_dataset(
|
||||
session_id: str,
|
||||
orchestrator: DatasetReviewOrchestrator = Depends(_get_orchestrator),
|
||||
config_manager=Depends(get_config_manager),
|
||||
current_user: User = Depends(get_current_user),
|
||||
):
|
||||
with belief_scope("dataset_review.launch_dataset"):
|
||||
@@ -1663,9 +1702,14 @@ async def launch_dataset(
|
||||
)
|
||||
raise HTTPException(status_code=status_code, detail=detail) from exc
|
||||
|
||||
environment = config_manager.get_environment(result.session.environment_id)
|
||||
environment_url = getattr(environment, "url", "") if environment is not None else ""
|
||||
return LaunchDatasetResponse(
|
||||
session=_serialize_session_summary(result.session),
|
||||
run_context=_serialize_run_context(result.run_context),
|
||||
redirect_url=_build_sql_lab_redirect_url(
|
||||
environment_url=environment_url,
|
||||
sql_lab_session_ref=result.run_context.sql_lab_session_ref,
|
||||
),
|
||||
)
|
||||
# [/DEF:launch_dataset:Function]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user