Add governed reusable pipelines and triggers
This commit is contained in:
@@ -5,6 +5,7 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from govoplan_core.audit.logging import audit_event
|
||||
from govoplan_core.auth import ApiPrincipal, get_api_principal, has_scope
|
||||
from govoplan_core.core.automation import AutomationInvocation
|
||||
from govoplan_core.core.dataflows import (
|
||||
DataflowPublicationTarget,
|
||||
DataflowRunRequest,
|
||||
@@ -24,13 +25,27 @@ from govoplan_core.core.tabular_sources import (
|
||||
parse_tabular_csv,
|
||||
)
|
||||
from govoplan_core.db.session import get_session
|
||||
from govoplan_dataflow.backend.manifest import ADMIN_SCOPE, READ_SCOPE, RUN_SCOPE, WRITE_SCOPE
|
||||
from govoplan_dataflow.backend.governance import (
|
||||
definition_decision,
|
||||
normalize_definition_scope,
|
||||
require_definition_action,
|
||||
)
|
||||
from govoplan_dataflow.backend.manifest import (
|
||||
ADMIN_SCOPE,
|
||||
READ_SCOPE,
|
||||
RUN_SCOPE,
|
||||
TRIGGER_DISPATCH_SCOPE,
|
||||
TRIGGER_READ_SCOPE,
|
||||
TRIGGER_WRITE_SCOPE,
|
||||
WRITE_SCOPE,
|
||||
)
|
||||
from govoplan_dataflow.backend.node_library import CATEGORY_LABELS, NODE_LIBRARY
|
||||
from govoplan_dataflow.backend.runtime import get_registry
|
||||
from govoplan_dataflow.backend.schemas import (
|
||||
NodeLibraryResponse,
|
||||
NodeTypeDefinitionResponse,
|
||||
PipelineCreateRequest,
|
||||
PipelineDeriveRequest,
|
||||
PipelineDeleteResponse,
|
||||
PipelineDraftRequest,
|
||||
PipelineListResponse,
|
||||
@@ -47,6 +62,14 @@ from govoplan_dataflow.backend.schemas import (
|
||||
TabularSourceColumnResponse,
|
||||
TabularSourceListResponse,
|
||||
TabularSourceResponse,
|
||||
DataflowEventDeliveryRequest,
|
||||
DataflowTriggerCreateRequest,
|
||||
DataflowTriggerDeliveryListResponse,
|
||||
DataflowTriggerDeleteResponse,
|
||||
DataflowTriggerDispatchResponse,
|
||||
DataflowTriggerListResponse,
|
||||
DataflowTriggerResponse,
|
||||
DataflowTriggerUpdateRequest,
|
||||
)
|
||||
from govoplan_dataflow.backend.service import (
|
||||
DataflowConflictError,
|
||||
@@ -55,6 +78,7 @@ from govoplan_dataflow.backend.service import (
|
||||
DataflowValidationError,
|
||||
compile_sql_draft,
|
||||
create_pipeline,
|
||||
derive_pipeline,
|
||||
cancel_pipeline_run,
|
||||
delete_pipeline,
|
||||
get_pipeline,
|
||||
@@ -69,6 +93,17 @@ from govoplan_dataflow.backend.service import (
|
||||
update_pipeline,
|
||||
validate_draft,
|
||||
)
|
||||
from govoplan_dataflow.backend.triggers import (
|
||||
create_trigger,
|
||||
delete_trigger,
|
||||
delivery_response,
|
||||
dispatch_due_triggers,
|
||||
ingest_event_delivery,
|
||||
list_deliveries,
|
||||
list_triggers,
|
||||
trigger_response,
|
||||
update_trigger,
|
||||
)
|
||||
|
||||
|
||||
router = APIRouter(prefix="/dataflow", tags=["dataflow"])
|
||||
@@ -108,6 +143,30 @@ def _http_error(exc: DataflowError) -> HTTPException:
|
||||
return HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(exc))
|
||||
|
||||
|
||||
def _governance_http_error(exc: PermissionError | ValueError) -> HTTPException:
|
||||
return HTTPException(
|
||||
status_code=(
|
||||
status.HTTP_403_FORBIDDEN
|
||||
if isinstance(exc, PermissionError)
|
||||
else status.HTTP_422_UNPROCESSABLE_CONTENT
|
||||
),
|
||||
detail=str(exc),
|
||||
)
|
||||
|
||||
|
||||
def _pipeline_response(
|
||||
session: Session,
|
||||
pipeline,
|
||||
principal: ApiPrincipal,
|
||||
) -> PipelineResponse:
|
||||
return pipeline_response(
|
||||
session,
|
||||
pipeline,
|
||||
principal=principal,
|
||||
registry=get_registry(),
|
||||
)
|
||||
|
||||
|
||||
def _source_http_error(exc: DatasourceError) -> HTTPException:
|
||||
if isinstance(exc, DatasourceAccessError):
|
||||
return HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=str(exc))
|
||||
@@ -317,8 +376,27 @@ def api_list_pipelines(
|
||||
) -> PipelineListResponse:
|
||||
_require_any_scope(principal, READ_SCOPE, ADMIN_SCOPE)
|
||||
pipelines = list_pipelines(session, tenant_id=principal.tenant_id)
|
||||
registry = get_registry()
|
||||
visible = [
|
||||
pipeline
|
||||
for pipeline in pipelines
|
||||
if definition_decision(
|
||||
pipeline,
|
||||
principal=principal,
|
||||
registry=registry,
|
||||
action="view",
|
||||
).allowed
|
||||
]
|
||||
return PipelineListResponse(
|
||||
pipelines=[pipeline_response(session, pipeline) for pipeline in pipelines]
|
||||
pipelines=[
|
||||
pipeline_response(
|
||||
session,
|
||||
pipeline,
|
||||
principal=principal,
|
||||
registry=registry,
|
||||
)
|
||||
for pipeline in visible
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -330,12 +408,23 @@ def api_create_pipeline(
|
||||
) -> PipelineResponse:
|
||||
_require_any_scope(principal, WRITE_SCOPE, ADMIN_SCOPE)
|
||||
try:
|
||||
tenant_id, scope_type, scope_id = normalize_definition_scope(
|
||||
principal,
|
||||
scope_type=payload.scope_type,
|
||||
scope_id=payload.scope_id,
|
||||
administrative=has_scope(principal, ADMIN_SCOPE),
|
||||
)
|
||||
payload = payload.model_copy(
|
||||
update={"scope_type": scope_type, "scope_id": scope_id}
|
||||
)
|
||||
pipeline = create_pipeline(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
tenant_id=tenant_id or principal.tenant_id,
|
||||
actor_id=_actor_id(principal),
|
||||
payload=payload,
|
||||
)
|
||||
except (PermissionError, ValueError) as exc:
|
||||
raise _governance_http_error(exc) from exc
|
||||
except DataflowError as exc:
|
||||
raise _http_error(exc) from exc
|
||||
audit_event(
|
||||
@@ -348,7 +437,7 @@ def api_create_pipeline(
|
||||
object_id=pipeline.id,
|
||||
details={"revision": pipeline.current_revision, "status": pipeline.status},
|
||||
)
|
||||
response = pipeline_response(session, pipeline)
|
||||
response = _pipeline_response(session, pipeline, principal)
|
||||
session.commit()
|
||||
return response
|
||||
|
||||
@@ -366,7 +455,15 @@ def api_get_pipeline(
|
||||
tenant_id=principal.tenant_id,
|
||||
pipeline_id=pipeline_id,
|
||||
)
|
||||
return pipeline_response(session, pipeline)
|
||||
require_definition_action(
|
||||
pipeline,
|
||||
principal=principal,
|
||||
registry=get_registry(),
|
||||
action="view",
|
||||
)
|
||||
return _pipeline_response(session, pipeline, principal)
|
||||
except PermissionError as exc:
|
||||
raise _governance_http_error(exc) from exc
|
||||
except DataflowError as exc:
|
||||
raise _http_error(exc) from exc
|
||||
|
||||
@@ -380,6 +477,17 @@ def api_update_pipeline(
|
||||
) -> PipelineResponse:
|
||||
_require_any_scope(principal, WRITE_SCOPE, ADMIN_SCOPE)
|
||||
try:
|
||||
existing = get_pipeline(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
pipeline_id=pipeline_id,
|
||||
)
|
||||
require_definition_action(
|
||||
existing,
|
||||
principal=principal,
|
||||
registry=get_registry(),
|
||||
action="edit",
|
||||
)
|
||||
pipeline = update_pipeline(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
@@ -387,6 +495,8 @@ def api_update_pipeline(
|
||||
actor_id=_actor_id(principal),
|
||||
payload=payload,
|
||||
)
|
||||
except PermissionError as exc:
|
||||
raise _governance_http_error(exc) from exc
|
||||
except DataflowError as exc:
|
||||
raise _http_error(exc) from exc
|
||||
audit_event(
|
||||
@@ -399,7 +509,7 @@ def api_update_pipeline(
|
||||
object_id=pipeline.id,
|
||||
details={"revision": pipeline.current_revision, "status": pipeline.status},
|
||||
)
|
||||
response = pipeline_response(session, pipeline)
|
||||
response = _pipeline_response(session, pipeline, principal)
|
||||
session.commit()
|
||||
return response
|
||||
|
||||
@@ -412,12 +522,25 @@ def api_delete_pipeline(
|
||||
) -> PipelineDeleteResponse:
|
||||
_require_any_scope(principal, WRITE_SCOPE, ADMIN_SCOPE)
|
||||
try:
|
||||
existing = get_pipeline(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
pipeline_id=pipeline_id,
|
||||
)
|
||||
require_definition_action(
|
||||
existing,
|
||||
principal=principal,
|
||||
registry=get_registry(),
|
||||
action="edit",
|
||||
)
|
||||
pipeline = delete_pipeline(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
pipeline_id=pipeline_id,
|
||||
actor_id=_actor_id(principal),
|
||||
)
|
||||
except PermissionError as exc:
|
||||
raise _governance_http_error(exc) from exc
|
||||
except DataflowError as exc:
|
||||
raise _http_error(exc) from exc
|
||||
audit_event(
|
||||
@@ -434,6 +557,314 @@ def api_delete_pipeline(
|
||||
return PipelineDeleteResponse(deleted=True, pipeline_id=pipeline.id)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/pipelines/{pipeline_id}/derive",
|
||||
response_model=PipelineResponse,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
)
|
||||
def api_derive_pipeline(
|
||||
pipeline_id: str,
|
||||
payload: PipelineDeriveRequest,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> PipelineResponse:
|
||||
_require_any_scope(principal, WRITE_SCOPE, ADMIN_SCOPE)
|
||||
try:
|
||||
tenant_id, scope_type, scope_id = normalize_definition_scope(
|
||||
principal,
|
||||
scope_type=payload.scope_type,
|
||||
scope_id=payload.scope_id,
|
||||
administrative=has_scope(principal, ADMIN_SCOPE),
|
||||
)
|
||||
payload = payload.model_copy(
|
||||
update={"scope_type": scope_type, "scope_id": scope_id}
|
||||
)
|
||||
pipeline = derive_pipeline(
|
||||
session,
|
||||
tenant_id=tenant_id or principal.tenant_id,
|
||||
actor_id=_actor_id(principal),
|
||||
principal=principal,
|
||||
registry=get_registry(),
|
||||
source_pipeline_id=pipeline_id,
|
||||
payload=payload,
|
||||
)
|
||||
except (PermissionError, ValueError) as exc:
|
||||
raise _governance_http_error(exc) from exc
|
||||
except DataflowError as exc:
|
||||
raise _http_error(exc) from exc
|
||||
audit_event(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
user_id=getattr(principal.user, "id", None),
|
||||
api_key_id=principal.api_key_id,
|
||||
action="dataflow.pipeline.derived",
|
||||
object_type="dataflow_pipeline",
|
||||
object_id=pipeline.id,
|
||||
details={
|
||||
"source_pipeline_id": pipeline.derived_from_pipeline_id,
|
||||
"source_revision": pipeline.derived_from_revision,
|
||||
"source_hash": pipeline.derived_from_hash,
|
||||
"scope_type": pipeline.scope_type,
|
||||
"scope_id": pipeline.scope_id,
|
||||
},
|
||||
)
|
||||
response = _pipeline_response(session, pipeline, principal)
|
||||
session.commit()
|
||||
return response
|
||||
|
||||
|
||||
@router.get(
|
||||
"/pipelines/{pipeline_id}/triggers",
|
||||
response_model=DataflowTriggerListResponse,
|
||||
)
|
||||
def api_list_triggers(
|
||||
pipeline_id: str,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> DataflowTriggerListResponse:
|
||||
_require_any_scope(principal, TRIGGER_READ_SCOPE, ADMIN_SCOPE)
|
||||
try:
|
||||
pipeline = get_pipeline(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
pipeline_id=pipeline_id,
|
||||
)
|
||||
require_definition_action(
|
||||
pipeline,
|
||||
principal=principal,
|
||||
registry=get_registry(),
|
||||
action="view",
|
||||
)
|
||||
items = list_triggers(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
pipeline_id=pipeline_id,
|
||||
)
|
||||
return DataflowTriggerListResponse(
|
||||
triggers=[trigger_response(session, item) for item in items]
|
||||
)
|
||||
except PermissionError as exc:
|
||||
raise _governance_http_error(exc) from exc
|
||||
except DataflowError as exc:
|
||||
raise _http_error(exc) from exc
|
||||
|
||||
|
||||
@router.post(
|
||||
"/pipelines/{pipeline_id}/triggers",
|
||||
response_model=DataflowTriggerResponse,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
)
|
||||
def api_create_trigger(
|
||||
pipeline_id: str,
|
||||
payload: DataflowTriggerCreateRequest,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> DataflowTriggerResponse:
|
||||
_require_any_scope(principal, TRIGGER_WRITE_SCOPE, ADMIN_SCOPE)
|
||||
try:
|
||||
trigger = create_trigger(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
pipeline_id=pipeline_id,
|
||||
actor_id=_actor_id(principal),
|
||||
principal=principal,
|
||||
registry=get_registry(),
|
||||
payload=payload,
|
||||
)
|
||||
except DataflowError as exc:
|
||||
raise _http_error(exc) from exc
|
||||
audit_event(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
user_id=getattr(principal.user, "id", None),
|
||||
api_key_id=principal.api_key_id,
|
||||
action="dataflow.trigger.created",
|
||||
object_type="dataflow_trigger",
|
||||
object_id=trigger.id,
|
||||
details={
|
||||
"pipeline_id": trigger.pipeline_id,
|
||||
"kind": trigger.kind,
|
||||
"status": trigger.status,
|
||||
"grant_scopes": list(trigger.grant_scopes),
|
||||
},
|
||||
)
|
||||
response = trigger_response(session, trigger)
|
||||
session.commit()
|
||||
return response
|
||||
|
||||
|
||||
@router.put(
|
||||
"/triggers/{trigger_id}",
|
||||
response_model=DataflowTriggerResponse,
|
||||
)
|
||||
def api_update_trigger(
|
||||
trigger_id: str,
|
||||
payload: DataflowTriggerUpdateRequest,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> DataflowTriggerResponse:
|
||||
_require_any_scope(principal, TRIGGER_WRITE_SCOPE, ADMIN_SCOPE)
|
||||
try:
|
||||
trigger = update_trigger(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
trigger_id=trigger_id,
|
||||
actor_id=_actor_id(principal),
|
||||
principal=principal,
|
||||
registry=get_registry(),
|
||||
payload=payload,
|
||||
)
|
||||
except DataflowError as exc:
|
||||
raise _http_error(exc) from exc
|
||||
audit_event(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
user_id=getattr(principal.user, "id", None),
|
||||
api_key_id=principal.api_key_id,
|
||||
action="dataflow.trigger.updated",
|
||||
object_type="dataflow_trigger",
|
||||
object_id=trigger.id,
|
||||
details={
|
||||
"pipeline_id": trigger.pipeline_id,
|
||||
"kind": trigger.kind,
|
||||
"status": trigger.status,
|
||||
"revision": trigger.revision,
|
||||
"grant_scopes": list(trigger.grant_scopes),
|
||||
},
|
||||
)
|
||||
response = trigger_response(session, trigger)
|
||||
session.commit()
|
||||
return response
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/triggers/{trigger_id}",
|
||||
response_model=DataflowTriggerDeleteResponse,
|
||||
)
|
||||
def api_delete_trigger(
|
||||
trigger_id: str,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> DataflowTriggerDeleteResponse:
|
||||
_require_any_scope(principal, TRIGGER_WRITE_SCOPE, ADMIN_SCOPE)
|
||||
try:
|
||||
trigger = delete_trigger(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
trigger_id=trigger_id,
|
||||
)
|
||||
except DataflowError as exc:
|
||||
raise _http_error(exc) from exc
|
||||
audit_event(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
user_id=getattr(principal.user, "id", None),
|
||||
api_key_id=principal.api_key_id,
|
||||
action="dataflow.trigger.deleted",
|
||||
object_type="dataflow_trigger",
|
||||
object_id=trigger.id,
|
||||
details={"pipeline_id": trigger.pipeline_id},
|
||||
)
|
||||
session.commit()
|
||||
return DataflowTriggerDeleteResponse(
|
||||
deleted=True,
|
||||
trigger_id=trigger.id,
|
||||
pipeline_id=trigger.pipeline_id,
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/triggers/{trigger_id}/deliveries",
|
||||
response_model=DataflowTriggerDeliveryListResponse,
|
||||
)
|
||||
def api_list_trigger_deliveries(
|
||||
trigger_id: str,
|
||||
limit: int = 50,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> DataflowTriggerDeliveryListResponse:
|
||||
_require_any_scope(principal, TRIGGER_READ_SCOPE, ADMIN_SCOPE)
|
||||
try:
|
||||
items = list_deliveries(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
trigger_id=trigger_id,
|
||||
limit=limit,
|
||||
)
|
||||
except DataflowError as exc:
|
||||
raise _http_error(exc) from exc
|
||||
return DataflowTriggerDeliveryListResponse(
|
||||
deliveries=[delivery_response(item) for item in items]
|
||||
)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/triggers/events",
|
||||
response_model=DataflowTriggerDispatchResponse,
|
||||
)
|
||||
def api_ingest_trigger_event(
|
||||
payload: DataflowEventDeliveryRequest,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> DataflowTriggerDispatchResponse:
|
||||
_require_any_scope(principal, TRIGGER_DISPATCH_SCOPE, ADMIN_SCOPE)
|
||||
try:
|
||||
queued = ingest_event_delivery(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
event=payload,
|
||||
)
|
||||
except DataflowError as exc:
|
||||
raise _http_error(exc) from exc
|
||||
audit_event(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
user_id=getattr(principal.user, "id", None),
|
||||
api_key_id=principal.api_key_id,
|
||||
action="dataflow.trigger.event_ingested",
|
||||
object_type="platform_event",
|
||||
object_id=payload.event_id,
|
||||
details={
|
||||
"event_type": payload.type,
|
||||
"module_id": payload.module_id,
|
||||
"classification": payload.classification,
|
||||
"queued": queued,
|
||||
},
|
||||
)
|
||||
session.commit()
|
||||
return DataflowTriggerDispatchResponse(queued=queued)
|
||||
|
||||
|
||||
@router.post(
|
||||
"/triggers/dispatch",
|
||||
response_model=DataflowTriggerDispatchResponse,
|
||||
)
|
||||
def api_dispatch_triggers(
|
||||
limit: int = 50,
|
||||
session: Session = Depends(get_session),
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> DataflowTriggerDispatchResponse:
|
||||
_require_any_scope(principal, TRIGGER_DISPATCH_SCOPE, ADMIN_SCOPE)
|
||||
result = dispatch_due_triggers(
|
||||
session,
|
||||
registry=get_registry(),
|
||||
limit=limit,
|
||||
tenant_id=principal.tenant_id,
|
||||
)
|
||||
audit_event(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
user_id=getattr(principal.user, "id", None),
|
||||
api_key_id=principal.api_key_id,
|
||||
action="dataflow.trigger.dispatched",
|
||||
object_type="dataflow_trigger_dispatch",
|
||||
object_id=principal.tenant_id,
|
||||
details=result.model_dump(mode="json"),
|
||||
)
|
||||
session.commit()
|
||||
return result
|
||||
|
||||
|
||||
@router.get(
|
||||
"/pipelines/{pipeline_id}/runs",
|
||||
response_model=PipelineRunListResponse,
|
||||
@@ -446,6 +877,17 @@ def api_list_pipeline_runs(
|
||||
) -> PipelineRunListResponse:
|
||||
_require_any_scope(principal, READ_SCOPE, RUN_SCOPE, ADMIN_SCOPE)
|
||||
try:
|
||||
pipeline = get_pipeline(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
pipeline_id=pipeline_id,
|
||||
)
|
||||
require_definition_action(
|
||||
pipeline,
|
||||
principal=principal,
|
||||
registry=get_registry(),
|
||||
action="view",
|
||||
)
|
||||
runs = list_pipeline_runs(
|
||||
session,
|
||||
tenant_id=principal.tenant_id,
|
||||
@@ -455,6 +897,8 @@ def api_list_pipeline_runs(
|
||||
return PipelineRunListResponse(
|
||||
runs=[pipeline_run_response(session, run) for run in runs]
|
||||
)
|
||||
except PermissionError as exc:
|
||||
raise _governance_http_error(exc) from exc
|
||||
except DataflowError as exc:
|
||||
raise _http_error(exc) from exc
|
||||
|
||||
@@ -471,6 +915,8 @@ def api_start_pipeline_run(
|
||||
principal: ApiPrincipal = Depends(get_api_principal),
|
||||
) -> PipelineRunResponse:
|
||||
_require_any_scope(principal, RUN_SCOPE, ADMIN_SCOPE)
|
||||
if payload.invocation_kind == "backfill":
|
||||
_require_any_scope(principal, ADMIN_SCOPE)
|
||||
try:
|
||||
pipeline = get_pipeline(
|
||||
session,
|
||||
@@ -503,6 +949,16 @@ def api_start_pipeline_run(
|
||||
idempotency_key=payload.idempotency_key,
|
||||
row_limit=payload.row_limit,
|
||||
publication=publication,
|
||||
invocation=AutomationInvocation(
|
||||
kind=(
|
||||
"backfill"
|
||||
if payload.invocation_kind == "backfill"
|
||||
else "api"
|
||||
if principal.auth_method == "api_key"
|
||||
else "manual"
|
||||
),
|
||||
requested_by=_actor_id(principal),
|
||||
),
|
||||
),
|
||||
)
|
||||
except DataflowError as exc:
|
||||
@@ -523,6 +979,7 @@ def api_start_pipeline_run(
|
||||
"pipeline_id": run.pipeline_id,
|
||||
"revision_id": run.pipeline_revision_id,
|
||||
"status": run.status,
|
||||
"invocation_kind": run.invocation_kind,
|
||||
"output_datasource_ref": run.output_datasource_ref,
|
||||
"output_materialization_ref": run.output_materialization_ref,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user