feat: govern connector configurations and simulations
This commit is contained in:
@@ -48,6 +48,30 @@ from govoplan_connectors.backend.schemas import (
|
||||
TabularSourceResponse,
|
||||
)
|
||||
from govoplan_connectors.backend.feeds import ConnectorFeedProvider, feed_rows
|
||||
from govoplan_connectors.backend.governed_runtime import (
|
||||
GovernedConnectorError,
|
||||
create_configuration,
|
||||
execute_run,
|
||||
list_configurations,
|
||||
list_definitions,
|
||||
list_runs,
|
||||
review_run,
|
||||
update_configuration,
|
||||
upsert_definition,
|
||||
)
|
||||
from govoplan_connectors.backend.governed_schemas import (
|
||||
ConnectorConfigurationCreateRequest,
|
||||
ConnectorConfigurationItem,
|
||||
ConnectorConfigurationListResponse,
|
||||
ConnectorConfigurationUpdateRequest,
|
||||
ConnectorDefinitionItem,
|
||||
ConnectorDefinitionListResponse,
|
||||
ConnectorDefinitionUpsertRequest,
|
||||
ConnectorReviewRequest,
|
||||
ConnectorRunItem,
|
||||
ConnectorRunListResponse,
|
||||
ConnectorRunRequest,
|
||||
)
|
||||
from govoplan_connectors.backend.recovery import (
|
||||
ConnectorRecoveryError,
|
||||
begin_connector_read_snapshot,
|
||||
@@ -135,6 +159,26 @@ def _recovery_http_error(exc: ConnectorRecoveryError) -> HTTPException:
|
||||
)
|
||||
|
||||
|
||||
def _governed_http_error(exc: GovernedConnectorError) -> HTTPException:
|
||||
if exc.code.endswith("_not_found"):
|
||||
status_code = status.HTTP_404_NOT_FOUND
|
||||
elif exc.code in {
|
||||
"configuration_conflict",
|
||||
"configuration_disabled",
|
||||
"idempotency_conflict",
|
||||
"local_definition_protected",
|
||||
"package_definition_requires_overrides",
|
||||
"run_not_reviewable",
|
||||
}:
|
||||
status_code = status.HTTP_409_CONFLICT
|
||||
else:
|
||||
status_code = status.HTTP_422_UNPROCESSABLE_CONTENT
|
||||
return HTTPException(
|
||||
status_code=status_code,
|
||||
detail={"code": exc.code, "message": str(exc)},
|
||||
)
|
||||
|
||||
|
||||
@router.post("/feeds/preview", response_model=FeedDocumentResponse)
|
||||
def api_preview_feed(
|
||||
payload: FeedAcquireRequest,
|
||||
@@ -609,6 +653,199 @@ def api_list_sanctions_runs(
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/governed/definitions",
|
||||
response_model=ConnectorDefinitionListResponse,
|
||||
)
|
||||
def api_list_governed_definitions(
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> ConnectorDefinitionListResponse:
|
||||
_require_any_scope(principal, READ_SCOPE, ADMIN_SCOPE)
|
||||
try:
|
||||
items = list_definitions(session, tenant_id=principal.tenant_id)
|
||||
except GovernedConnectorError as exc:
|
||||
raise _governed_http_error(exc) from exc
|
||||
return ConnectorDefinitionListResponse(items=items)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/governed/definitions",
|
||||
response_model=ConnectorDefinitionItem,
|
||||
)
|
||||
def api_upsert_governed_definition(
|
||||
payload: ConnectorDefinitionUpsertRequest,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> ConnectorDefinitionItem:
|
||||
_require_any_scope(principal, ADMIN_SCOPE)
|
||||
try:
|
||||
return upsert_definition(session, principal, payload)
|
||||
except GovernedConnectorError as exc:
|
||||
session.rollback()
|
||||
raise _governed_http_error(exc) from exc
|
||||
|
||||
|
||||
@router.get(
|
||||
"/governed/configurations",
|
||||
response_model=ConnectorConfigurationListResponse,
|
||||
)
|
||||
def api_list_governed_configurations(
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> ConnectorConfigurationListResponse:
|
||||
_require_any_scope(principal, READ_SCOPE, ADMIN_SCOPE)
|
||||
try:
|
||||
items = list_configurations(session, tenant_id=principal.tenant_id)
|
||||
except GovernedConnectorError as exc:
|
||||
raise _governed_http_error(exc) from exc
|
||||
return ConnectorConfigurationListResponse(items=items)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/governed/configurations",
|
||||
response_model=ConnectorConfigurationItem,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
)
|
||||
def api_create_governed_configuration(
|
||||
payload: ConnectorConfigurationCreateRequest,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> ConnectorConfigurationItem:
|
||||
_require_any_scope(principal, ADMIN_SCOPE)
|
||||
try:
|
||||
return create_configuration(session, principal, payload)
|
||||
except GovernedConnectorError as exc:
|
||||
session.rollback()
|
||||
raise _governed_http_error(exc) from exc
|
||||
|
||||
|
||||
@router.put(
|
||||
"/governed/configurations/{configuration_id}",
|
||||
response_model=ConnectorConfigurationItem,
|
||||
)
|
||||
def api_update_governed_configuration(
|
||||
configuration_id: str,
|
||||
payload: ConnectorConfigurationUpdateRequest,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> ConnectorConfigurationItem:
|
||||
_require_any_scope(principal, ADMIN_SCOPE)
|
||||
try:
|
||||
return update_configuration(
|
||||
session,
|
||||
principal,
|
||||
configuration_id=configuration_id,
|
||||
payload=payload,
|
||||
)
|
||||
except GovernedConnectorError as exc:
|
||||
session.rollback()
|
||||
raise _governed_http_error(exc) from exc
|
||||
|
||||
|
||||
def _api_execute_governed_run(
|
||||
*,
|
||||
configuration_id: str,
|
||||
mode: str,
|
||||
payload: ConnectorRunRequest,
|
||||
session: Session,
|
||||
principal: ApiPrincipal,
|
||||
) -> ConnectorRunItem:
|
||||
_require_any_scope(principal, WRITE_SCOPE, ADMIN_SCOPE)
|
||||
try:
|
||||
return execute_run(
|
||||
session,
|
||||
principal,
|
||||
configuration_id=configuration_id,
|
||||
mode=mode,
|
||||
payload=payload,
|
||||
)
|
||||
except GovernedConnectorError as exc:
|
||||
session.rollback()
|
||||
raise _governed_http_error(exc) from exc
|
||||
|
||||
|
||||
@router.post(
|
||||
"/governed/configurations/{configuration_id}/dry-runs",
|
||||
response_model=ConnectorRunItem,
|
||||
)
|
||||
def api_dry_run_governed_configuration(
|
||||
configuration_id: str,
|
||||
payload: ConnectorRunRequest,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> ConnectorRunItem:
|
||||
return _api_execute_governed_run(
|
||||
configuration_id=configuration_id,
|
||||
mode="dry_run",
|
||||
payload=payload,
|
||||
session=session,
|
||||
principal=principal,
|
||||
)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/governed/configurations/{configuration_id}/simulations",
|
||||
response_model=ConnectorRunItem,
|
||||
)
|
||||
def api_simulate_governed_configuration(
|
||||
configuration_id: str,
|
||||
payload: ConnectorRunRequest,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> ConnectorRunItem:
|
||||
return _api_execute_governed_run(
|
||||
configuration_id=configuration_id,
|
||||
mode="simulation",
|
||||
payload=payload,
|
||||
session=session,
|
||||
principal=principal,
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/governed/runs",
|
||||
response_model=ConnectorRunListResponse,
|
||||
)
|
||||
def api_list_governed_runs(
|
||||
configuration_id: str | None = Query(default=None),
|
||||
review_state: str | None = Query(default=None),
|
||||
limit: int = Query(default=100, ge=1, le=500),
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> ConnectorRunListResponse:
|
||||
_require_any_scope(principal, READ_SCOPE, ADMIN_SCOPE)
|
||||
try:
|
||||
items = list_runs(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
configuration_id=configuration_id,
|
||||
review_state=review_state,
|
||||
limit=limit,
|
||||
)
|
||||
except GovernedConnectorError as exc:
|
||||
raise _governed_http_error(exc) from exc
|
||||
return ConnectorRunListResponse(items=items)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/governed/runs/{run_id}/review",
|
||||
response_model=ConnectorRunItem,
|
||||
)
|
||||
def api_review_governed_run(
|
||||
run_id: str,
|
||||
payload: ConnectorReviewRequest,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> ConnectorRunItem:
|
||||
_require_any_scope(principal, ADMIN_SCOPE)
|
||||
try:
|
||||
return review_run(session, principal, run_id=run_id, payload=payload)
|
||||
except GovernedConnectorError as exc:
|
||||
session.rollback()
|
||||
raise _governed_http_error(exc) from exc
|
||||
|
||||
|
||||
def _source_response(source: TabularSource) -> TabularSourceResponse:
|
||||
return TabularSourceResponse(
|
||||
ref=source.ref,
|
||||
|
||||
Reference in New Issue
Block a user