Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Schedule run history issues #537

Merged
merged 2 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 24 additions & 3 deletions frontend/src/components/executions/nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,12 @@ export function WorkflowExecutionNav({
{workflowExecutions.map((execution, index) => (
<HoverCard openDelay={10} closeDelay={10} key={index}>
<Link
href={`${baseUrl}/executions/${execution.id.split(":")[1]}`}
href={`${baseUrl}/executions/${parseExecutionId(execution.id)[1]}`}
className={cn(
buttonVariants({ variant: "default", size: "sm" }),
"justify-start bg-background text-muted-foreground shadow-none hover:cursor-default hover:bg-gray-100",
execution.id.split(":")[1] === executionId && "bg-gray-200"
parseExecutionId(execution.id)[1] === executionId &&
"bg-gray-200"
)}
>
<div className="flex items-center">
Expand Down Expand Up @@ -152,7 +153,7 @@ export function WorkflowExecutionNav({
<Label className="text-xs text-muted-foreground">
Execution ID
</Label>
<span>{execution.id.split(":")[1]}</span>
<span>{parseExecutionId(execution.id)[1]}</span>
</div>
<div className="flex flex-col">
<Label className="text-xs text-muted-foreground">
Expand Down Expand Up @@ -238,3 +239,23 @@ export function getExecutionStatusIcon(
throw new Error("Invalid status")
}
}

/**
* Get the execution ID from a full execution ID
* @param fullExecutionId
* @returns the execution ID
*
* Example:
* - "wf-123:1234567890" -> ["wf-123", "1234567890"]
* - "wf-123:1234567890:1" -> ["wf-123", "1234567890:1"]
*/
function parseExecutionId(fullExecutionId: string): [string, string] {
// Split at most once from the left, keeping any remaining colons in the second part
const splitIndex = fullExecutionId.indexOf(":")
if (splitIndex === -1) {
throw new Error("Invalid execution ID format - missing colon separator")
}
const workflowId = fullExecutionId.slice(0, splitIndex)
const executionId = fullExecutionId.slice(splitIndex + 1)
return [workflowId, executionId]
}
14 changes: 14 additions & 0 deletions tracecat/workflow/executions/dependencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import urllib.parse
from typing import Annotated

from fastapi import Depends

from tracecat.workflow.executions.models import ExecutionOrScheduleID


def unquote_dep(execution_id: ExecutionOrScheduleID) -> ExecutionOrScheduleID:
return urllib.parse.unquote(execution_id)


UnquotedExecutionOrScheduleID = Annotated[ExecutionOrScheduleID, Depends(unquote_dep)]
"""Dependency for an unquoted execution or schedule ID."""
4 changes: 3 additions & 1 deletion tracecat/workflow/executions/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
RunActionInput,
TriggerInputs,
)
from tracecat.identifiers import WorkflowExecutionID, WorkflowID
from tracecat.identifiers import WorkflowExecutionID, WorkflowID, WorkflowScheduleID
from tracecat.types.auth import Role
from tracecat.workflow.management.models import GetWorkflowDefinitionActivityInputs

Expand All @@ -35,6 +35,8 @@
]
"""Mapped literal types for workflow execution statuses."""

ExecutionOrScheduleID = WorkflowExecutionID | WorkflowScheduleID


class EventHistoryType(StrEnum):
"""The event types we care about."""
Expand Down
13 changes: 7 additions & 6 deletions tracecat/workflow/executions/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@
from sqlmodel import select
from sqlmodel.ext.asyncio.session import AsyncSession

from tracecat import identifiers
from tracecat.auth.credentials import authenticate_user_for_workspace
from tracecat.db.engine import get_async_session
from tracecat.db.schemas import WorkflowDefinition
from tracecat.dsl.common import DSLInput
from tracecat.identifiers import WorkflowID
from tracecat.logger import logger
from tracecat.types.auth import Role
from tracecat.types.exceptions import TracecatValidationError
from tracecat.workflow.executions.dependencies import UnquotedExecutionOrScheduleID
from tracecat.workflow.executions.models import (
CreateWorkflowExecutionParams,
CreateWorkflowExecutionResponse,
Expand All @@ -36,7 +37,7 @@
async def list_workflow_executions(
role: Annotated[Role, Depends(authenticate_user_for_workspace)],
# Filters
workflow_id: identifiers.WorkflowID | None = Query(None),
workflow_id: WorkflowID | None = Query(None),
) -> list[WorkflowExecutionResponse]:
"""List all workflow executions."""
with logger.contextualize(role=role):
Expand All @@ -54,7 +55,7 @@ async def list_workflow_executions(
@router.get("/{execution_id}", tags=["workflow-executions"])
async def get_workflow_execution(
role: Annotated[Role, Depends(authenticate_user_for_workspace)],
execution_id: identifiers.WorkflowExecutionID | identifiers.WorkflowScheduleID,
execution_id: UnquotedExecutionOrScheduleID,
) -> WorkflowExecutionResponse:
"""Get a workflow execution."""
with logger.contextualize(role=role):
Expand All @@ -66,7 +67,7 @@ async def get_workflow_execution(
@router.get("/{execution_id}/history", tags=["workflow-executions"])
async def list_workflow_execution_event_history(
role: Annotated[Role, Depends(authenticate_user_for_workspace)],
execution_id: identifiers.WorkflowExecutionID | identifiers.WorkflowScheduleID,
execution_id: UnquotedExecutionOrScheduleID,
) -> list[EventHistoryResponse]:
"""Get a workflow execution."""
with logger.contextualize(role=role):
Expand Down Expand Up @@ -126,7 +127,7 @@ async def create_workflow_execution(
)
async def cancel_workflow_execution(
role: Annotated[Role, Depends(authenticate_user_for_workspace)],
execution_id: identifiers.WorkflowExecutionID | identifiers.WorkflowScheduleID,
execution_id: UnquotedExecutionOrScheduleID,
) -> None:
"""Get a workflow execution."""
with logger.contextualize(role=role):
Expand All @@ -150,7 +151,7 @@ async def cancel_workflow_execution(
)
async def terminate_workflow_execution(
role: Annotated[Role, Depends(authenticate_user_for_workspace)],
execution_id: identifiers.WorkflowExecutionID | identifiers.WorkflowScheduleID,
execution_id: UnquotedExecutionOrScheduleID,
params: TerminateWorkflowExecutionParams,
) -> None:
"""Get a workflow execution."""
Expand Down
Loading