feat: fe overhaul api changes (#1639)

* api changes

* generate

* pkg

* backmerge main

* revert to main

* revert main
This commit is contained in:
Gabe Ruttner
2025-04-30 11:09:34 -04:00
committed by GitHub
parent d047813fd8
commit ccfc0c8d60
18 changed files with 1459 additions and 362 deletions
+13 -2
View File
@@ -53,6 +53,12 @@ tasks:
SERVER_INTERNAL_CLIENT_BASE_STRATEGY=none
SERVER_INTERNAL_CLIENT_BASE_INHERIT_BASE=false
EOF
pre:
cmds:
# FIXME: Remove this once we have a better way to handle pre-commit reliably
- pre-commit run --all-files
- pre-commit run --all-files
- pre-commit run --all-files
start-db:
cmds:
- docker compose up -d
@@ -185,5 +191,10 @@ tasks:
- pre-commit run --all-files || pre-commit run --all-files
docs:
cmds:
- npx serve -l 4001 &
- cd frontend/docs/ && pnpm run dev
- |
# Create a trap to kill the watcher when the task exits
trap 'kill $(jobs -p) 2>/dev/null' EXIT
npx chokidar "sdks/**/*.{py,ts,go}" --ignore "**/node_modules/**" --ignore "**/.git/**" --ignore "**/dist/**" --ignore "**/build/**" --initial -c "echo 'TODO: Add a task to generate and sync the docs'" &
cd frontend/docs/ && pnpm run taskfile-dev
@@ -330,3 +330,7 @@ V1LogLineLevel:
$ref: "./v1/logs.yaml#/V1LogLineLevel"
V1LogLineList:
$ref: "./v1/logs.yaml#/V1LogLineList"
V1TaskTiming:
$ref: "./v1/task.yaml#/V1TaskTiming"
V1TaskTimingList:
$ref: "./v1/task.yaml#/V1TaskTimingList"
@@ -118,6 +118,7 @@ V1TaskSummary:
- tenantId
- type
- workflowId
- workflowRunExternalId
V1WorkflowRunDisplayName:
properties:
@@ -332,3 +333,77 @@ V1ReplayTaskRequest:
maxLength: 36
filter:
$ref: "#/V1TaskFilter"
V1TaskTiming:
properties:
metadata:
$ref: ".././metadata.yaml#/APIResourceMeta"
depth:
type: integer
description: The depth of the task in the waterfall.
status:
$ref: "#/V1TaskStatus"
taskDisplayName:
type: string
description: The display name of the task run.
taskExternalId:
type: string
description: The external ID of the task.
format: uuid
minLength: 36
maxLength: 36
taskId:
type: integer
description: The ID of the task.
taskInsertedAt:
type: string
format: date-time
description: The timestamp the task was inserted.
tenantId:
type: string
description: The ID of the tenant.
example: bb214807-246e-43a5-a25d-41761d1cff9e
minLength: 36
maxLength: 36
format: uuid
parentTaskExternalId:
type: string
description: The external ID of the parent task.
format: uuid
minLength: 36
maxLength: 36
queuedAt:
type: string
format: date-time
description: The timestamp the task run was queued.
startedAt:
type: string
format: date-time
description: The timestamp the task run started.
finishedAt:
type: string
format: date-time
description: The timestamp the task run finished.
required:
- metadata
- depth
- status
- taskDisplayName
- taskExternalId
- taskId
- taskInsertedAt
- tenantId
V1TaskTimingList:
type: object
properties:
pagination:
$ref: ".././metadata.yaml#/PaginationResponse"
rows:
type: array
items:
$ref: "#/V1TaskTiming"
description: The list of task timings
required:
- pagination
- rows
+2
View File
@@ -42,6 +42,8 @@ paths:
$ref: "./paths/v1/workflow-runs/workflow_run.yaml#/getWorkflowRunDetails"
/api/v1/stable/workflow-runs/{v1-workflow-run}/task-events:
$ref: "./paths/v1/workflow-runs/workflow_run.yaml#/listTaskEventsForWorkflowRun"
/api/v1/stable/workflow-runs/{v1-workflow-run}/task-timings:
$ref: "./paths/v1/workflow-runs/workflow_run.yaml#/getTimings"
/api/v1/stable/tenants/{tenant}/task-metrics:
$ref: "./paths/v1/tasks/tasks.yaml#/getTaskStatusMetrics"
/api/v1/stable/tenants/{tenant}/task-point-metrics:
@@ -191,6 +191,12 @@ getTaskStatusMetrics:
schema:
type: string
format: date-time
- description: The end time to get metrics for
in: query
name: until
schema:
type: string
format: date-time
- description: The workflow id to find runs for
in: query
name: workflow_ids
@@ -322,3 +322,54 @@ trigger:
summary: Create workflow run
tags:
- Workflow Runs
getTimings:
get:
x-resources: ["tenant", "v1-workflow-run"]
description: Get the timings for a workflow run
operationId: v1-workflow-run:get:timings
parameters:
- description: The workflow run id to get
in: path
name: v1-workflow-run
required: true
schema:
type: string
format: uuid
minLength: 36
maxLength: 36
- description: The depth to retrieve children
in: query
name: depth
required: false
schema:
type: integer
format: int64
responses:
"200":
content:
application/json:
schema:
$ref: "../../../components/schemas/_index.yaml#/V1TaskTimingList"
description: Successfully listed the tasks
"400":
content:
application/json:
schema:
$ref: "../../../components/schemas/_index.yaml#/APIErrors"
description: A malformed or bad request
"403":
content:
application/json:
schema:
$ref: "../../../components/schemas/_index.yaml#/APIErrors"
description: Forbidden
"501":
content:
application/json:
schema:
$ref: "../../../components/schemas/_index.yaml#/APIErrors"
description: Not implemented
summary: List timings for a workflow run
tags:
- Workflow Runs
@@ -34,6 +34,7 @@ func (t *TasksService) V1TaskListStatusMetrics(ctx echo.Context, request gen.V1T
metrics, err := t.config.V1.OLAP().ReadTaskRunMetrics(ctx.Request().Context(), tenantId, v1.ReadTaskRunMetricsOpts{
CreatedAfter: request.Params.Since,
CreatedBefore: request.Params.Until,
WorkflowIds: workflowIds,
ParentTaskExternalID: parentTaskExternalId,
})
@@ -0,0 +1,126 @@
package workflowruns
import (
"sort"
"github.com/labstack/echo/v4"
"github.com/hatchet-dev/hatchet/api/v1/server/oas/apierrors"
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
"github.com/hatchet-dev/hatchet/pkg/repository/postgres/dbsqlc"
"github.com/hatchet-dev/hatchet/pkg/repository/postgres/sqlchelpers"
v1 "github.com/hatchet-dev/hatchet/pkg/repository/v1"
transformers "github.com/hatchet-dev/hatchet/api/v1/server/oas/transformers/v1"
openapi_types "github.com/oapi-codegen/runtime/types"
)
func (t *V1WorkflowRunsService) V1WorkflowRunGetTimings(ctx echo.Context, request gen.V1WorkflowRunGetTimingsRequestObject) (gen.V1WorkflowRunGetTimingsResponseObject, error) {
tenant := ctx.Get("tenant").(*dbsqlc.Tenant)
tenantId := sqlchelpers.UUIDToStr(tenant.ID)
rawWorkflowRun := ctx.Get("v1-workflow-run").(*v1.V1WorkflowRunPopulator)
workflowRun := rawWorkflowRun.WorkflowRun
var depth int32 = 0
if request.Params.Depth != nil {
depth = int32(*request.Params.Depth)
}
if depth > 10 {
return gen.V1WorkflowRunGetTimings400JSONResponse(
apierrors.NewAPIErrors("depth must be less than or equal to 10"),
), nil
}
taskTimings, idsToDepths, err := t.config.V1.OLAP().GetTaskTimings(
ctx.Request().Context(),
tenantId,
workflowRun.ExternalID,
depth,
)
if err != nil {
return nil, err
}
result := transformers.ToTaskTimings(taskTimings, idsToDepths)
// Search for api errors to see how we handle errors in other cases
return gen.V1WorkflowRunGetTimings200JSONResponse(
gen.V1TaskTimingList{
Rows: DFSOrder(result),
},
), nil
}
func DFSOrder(tasks []gen.V1TaskTiming) []gen.V1TaskTiming {
// build set of task IDs
idSet := make(map[openapi_types.UUID]struct{}, len(tasks))
for _, t := range tasks {
idSet[t.TaskExternalId] = struct{}{}
}
// group children and collect roots
children := make(map[openapi_types.UUID][]gen.V1TaskTiming)
var roots []gen.V1TaskTiming
for _, t := range tasks {
if t.ParentTaskExternalId != nil {
if _, ok := idSet[*t.ParentTaskExternalId]; ok {
children[*t.ParentTaskExternalId] = append(children[*t.ParentTaskExternalId], t)
continue
}
}
roots = append(roots, t)
}
// sort roots by queuedAt, then taskId
sort.SliceStable(roots, func(i, j int) bool {
qi, qj := roots[i].QueuedAt, roots[j].QueuedAt
if qi != nil && qj != nil {
if !qi.Equal(*qj) {
return qi.Before(*qj)
}
} else if qi != nil {
return true
} else if qj != nil {
return false
}
return roots[i].TaskId < roots[j].TaskId
})
// sort each child list the same way
for parent, childs := range children {
sort.SliceStable(childs, func(i, j int) bool {
qi, qj := childs[i].QueuedAt, childs[j].QueuedAt
if qi != nil && qj != nil {
if !qi.Equal(*qj) {
return qi.Before(*qj)
}
} else if qi != nil {
return true
} else if qj != nil {
return false
}
return childs[i].TaskId < childs[j].TaskId
})
children[parent] = childs
}
// depth-first traversal
var result []gen.V1TaskTiming
var visit func(gen.V1TaskTiming)
visit = func(t gen.V1TaskTiming) {
result = append(result, t)
for _, child := range children[t.TaskExternalId] {
visit(child)
}
}
for _, root := range roots {
visit(root)
}
return result
}
+407 -239
View File
@@ -1328,7 +1328,7 @@ type V1TaskSummary struct {
WorkflowName *string `json:"workflowName,omitempty"`
// WorkflowRunExternalId The external ID of the workflow run
WorkflowRunExternalId *openapi_types.UUID `json:"workflowRunExternalId,omitempty"`
WorkflowRunExternalId openapi_types.UUID `json:"workflowRunExternalId"`
// WorkflowVersionId The version ID of the workflow
WorkflowVersionId *openapi_types.UUID `json:"workflowVersionId,omitempty"`
@@ -1342,6 +1342,49 @@ type V1TaskSummaryList struct {
Rows []V1TaskSummary `json:"rows"`
}
// V1TaskTiming defines model for V1TaskTiming.
type V1TaskTiming struct {
// Depth The depth of the task in the waterfall.
Depth int `json:"depth"`
// FinishedAt The timestamp the task run finished.
FinishedAt *time.Time `json:"finishedAt,omitempty"`
Metadata APIResourceMeta `json:"metadata"`
// ParentTaskExternalId The external ID of the parent task.
ParentTaskExternalId *openapi_types.UUID `json:"parentTaskExternalId,omitempty"`
// QueuedAt The timestamp the task run was queued.
QueuedAt *time.Time `json:"queuedAt,omitempty"`
// StartedAt The timestamp the task run started.
StartedAt *time.Time `json:"startedAt,omitempty"`
Status V1TaskStatus `json:"status"`
// TaskDisplayName The display name of the task run.
TaskDisplayName string `json:"taskDisplayName"`
// TaskExternalId The external ID of the task.
TaskExternalId openapi_types.UUID `json:"taskExternalId"`
// TaskId The ID of the task.
TaskId int `json:"taskId"`
// TaskInsertedAt The timestamp the task was inserted.
TaskInsertedAt time.Time `json:"taskInsertedAt"`
// TenantId The ID of the tenant.
TenantId openapi_types.UUID `json:"tenantId"`
}
// V1TaskTimingList defines model for V1TaskTimingList.
type V1TaskTimingList struct {
Pagination PaginationResponse `json:"pagination"`
// Rows The list of task timings
Rows []V1TaskTiming `json:"rows"`
}
// V1TriggerWorkflowRunRequest defines model for V1TriggerWorkflowRunRequest.
type V1TriggerWorkflowRunRequest struct {
AdditionalMetadata *map[string]interface{} `json:"additionalMetadata,omitempty"`
@@ -1814,6 +1857,9 @@ type V1TaskListStatusMetricsParams struct {
// Since The start time to get metrics for
Since time.Time `form:"since" json:"since"`
// Until The end time to get metrics for
Until *time.Time `form:"until,omitempty" json:"until,omitempty"`
// WorkflowIds The workflow id to find runs for
WorkflowIds *[]openapi_types.UUID `form:"workflow_ids,omitempty" json:"workflow_ids,omitempty"`
@@ -1878,6 +1924,12 @@ type V1WorkflowRunTaskEventsListParams struct {
Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"`
}
// V1WorkflowRunGetTimingsParams defines parameters for V1WorkflowRunGetTimings.
type V1WorkflowRunGetTimingsParams struct {
// Depth The depth to retrieve children
Depth *int64 `form:"depth,omitempty" json:"depth,omitempty"`
}
// StepRunListArchivesParams defines parameters for StepRunListArchives.
type StepRunListArchivesParams struct {
// Offset The number to skip
@@ -2309,6 +2361,9 @@ type ServerInterface interface {
// List tasks
// (GET /api/v1/stable/workflow-runs/{v1-workflow-run}/task-events)
V1WorkflowRunTaskEventsList(ctx echo.Context, v1WorkflowRun openapi_types.UUID, params V1WorkflowRunTaskEventsListParams) error
// List timings for a workflow run
// (GET /api/v1/stable/workflow-runs/{v1-workflow-run}/task-timings)
V1WorkflowRunGetTimings(ctx echo.Context, v1WorkflowRun openapi_types.UUID, params V1WorkflowRunGetTimingsParams) error
// List archives for step run
// (GET /api/v1/step-runs/{step-run}/archives)
StepRunListArchives(ctx echo.Context, stepRun openapi_types.UUID, params StepRunListArchivesParams) error
@@ -2918,6 +2973,13 @@ func (w *ServerInterfaceWrapper) V1TaskListStatusMetrics(ctx echo.Context) error
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter since: %s", err))
}
// ------------- Optional query parameter "until" -------------
err = runtime.BindQueryParameter("form", true, false, "until", ctx.QueryParams(), &params.Until)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter until: %s", err))
}
// ------------- Optional query parameter "workflow_ids" -------------
err = runtime.BindQueryParameter("form", true, false, "workflow_ids", ctx.QueryParams(), &params.WorkflowIds)
@@ -3210,6 +3272,35 @@ func (w *ServerInterfaceWrapper) V1WorkflowRunTaskEventsList(ctx echo.Context) e
return err
}
// V1WorkflowRunGetTimings converts echo context to params.
func (w *ServerInterfaceWrapper) V1WorkflowRunGetTimings(ctx echo.Context) error {
var err error
// ------------- Path parameter "v1-workflow-run" -------------
var v1WorkflowRun openapi_types.UUID
err = runtime.BindStyledParameterWithLocation("simple", false, "v1-workflow-run", runtime.ParamLocationPath, ctx.Param("v1-workflow-run"), &v1WorkflowRun)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter v1-workflow-run: %s", err))
}
ctx.Set(BearerAuthScopes, []string{})
ctx.Set(CookieAuthScopes, []string{})
// Parameter object where we will unmarshal all parameters from the context
var params V1WorkflowRunGetTimingsParams
// ------------- Optional query parameter "depth" -------------
err = runtime.BindQueryParameter("form", true, false, "depth", ctx.QueryParams(), &params.Depth)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter depth: %s", err))
}
// Invoke the callback with all the unmarshaled arguments
err = w.Handler.V1WorkflowRunGetTimings(ctx, v1WorkflowRun, params)
return err
}
// StepRunListArchives converts echo context to params.
func (w *ServerInterfaceWrapper) StepRunListArchives(ctx echo.Context) error {
var err error
@@ -5389,6 +5480,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL
router.POST(baseURL+"/api/v1/stable/tenants/:tenant/workflow-runs/trigger", wrapper.V1WorkflowRunCreate)
router.GET(baseURL+"/api/v1/stable/workflow-runs/:v1-workflow-run", wrapper.V1WorkflowRunGet)
router.GET(baseURL+"/api/v1/stable/workflow-runs/:v1-workflow-run/task-events", wrapper.V1WorkflowRunTaskEventsList)
router.GET(baseURL+"/api/v1/stable/workflow-runs/:v1-workflow-run/task-timings", wrapper.V1WorkflowRunGetTimings)
router.GET(baseURL+"/api/v1/step-runs/:step-run/archives", wrapper.StepRunListArchives)
router.GET(baseURL+"/api/v1/step-runs/:step-run/events", wrapper.StepRunListEvents)
router.GET(baseURL+"/api/v1/step-runs/:step-run/logs", wrapper.LogLineList)
@@ -6519,6 +6611,51 @@ func (response V1WorkflowRunTaskEventsList501JSONResponse) VisitV1WorkflowRunTas
return json.NewEncoder(w).Encode(response)
}
type V1WorkflowRunGetTimingsRequestObject struct {
V1WorkflowRun openapi_types.UUID `json:"v1-workflow-run"`
Params V1WorkflowRunGetTimingsParams
}
type V1WorkflowRunGetTimingsResponseObject interface {
VisitV1WorkflowRunGetTimingsResponse(w http.ResponseWriter) error
}
type V1WorkflowRunGetTimings200JSONResponse V1TaskTimingList
func (response V1WorkflowRunGetTimings200JSONResponse) VisitV1WorkflowRunGetTimingsResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(200)
return json.NewEncoder(w).Encode(response)
}
type V1WorkflowRunGetTimings400JSONResponse APIErrors
func (response V1WorkflowRunGetTimings400JSONResponse) VisitV1WorkflowRunGetTimingsResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(400)
return json.NewEncoder(w).Encode(response)
}
type V1WorkflowRunGetTimings403JSONResponse APIErrors
func (response V1WorkflowRunGetTimings403JSONResponse) VisitV1WorkflowRunGetTimingsResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(403)
return json.NewEncoder(w).Encode(response)
}
type V1WorkflowRunGetTimings501JSONResponse APIErrors
func (response V1WorkflowRunGetTimings501JSONResponse) VisitV1WorkflowRunGetTimingsResponse(w http.ResponseWriter) error {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(501)
return json.NewEncoder(w).Encode(response)
}
type StepRunListArchivesRequestObject struct {
StepRun openapi_types.UUID `json:"step-run"`
Params StepRunListArchivesParams
@@ -9644,6 +9781,8 @@ type StrictServerInterface interface {
V1WorkflowRunTaskEventsList(ctx echo.Context, request V1WorkflowRunTaskEventsListRequestObject) (V1WorkflowRunTaskEventsListResponseObject, error)
V1WorkflowRunGetTimings(ctx echo.Context, request V1WorkflowRunGetTimingsRequestObject) (V1WorkflowRunGetTimingsResponseObject, error)
StepRunListArchives(ctx echo.Context, request StepRunListArchivesRequestObject) (StepRunListArchivesResponseObject, error)
StepRunListEvents(ctx echo.Context, request StepRunListEventsRequestObject) (StepRunListEventsResponseObject, error)
@@ -10512,6 +10651,32 @@ func (sh *strictHandler) V1WorkflowRunTaskEventsList(ctx echo.Context, v1Workflo
return nil
}
// V1WorkflowRunGetTimings operation middleware
func (sh *strictHandler) V1WorkflowRunGetTimings(ctx echo.Context, v1WorkflowRun openapi_types.UUID, params V1WorkflowRunGetTimingsParams) error {
var request V1WorkflowRunGetTimingsRequestObject
request.V1WorkflowRun = v1WorkflowRun
request.Params = params
handler := func(ctx echo.Context, request interface{}) (interface{}, error) {
return sh.ssi.V1WorkflowRunGetTimings(ctx, request.(V1WorkflowRunGetTimingsRequestObject))
}
for _, middleware := range sh.middlewares {
handler = middleware(handler, "V1WorkflowRunGetTimings")
}
response, err := handler(ctx, request)
if err != nil {
return err
} else if validResponse, ok := response.(V1WorkflowRunGetTimingsResponseObject); ok {
return validResponse.VisitV1WorkflowRunGetTimingsResponse(ctx.Response())
} else if response != nil {
return fmt.Errorf("Unexpected response type: %T", response)
}
return nil
}
// StepRunListArchives operation middleware
func (sh *strictHandler) StepRunListArchives(ctx echo.Context, stepRun openapi_types.UUID, params StepRunListArchivesParams) error {
var request StepRunListArchivesRequestObject
@@ -12666,244 +12831,247 @@ func (sh *strictHandler) WorkflowVersionGet(ctx echo.Context, workflow openapi_t
// Base64 encoded, gzipped, json marshaled Swagger object
var swaggerSpec = []string{
"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",
"/4iwYK/VaQIA",
"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",
}
// GetSwagger returns the content of the embedded swagger specification file
+69 -30
View File
@@ -23,7 +23,7 @@ func jsonToMap(jsonBytes []byte) map[string]interface{} {
}
func ToTaskSummary(task *sqlcv1.PopulateTaskRunDataRow) gen.V1TaskSummary {
workflowVersionId := uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowVersionID))
workflowVersionID := uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowVersionID))
additionalMetadata := jsonToMap(task.AdditionalMetadata)
var finishedAt *time.Time
@@ -54,24 +54,25 @@ func ToTaskSummary(task *sqlcv1.PopulateTaskRunDataRow) gen.V1TaskSummary {
CreatedAt: task.InsertedAt.Time,
UpdatedAt: task.InsertedAt.Time,
},
Input: jsonToMap(task.Input),
Output: jsonToMap(task.Output),
Type: gen.V1WorkflowTypeTASK,
DisplayName: task.DisplayName,
Duration: durationPtr,
StartedAt: startedAt,
FinishedAt: finishedAt,
AdditionalMetadata: &additionalMetadata,
ErrorMessage: &task.ErrorMessage.String,
Status: gen.V1TaskStatus(task.Status),
TenantId: uuid.MustParse(sqlchelpers.UUIDToStr(task.TenantID)),
WorkflowId: uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowID)),
TaskId: int(task.ID),
TaskInsertedAt: task.InsertedAt.Time,
TaskExternalId: taskExternalId,
StepId: &stepId,
ActionId: &task.ActionID,
WorkflowVersionId: &workflowVersionId,
Input: jsonToMap(task.Input),
Output: jsonToMap(task.Output),
Type: gen.V1WorkflowTypeTASK,
DisplayName: task.DisplayName,
Duration: durationPtr,
StartedAt: startedAt,
FinishedAt: finishedAt,
AdditionalMetadata: &additionalMetadata,
ErrorMessage: &task.ErrorMessage.String,
Status: gen.V1TaskStatus(task.Status),
TenantId: uuid.MustParse(sqlchelpers.UUIDToStr(task.TenantID)),
WorkflowId: uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowID)),
TaskId: int(task.ID),
TaskInsertedAt: task.InsertedAt.Time,
TaskExternalId: taskExternalId,
StepId: &stepId,
ActionId: &task.ActionID,
WorkflowRunExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowRunID)),
WorkflowVersionId: &workflowVersionID,
}
}
@@ -228,8 +229,8 @@ func ToTaskRunMetrics(metrics *[]v1.TaskRunMetric) gen.V1TaskRunMetrics {
return toReturn
}
func ToTask(taskWithData *sqlcv1.PopulateSingleTaskRunDataRow, workflowRunExternalId *pgtype.UUID) gen.V1TaskSummary {
workflowVersionId := uuid.MustParse(sqlchelpers.UUIDToStr(taskWithData.WorkflowVersionID))
func ToTask(taskWithData *sqlcv1.PopulateSingleTaskRunDataRow, workflowRunExternalId pgtype.UUID) gen.V1TaskSummary {
workflowVersionID := uuid.MustParse(sqlchelpers.UUIDToStr(taskWithData.WorkflowVersionID))
additionalMetadata := jsonToMap(taskWithData.AdditionalMetadata)
var finishedAt *time.Time
@@ -259,13 +260,6 @@ func ToTask(taskWithData *sqlcv1.PopulateSingleTaskRunDataRow, workflowRunExtern
input := jsonToMap(taskWithData.Input)
var parsedWorkflowRunUUID *uuid.UUID
if workflowRunExternalId != nil && workflowRunExternalId.Valid {
id := uuid.MustParse(sqlchelpers.UUIDToStr(*workflowRunExternalId))
parsedWorkflowRunUUID = &id
}
stepId := uuid.MustParse(sqlchelpers.UUIDToStr(taskWithData.StepID))
return gen.V1TaskSummary{
@@ -287,13 +281,13 @@ func ToTask(taskWithData *sqlcv1.PopulateSingleTaskRunDataRow, workflowRunExtern
TenantId: uuid.MustParse(sqlchelpers.UUIDToStr(taskWithData.TenantID)),
WorkflowId: uuid.MustParse(sqlchelpers.UUIDToStr(taskWithData.WorkflowID)),
ErrorMessage: &taskWithData.ErrorMessage.String,
WorkflowRunExternalId: parsedWorkflowRunUUID,
WorkflowRunExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(workflowRunExternalId)),
TaskExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(taskWithData.ExternalID)),
Type: gen.V1WorkflowTypeTASK,
NumSpawnedChildren: int(taskWithData.SpawnedChildren.Int64),
StepId: &stepId,
ActionId: &taskWithData.ActionID,
WorkflowVersionId: &workflowVersionId,
WorkflowVersionId: &workflowVersionID,
}
}
@@ -391,3 +385,48 @@ func ToWorkflowRunDetails(
Tasks: parsedTasks,
}, nil
}
func ToTaskTimings(
timings []*sqlcv1.PopulateTaskRunDataRow,
idsToDepth map[string]int32,
) []gen.V1TaskTiming {
toReturn := make([]gen.V1TaskTiming, len(timings))
for i, timing := range timings {
depth := idsToDepth[sqlchelpers.UUIDToStr(timing.ExternalID)]
toReturn[i] = gen.V1TaskTiming{
Metadata: gen.APIResourceMeta{
Id: sqlchelpers.UUIDToStr(timing.ExternalID),
CreatedAt: timing.InsertedAt.Time,
UpdatedAt: timing.InsertedAt.Time,
},
Status: gen.V1TaskStatus(timing.Status),
TaskDisplayName: timing.DisplayName,
TaskId: int(timing.ID),
TaskInsertedAt: timing.InsertedAt.Time,
TaskExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(timing.ExternalID)),
TenantId: uuid.MustParse(sqlchelpers.UUIDToStr(timing.TenantID)),
Depth: int(depth),
}
if timing.QueuedAt.Valid {
toReturn[i].QueuedAt = &timing.QueuedAt.Time
}
if timing.StartedAt.Valid {
toReturn[i].StartedAt = &timing.StartedAt.Time
}
if timing.FinishedAt.Valid {
toReturn[i].FinishedAt = &timing.FinishedAt.Time
}
if timing.ParentTaskExternalID.Valid {
parentId := uuid.MustParse(sqlchelpers.UUIDToStr(timing.ParentTaskExternalID))
toReturn[i].ParentTaskExternalId = &parentId
}
}
return toReturn
}
@@ -69,26 +69,27 @@ func WorkflowRunDataToV1TaskSummary(task *v1.WorkflowRunData, workflowIdsToNames
CreatedAt: task.InsertedAt.Time,
UpdatedAt: task.InsertedAt.Time,
},
CreatedAt: task.CreatedAt.Time,
DisplayName: task.DisplayName,
Duration: durationPtr,
StartedAt: startedAt,
FinishedAt: finishedAt,
Input: input,
Output: output,
AdditionalMetadata: &additionalMetadata,
ErrorMessage: &task.ErrorMessage,
Status: gen.V1TaskStatus(task.ReadableStatus),
TenantId: uuid.MustParse(sqlchelpers.UUIDToStr(task.TenantID)),
WorkflowId: uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowID)),
WorkflowVersionId: &workflowVersionId,
TaskExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(task.ExternalID)),
TaskId: taskId,
TaskInsertedAt: task.InsertedAt.Time,
Type: gen.V1WorkflowTypeDAG,
WorkflowName: workflowName,
StepId: &stepId,
ActionId: &actionId,
CreatedAt: task.CreatedAt.Time,
DisplayName: task.DisplayName,
Duration: durationPtr,
StartedAt: startedAt,
FinishedAt: finishedAt,
Input: input,
Output: output,
AdditionalMetadata: &additionalMetadata,
ErrorMessage: &task.ErrorMessage,
Status: gen.V1TaskStatus(task.ReadableStatus),
TenantId: uuid.MustParse(sqlchelpers.UUIDToStr(task.TenantID)),
WorkflowId: uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowID)),
WorkflowVersionId: &workflowVersionId,
TaskExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(task.ExternalID)),
TaskId: taskId,
TaskInsertedAt: task.InsertedAt.Time,
Type: gen.V1WorkflowTypeDAG,
WorkflowName: workflowName,
StepId: &stepId,
ActionId: &actionId,
WorkflowRunExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(task.ExternalID)),
}
}
@@ -138,7 +139,7 @@ func ToWorkflowRunMany(
}
func PopulateTaskRunDataRowToV1TaskSummary(task *sqlcv1.PopulateTaskRunDataRow, workflowName *string) gen.V1TaskSummary {
workflowVersionId := uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowVersionID))
workflowVersionID := uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowVersionID))
additionalMetadata := jsonToMap(task.AdditionalMetadata)
var finishedAt *time.Time
@@ -170,27 +171,28 @@ func PopulateTaskRunDataRowToV1TaskSummary(task *sqlcv1.PopulateTaskRunDataRow,
CreatedAt: task.InsertedAt.Time,
UpdatedAt: task.InsertedAt.Time,
},
CreatedAt: task.InsertedAt.Time,
DisplayName: task.DisplayName,
Duration: durationPtr,
StartedAt: startedAt,
FinishedAt: finishedAt,
Input: input,
Output: output,
AdditionalMetadata: &additionalMetadata,
ErrorMessage: &task.ErrorMessage.String,
Status: gen.V1TaskStatus(task.Status),
TenantId: uuid.MustParse(sqlchelpers.UUIDToStr(task.TenantID)),
WorkflowId: uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowID)),
WorkflowVersionId: &workflowVersionId,
Children: nil,
TaskExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(task.ExternalID)),
TaskId: int(task.ID),
TaskInsertedAt: task.InsertedAt.Time,
Type: gen.V1WorkflowTypeTASK,
WorkflowName: workflowName,
StepId: &stepId,
ActionId: &task.ActionID,
CreatedAt: task.InsertedAt.Time,
DisplayName: task.DisplayName,
Duration: durationPtr,
StartedAt: startedAt,
FinishedAt: finishedAt,
Input: input,
Output: output,
AdditionalMetadata: &additionalMetadata,
ErrorMessage: &task.ErrorMessage.String,
Status: gen.V1TaskStatus(task.Status),
TenantId: uuid.MustParse(sqlchelpers.UUIDToStr(task.TenantID)),
WorkflowId: uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowID)),
WorkflowVersionId: &workflowVersionID,
Children: nil,
TaskExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(task.ExternalID)),
TaskId: int(task.ID),
TaskInsertedAt: task.InsertedAt.Time,
Type: gen.V1WorkflowTypeTASK,
WorkflowName: workflowName,
StepId: &stepId,
ActionId: &task.ActionID,
WorkflowRunExternalId: uuid.MustParse(sqlchelpers.UUIDToStr(task.WorkflowRunID)),
}
}
+34
View File
@@ -94,6 +94,7 @@ import {
V1TaskStatus,
V1TaskSummary,
V1TaskSummaryList,
V1TaskTimingList,
V1TriggerWorkflowRunRequest,
V1WorkflowRunDetails,
V1WorkflowRunDisplayNameList,
@@ -417,6 +418,34 @@ export class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType
format: 'json',
...params,
});
/**
* @description Get the timings for a workflow run
*
* @tags Workflow Runs
* @name V1WorkflowRunGetTimings
* @summary List timings for a workflow run
* @request GET:/api/v1/stable/workflow-runs/{v1-workflow-run}/task-timings
* @secure
*/
v1WorkflowRunGetTimings = (
v1WorkflowRun: string,
query?: {
/**
* The depth to retrieve children
* @format int64
*/
depth?: number;
},
params: RequestParams = {},
) =>
this.request<V1TaskTimingList, APIErrors>({
path: `/api/v1/stable/workflow-runs/${v1WorkflowRun}/task-timings`,
method: 'GET',
query: query,
secure: true,
format: 'json',
...params,
});
/**
* @description Get a summary of task run metrics for a tenant
*
@@ -434,6 +463,11 @@ export class Api<SecurityDataType = unknown> extends HttpClient<SecurityDataType
* @format date-time
*/
since: string;
/**
* The end time to get metrics for
* @format date-time
*/
until?: string;
/** The workflow id to find runs for */
workflow_ids?: string[];
/**
@@ -106,7 +106,7 @@ export interface V1TaskSummary {
* The external ID of the workflow run
* @format uuid
*/
workflowRunExternalId?: string;
workflowRunExternalId: string;
/**
* The version ID of the workflow
* @format uuid
@@ -371,6 +371,65 @@ export interface V1WorkflowRunDetails {
tasks: V1TaskSummary[];
}
export interface V1TaskTiming {
metadata: APIResourceMeta;
/** The depth of the task in the waterfall. */
depth: number;
status: V1TaskStatus;
/** The display name of the task run. */
taskDisplayName: string;
/**
* The external ID of the task.
* @format uuid
* @minLength 36
* @maxLength 36
*/
taskExternalId: string;
/** The ID of the task. */
taskId: number;
/**
* The timestamp the task was inserted.
* @format date-time
*/
taskInsertedAt: string;
/**
* The ID of the tenant.
* @format uuid
* @minLength 36
* @maxLength 36
* @example "bb214807-246e-43a5-a25d-41761d1cff9e"
*/
tenantId: string;
/**
* The external ID of the parent task.
* @format uuid
* @minLength 36
* @maxLength 36
*/
parentTaskExternalId?: string;
/**
* The timestamp the task run was queued.
* @format date-time
*/
queuedAt?: string;
/**
* The timestamp the task run started.
* @format date-time
*/
startedAt?: string;
/**
* The timestamp the task run finished.
* @format date-time
*/
finishedAt?: string;
}
export interface V1TaskTimingList {
pagination: PaginationResponse;
/** The list of task timings */
rows: V1TaskTiming[];
}
export interface V1TaskRunMetric {
status: V1TaskStatus;
count: number;
+224 -1
View File
@@ -1325,7 +1325,7 @@ type V1TaskSummary struct {
WorkflowName *string `json:"workflowName,omitempty"`
// WorkflowRunExternalId The external ID of the workflow run
WorkflowRunExternalId *openapi_types.UUID `json:"workflowRunExternalId,omitempty"`
WorkflowRunExternalId openapi_types.UUID `json:"workflowRunExternalId"`
// WorkflowVersionId The version ID of the workflow
WorkflowVersionId *openapi_types.UUID `json:"workflowVersionId,omitempty"`
@@ -1339,6 +1339,49 @@ type V1TaskSummaryList struct {
Rows []V1TaskSummary `json:"rows"`
}
// V1TaskTiming defines model for V1TaskTiming.
type V1TaskTiming struct {
// Depth The depth of the task in the waterfall.
Depth int `json:"depth"`
// FinishedAt The timestamp the task run finished.
FinishedAt *time.Time `json:"finishedAt,omitempty"`
Metadata APIResourceMeta `json:"metadata"`
// ParentTaskExternalId The external ID of the parent task.
ParentTaskExternalId *openapi_types.UUID `json:"parentTaskExternalId,omitempty"`
// QueuedAt The timestamp the task run was queued.
QueuedAt *time.Time `json:"queuedAt,omitempty"`
// StartedAt The timestamp the task run started.
StartedAt *time.Time `json:"startedAt,omitempty"`
Status V1TaskStatus `json:"status"`
// TaskDisplayName The display name of the task run.
TaskDisplayName string `json:"taskDisplayName"`
// TaskExternalId The external ID of the task.
TaskExternalId openapi_types.UUID `json:"taskExternalId"`
// TaskId The ID of the task.
TaskId int `json:"taskId"`
// TaskInsertedAt The timestamp the task was inserted.
TaskInsertedAt time.Time `json:"taskInsertedAt"`
// TenantId The ID of the tenant.
TenantId openapi_types.UUID `json:"tenantId"`
}
// V1TaskTimingList defines model for V1TaskTimingList.
type V1TaskTimingList struct {
Pagination PaginationResponse `json:"pagination"`
// Rows The list of task timings
Rows []V1TaskTiming `json:"rows"`
}
// V1TriggerWorkflowRunRequest defines model for V1TriggerWorkflowRunRequest.
type V1TriggerWorkflowRunRequest struct {
AdditionalMetadata *map[string]interface{} `json:"additionalMetadata,omitempty"`
@@ -1811,6 +1854,9 @@ type V1TaskListStatusMetricsParams struct {
// Since The start time to get metrics for
Since time.Time `form:"since" json:"since"`
// Until The end time to get metrics for
Until *time.Time `form:"until,omitempty" json:"until,omitempty"`
// WorkflowIds The workflow id to find runs for
WorkflowIds *[]openapi_types.UUID `form:"workflow_ids,omitempty" json:"workflow_ids,omitempty"`
@@ -1875,6 +1921,12 @@ type V1WorkflowRunTaskEventsListParams struct {
Limit *int64 `form:"limit,omitempty" json:"limit,omitempty"`
}
// V1WorkflowRunGetTimingsParams defines parameters for V1WorkflowRunGetTimings.
type V1WorkflowRunGetTimingsParams struct {
// Depth The depth to retrieve children
Depth *int64 `form:"depth,omitempty" json:"depth,omitempty"`
}
// StepRunListArchivesParams defines parameters for StepRunListArchives.
type StepRunListArchivesParams struct {
// Offset The number to skip
@@ -2385,6 +2437,9 @@ type ClientInterface interface {
// V1WorkflowRunTaskEventsList request
V1WorkflowRunTaskEventsList(ctx context.Context, v1WorkflowRun openapi_types.UUID, params *V1WorkflowRunTaskEventsListParams, reqEditors ...RequestEditorFn) (*http.Response, error)
// V1WorkflowRunGetTimings request
V1WorkflowRunGetTimings(ctx context.Context, v1WorkflowRun openapi_types.UUID, params *V1WorkflowRunGetTimingsParams, reqEditors ...RequestEditorFn) (*http.Response, error)
// StepRunListArchives request
StepRunListArchives(ctx context.Context, stepRun openapi_types.UUID, params *StepRunListArchivesParams, reqEditors ...RequestEditorFn) (*http.Response, error)
@@ -3048,6 +3103,18 @@ func (c *Client) V1WorkflowRunTaskEventsList(ctx context.Context, v1WorkflowRun
return c.Client.Do(req)
}
func (c *Client) V1WorkflowRunGetTimings(ctx context.Context, v1WorkflowRun openapi_types.UUID, params *V1WorkflowRunGetTimingsParams, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewV1WorkflowRunGetTimingsRequest(c.Server, v1WorkflowRun, params)
if err != nil {
return nil, err
}
req = req.WithContext(ctx)
if err := c.applyEditors(ctx, req, reqEditors); err != nil {
return nil, err
}
return c.Client.Do(req)
}
func (c *Client) StepRunListArchives(ctx context.Context, stepRun openapi_types.UUID, params *StepRunListArchivesParams, reqEditors ...RequestEditorFn) (*http.Response, error) {
req, err := NewStepRunListArchivesRequest(c.Server, stepRun, params)
if err != nil {
@@ -5007,6 +5074,22 @@ func NewV1TaskListStatusMetricsRequest(server string, tenant openapi_types.UUID,
}
}
if params.Until != nil {
if queryFrag, err := runtime.StyleParamWithLocation("form", true, "until", runtime.ParamLocationQuery, *params.Until); err != nil {
return nil, err
} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
return nil, err
} else {
for k, v := range parsed {
for _, v2 := range v {
queryValues.Add(k, v2)
}
}
}
}
if params.WorkflowIds != nil {
if queryFrag, err := runtime.StyleParamWithLocation("form", true, "workflow_ids", runtime.ParamLocationQuery, *params.WorkflowIds); err != nil {
@@ -5613,6 +5696,62 @@ func NewV1WorkflowRunTaskEventsListRequest(server string, v1WorkflowRun openapi_
return req, nil
}
// NewV1WorkflowRunGetTimingsRequest generates requests for V1WorkflowRunGetTimings
func NewV1WorkflowRunGetTimingsRequest(server string, v1WorkflowRun openapi_types.UUID, params *V1WorkflowRunGetTimingsParams) (*http.Request, error) {
var err error
var pathParam0 string
pathParam0, err = runtime.StyleParamWithLocation("simple", false, "v1-workflow-run", runtime.ParamLocationPath, v1WorkflowRun)
if err != nil {
return nil, err
}
serverURL, err := url.Parse(server)
if err != nil {
return nil, err
}
operationPath := fmt.Sprintf("/api/v1/stable/workflow-runs/%s/task-timings", pathParam0)
if operationPath[0] == '/' {
operationPath = "." + operationPath
}
queryURL, err := serverURL.Parse(operationPath)
if err != nil {
return nil, err
}
if params != nil {
queryValues := queryURL.Query()
if params.Depth != nil {
if queryFrag, err := runtime.StyleParamWithLocation("form", true, "depth", runtime.ParamLocationQuery, *params.Depth); err != nil {
return nil, err
} else if parsed, err := url.ParseQuery(queryFrag); err != nil {
return nil, err
} else {
for k, v := range parsed {
for _, v2 := range v {
queryValues.Add(k, v2)
}
}
}
}
queryURL.RawQuery = queryValues.Encode()
}
req, err := http.NewRequest("GET", queryURL.String(), nil)
if err != nil {
return nil, err
}
return req, nil
}
// NewStepRunListArchivesRequest generates requests for StepRunListArchives
func NewStepRunListArchivesRequest(server string, stepRun openapi_types.UUID, params *StepRunListArchivesParams) (*http.Request, error) {
var err error
@@ -10046,6 +10185,9 @@ type ClientWithResponsesInterface interface {
// V1WorkflowRunTaskEventsListWithResponse request
V1WorkflowRunTaskEventsListWithResponse(ctx context.Context, v1WorkflowRun openapi_types.UUID, params *V1WorkflowRunTaskEventsListParams, reqEditors ...RequestEditorFn) (*V1WorkflowRunTaskEventsListResponse, error)
// V1WorkflowRunGetTimingsWithResponse request
V1WorkflowRunGetTimingsWithResponse(ctx context.Context, v1WorkflowRun openapi_types.UUID, params *V1WorkflowRunGetTimingsParams, reqEditors ...RequestEditorFn) (*V1WorkflowRunGetTimingsResponse, error)
// StepRunListArchivesWithResponse request
StepRunListArchivesWithResponse(ctx context.Context, stepRun openapi_types.UUID, params *StepRunListArchivesParams, reqEditors ...RequestEditorFn) (*StepRunListArchivesResponse, error)
@@ -10985,6 +11127,31 @@ func (r V1WorkflowRunTaskEventsListResponse) StatusCode() int {
return 0
}
type V1WorkflowRunGetTimingsResponse struct {
Body []byte
HTTPResponse *http.Response
JSON200 *V1TaskTimingList
JSON400 *APIErrors
JSON403 *APIErrors
JSON501 *APIErrors
}
// Status returns HTTPResponse.Status
func (r V1WorkflowRunGetTimingsResponse) Status() string {
if r.HTTPResponse != nil {
return r.HTTPResponse.Status
}
return http.StatusText(0)
}
// StatusCode returns HTTPResponse.StatusCode
func (r V1WorkflowRunGetTimingsResponse) StatusCode() int {
if r.HTTPResponse != nil {
return r.HTTPResponse.StatusCode
}
return 0
}
type StepRunListArchivesResponse struct {
Body []byte
HTTPResponse *http.Response
@@ -13196,6 +13363,15 @@ func (c *ClientWithResponses) V1WorkflowRunTaskEventsListWithResponse(ctx contex
return ParseV1WorkflowRunTaskEventsListResponse(rsp)
}
// V1WorkflowRunGetTimingsWithResponse request returning *V1WorkflowRunGetTimingsResponse
func (c *ClientWithResponses) V1WorkflowRunGetTimingsWithResponse(ctx context.Context, v1WorkflowRun openapi_types.UUID, params *V1WorkflowRunGetTimingsParams, reqEditors ...RequestEditorFn) (*V1WorkflowRunGetTimingsResponse, error) {
rsp, err := c.V1WorkflowRunGetTimings(ctx, v1WorkflowRun, params, reqEditors...)
if err != nil {
return nil, err
}
return ParseV1WorkflowRunGetTimingsResponse(rsp)
}
// StepRunListArchivesWithResponse request returning *StepRunListArchivesResponse
func (c *ClientWithResponses) StepRunListArchivesWithResponse(ctx context.Context, stepRun openapi_types.UUID, params *StepRunListArchivesParams, reqEditors ...RequestEditorFn) (*StepRunListArchivesResponse, error) {
rsp, err := c.StepRunListArchives(ctx, stepRun, params, reqEditors...)
@@ -15190,6 +15366,53 @@ func ParseV1WorkflowRunTaskEventsListResponse(rsp *http.Response) (*V1WorkflowRu
return response, nil
}
// ParseV1WorkflowRunGetTimingsResponse parses an HTTP response from a V1WorkflowRunGetTimingsWithResponse call
func ParseV1WorkflowRunGetTimingsResponse(rsp *http.Response) (*V1WorkflowRunGetTimingsResponse, error) {
bodyBytes, err := io.ReadAll(rsp.Body)
defer func() { _ = rsp.Body.Close() }()
if err != nil {
return nil, err
}
response := &V1WorkflowRunGetTimingsResponse{
Body: bodyBytes,
HTTPResponse: rsp,
}
switch {
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200:
var dest V1TaskTimingList
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON200 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400:
var dest APIErrors
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON400 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 403:
var dest APIErrors
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON403 = &dest
case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 501:
var dest APIErrors
if err := json.Unmarshal(bodyBytes, &dest); err != nil {
return nil, err
}
response.JSON501 = &dest
}
return response, nil
}
// ParseStepRunListArchivesResponse parses an HTTP response from a StepRunListArchivesWithResponse call
func ParseStepRunListArchivesResponse(rsp *http.Response) (*StepRunListArchivesResponse, error) {
bodyBytes, err := io.ReadAll(rsp.Body)
+76 -8
View File
@@ -70,6 +70,8 @@ type ListWorkflowRunOpts struct {
type ReadTaskRunMetricsOpts struct {
CreatedAfter time.Time
CreatedBefore *time.Time
WorkflowIds []uuid.UUID
ParentTaskExternalID *pgtype.UUID
@@ -186,7 +188,7 @@ type OLAPRepository interface {
ReadTaskRun(ctx context.Context, taskExternalId string) (*sqlcv1.V1TasksOlap, error)
ReadWorkflowRun(ctx context.Context, workflowRunExternalId pgtype.UUID) (*V1WorkflowRunPopulator, error)
ReadTaskRunData(ctx context.Context, tenantId pgtype.UUID, taskId int64, taskInsertedAt pgtype.Timestamptz) (*sqlcv1.PopulateSingleTaskRunDataRow, *pgtype.UUID, error)
ReadTaskRunData(ctx context.Context, tenantId pgtype.UUID, taskId int64, taskInsertedAt pgtype.Timestamptz) (*sqlcv1.PopulateSingleTaskRunDataRow, pgtype.UUID, error)
ListTasks(ctx context.Context, tenantId string, opts ListTaskRunOpts) ([]*sqlcv1.PopulateTaskRunDataRow, int, error)
ListWorkflowRuns(ctx context.Context, tenantId string, opts ListWorkflowRunOpts) ([]*WorkflowRunData, int, error)
@@ -207,6 +209,8 @@ type OLAPRepository interface {
// ListTasksByExternalIds returns a list of tasks based on their external ids or the external id of their parent DAG.
// In the case of a DAG, we flatten the result into the list of tasks which belong to that DAG.
ListTasksByExternalIds(ctx context.Context, tenantId string, externalIds []string) ([]*sqlcv1.FlattenTasksByExternalIdsRow, error)
GetTaskTimings(ctx context.Context, tenantId string, workflowRunId pgtype.UUID, depth int32) ([]*sqlcv1.PopulateTaskRunDataRow, map[string]int32, error)
}
type OLAPRepositoryImpl struct {
@@ -411,15 +415,16 @@ func (r *OLAPRepositoryImpl) ReadWorkflowRun(ctx context.Context, workflowRunExt
}, nil
}
func (r *OLAPRepositoryImpl) ReadTaskRunData(ctx context.Context, tenantId pgtype.UUID, taskId int64, taskInsertedAt pgtype.Timestamptz) (*sqlcv1.PopulateSingleTaskRunDataRow, *pgtype.UUID, error) {
taskRun, err := r.queries.PopulateSingleTaskRunData(ctx, r.readPool, sqlcv1.PopulateSingleTaskRunDataParams{
func (r *OLAPRepositoryImpl) ReadTaskRunData(ctx context.Context, tenantId pgtype.UUID, taskId int64, taskInsertedAt pgtype.Timestamptz) (*sqlcv1.PopulateSingleTaskRunDataRow, pgtype.UUID, error) {
emptyUUID := pgtype.UUID{}
taskRun, err := r.queries.PopulateSingleTaskRunData(ctx, r.pool, sqlcv1.PopulateSingleTaskRunDataParams{
Taskid: taskId,
Tenantid: tenantId,
Taskinsertedat: taskInsertedAt,
})
if err != nil {
return nil, nil, err
return nil, emptyUUID, err
}
workflowRunId := pgtype.UUID{}
@@ -434,11 +439,11 @@ func (r *OLAPRepositoryImpl) ReadTaskRunData(ctx context.Context, tenantId pgtyp
})
if err != nil {
return nil, nil, err
return nil, emptyUUID, err
}
}
return taskRun, &workflowRunId, nil
return taskRun, workflowRunId, nil
}
func (r *OLAPRepositoryImpl) ListTasks(ctx context.Context, tenantId string, opts ListTaskRunOpts) ([]*sqlcv1.PopulateTaskRunDataRow, int, error) {
@@ -889,12 +894,18 @@ func (r *OLAPRepositoryImpl) ReadTaskRunMetrics(ctx context.Context, tenantId st
parentTaskExternalId = *opts.ParentTaskExternalID
}
res, err := r.queries.GetTenantStatusMetrics(context.Background(), r.readPool, sqlcv1.GetTenantStatusMetricsParams{
params := sqlcv1.GetTenantStatusMetricsParams{
Tenantid: sqlchelpers.UUIDFromStr(tenantId),
Createdafter: sqlchelpers.TimestamptzFromTime(opts.CreatedAfter),
WorkflowIds: workflowIds,
ParentTaskExternalId: parentTaskExternalId,
})
}
if opts.CreatedBefore != nil {
params.CreatedBefore = sqlchelpers.TimestamptzFromTime(*opts.CreatedBefore)
}
res, err := r.queries.GetTenantStatusMetrics(context.Background(), r.pool, params)
if err != nil {
if errors.Is(err, pgx.ErrNoRows) {
@@ -1265,3 +1276,60 @@ func (r *OLAPRepositoryImpl) ListWorkflowRunDisplayNames(ctx context.Context, te
Externalids: externalIds,
})
}
func (r *OLAPRepositoryImpl) GetTaskTimings(ctx context.Context, tenantId string, workflowRunId pgtype.UUID, depth int32) ([]*sqlcv1.PopulateTaskRunDataRow, map[string]int32, error) {
if depth > 10 {
return nil, nil, fmt.Errorf("depth too large")
}
// start out by getting a list of task external ids for the workflow run id
rootTaskExternalIds := make([]pgtype.UUID, 0)
rootTasks, err := r.queries.FlattenTasksByExternalIds(ctx, r.pool, sqlcv1.FlattenTasksByExternalIdsParams{
Externalids: []pgtype.UUID{workflowRunId},
Tenantid: sqlchelpers.UUIDFromStr(tenantId),
})
if err != nil {
return nil, nil, err
}
for _, task := range rootTasks {
rootTaskExternalIds = append(rootTaskExternalIds, task.ExternalID)
}
runsList, err := r.queries.GetRunsListRecursive(ctx, r.pool, sqlcv1.GetRunsListRecursiveParams{
Taskexternalids: rootTaskExternalIds,
Tenantid: sqlchelpers.UUIDFromStr(tenantId),
Depth: depth,
})
if err != nil {
return nil, nil, err
}
taskIds := make([]int64, 0)
taskInsertedAts := make([]pgtype.Timestamptz, 0)
// associate each run external id with a depth
idsToDepth := make(map[string]int32)
for _, row := range runsList {
taskIds = append(taskIds, row.ID)
taskInsertedAts = append(taskInsertedAts, row.InsertedAt)
idsToDepth[sqlchelpers.UUIDToStr(row.ExternalID)] = row.Depth
}
tasksWithData, err := r.queries.PopulateTaskRunData(ctx, r.pool, sqlcv1.PopulateTaskRunDataParams{
Taskids: taskIds,
Taskinsertedats: taskInsertedAts,
Tenantid: sqlchelpers.UUIDFromStr(tenantId),
})
if err != nil && !errors.Is(err, pgx.ErrNoRows) {
return nil, nil, err
}
return tasksWithData, idsToDepth, nil
}
+94 -7
View File
@@ -447,7 +447,9 @@ WITH input AS (
t.display_name,
t.input,
t.additional_metadata,
t.readable_status
t.readable_status,
t.parent_task_external_id,
t.workflow_run_id
FROM
v1_tasks_olap t
JOIN
@@ -471,10 +473,10 @@ WITH input AS (
relevant_events e
GROUP BY
e.tenant_id, e.task_id, e.task_inserted_at
), finished_ats AS (
), queued_ats AS (
SELECT
e.task_id::bigint,
MAX(e.event_timestamp) AS finished_at
MAX(e.event_timestamp) AS queued_at
FROM
relevant_events e
JOIN
@@ -484,7 +486,7 @@ WITH input AS (
AND e.task_inserted_at = mrc.task_inserted_at
AND e.retry_count = mrc.max_retry_count
WHERE
e.readable_status = ANY(ARRAY['COMPLETED', 'FAILED', 'CANCELLED']::v1_readable_status_olap[])
e.event_type = 'QUEUED'
GROUP BY e.task_id
), started_ats AS (
SELECT
@@ -501,6 +503,21 @@ WITH input AS (
WHERE
e.event_type = 'STARTED'
GROUP BY e.task_id
), finished_ats AS (
SELECT
e.task_id::bigint,
MAX(e.event_timestamp) AS finished_at
FROM
relevant_events e
JOIN
max_retry_counts mrc ON
e.tenant_id = mrc.tenant_id
AND e.task_id = mrc.task_id
AND e.task_inserted_at = mrc.task_inserted_at
AND e.retry_count = mrc.max_retry_count
WHERE
e.readable_status = ANY(ARRAY['COMPLETED', 'FAILED', 'CANCELLED']::v1_readable_status_olap[])
GROUP BY e.task_id
), error_message AS (
SELECT
DISTINCT ON (e.task_id) e.task_id::bigint,
@@ -528,7 +545,6 @@ WITH input AS (
GROUP BY
task_id
)
SELECT
t.tenant_id,
t.id,
@@ -545,10 +561,13 @@ SELECT
t.sticky,
t.display_name,
t.additional_metadata,
t.parent_task_external_id,
t.input,
t.readable_status::v1_readable_status_olap as status,
t.workflow_run_id,
f.finished_at::timestamptz as finished_at,
s.started_at::timestamptz as started_at,
q.queued_at::timestamptz as queued_at,
e.error_message as error_message,
o.output::jsonb as output
FROM
@@ -557,13 +576,14 @@ LEFT JOIN
finished_ats f ON f.task_id = t.id
LEFT JOIN
started_ats s ON s.task_id = t.id
LEFT JOIN
queued_ats q ON q.task_id = t.id
LEFT JOIN
error_message e ON e.task_id = t.id
LEFT JOIN
task_output o ON o.task_id = t.id
ORDER BY t.inserted_at DESC, t.id DESC;
-- name: UpdateTaskStatuses :one
WITH locked_events AS (
SELECT
@@ -988,6 +1008,9 @@ FROM v1_statuses_olap
WHERE
tenant_id = @tenantId::UUID
AND inserted_at >= @createdAfter::TIMESTAMPTZ
AND (
sqlc.narg('createdBefore')::TIMESTAMPTZ IS NULL OR inserted_at <= sqlc.narg('createdBefore')::TIMESTAMPTZ
)
AND (
sqlc.narg('workflowIds')::UUID[] IS NULL OR workflow_id = ANY(sqlc.narg('workflowIds')::UUID[])
)
@@ -1127,7 +1150,7 @@ WITH lookups AS (
FROM
lookups l
JOIN
v1_dag_to_task_olap dt ON l.dag_id = dt.dag_id
v1_dag_to_task_olap dt ON l.dag_id = dt.dag_id AND l.inserted_at = dt.dag_inserted_at
WHERE
l.dag_id IS NOT NULL
), unioned_tasks AS (
@@ -1150,6 +1173,7 @@ SELECT
t.tenant_id,
t.id,
t.inserted_at,
t.external_id,
t.latest_retry_count AS retry_count
FROM
v1_tasks_olap t
@@ -1170,3 +1194,66 @@ WHERE
AND lt.tenant_id = @tenantId::uuid
LIMIT 10000
;
-- name: GetRunsListRecursive :many
WITH RECURSIVE all_runs AS (
-- seed term
SELECT
t.id,
t.inserted_at,
t.tenant_id,
t.external_id,
t.parent_task_external_id,
0 AS depth
FROM
v1_lookup_table_olap lt
JOIN v1_tasks_olap t
ON t.inserted_at = lt.inserted_at
AND t.id = lt.task_id
WHERE
lt.external_id = ANY(@taskExternalIds::uuid[])
UNION ALL
-- single recursive term for both DAG- and TASK-driven children
SELECT
t.id,
t.inserted_at,
t.tenant_id,
t.external_id,
t.parent_task_external_id,
ar.depth + 1 AS depth
FROM
v1_runs_olap r
JOIN all_runs ar ON ar.external_id = r.parent_task_external_id
-- only present when r.kind = 'DAG'
LEFT JOIN v1_dag_to_task_olap dt ON r.kind = 'DAG' AND r.id = dt.dag_id AND r.inserted_at = dt.dag_inserted_at
-- pick the correct task row for either branch
JOIN v1_tasks_olap t
ON (
r.kind = 'DAG'
AND t.id = dt.task_id
AND t.inserted_at = dt.task_inserted_at
)
OR (
r.kind = 'TASK'
AND t.id = r.id
AND t.inserted_at = r.inserted_at
)
WHERE
r.tenant_id = @tenantId::uuid
AND ar.depth < @depth::int
)
SELECT
tenant_id,
id,
inserted_at,
external_id,
parent_task_external_id,
depth
FROM
all_runs
WHERE
tenant_id = @tenantId::uuid;
+170 -29
View File
@@ -111,7 +111,7 @@ WITH lookups AS (
FROM
lookups l
JOIN
v1_dag_to_task_olap dt ON l.dag_id = dt.dag_id
v1_dag_to_task_olap dt ON l.dag_id = dt.dag_id AND l.inserted_at = dt.dag_inserted_at
WHERE
l.dag_id IS NOT NULL
), unioned_tasks AS (
@@ -133,6 +133,7 @@ SELECT
t.tenant_id,
t.id,
t.inserted_at,
t.external_id,
t.latest_retry_count AS retry_count
FROM
v1_tasks_olap t
@@ -149,6 +150,7 @@ type FlattenTasksByExternalIdsRow struct {
TenantID pgtype.UUID `json:"tenant_id"`
ID int64 `json:"id"`
InsertedAt pgtype.Timestamptz `json:"inserted_at"`
ExternalID pgtype.UUID `json:"external_id"`
RetryCount int32 `json:"retry_count"`
}
@@ -166,6 +168,7 @@ func (q *Queries) FlattenTasksByExternalIds(ctx context.Context, db DBTX, arg Fl
&i.TenantID,
&i.ID,
&i.InsertedAt,
&i.ExternalID,
&i.RetryCount,
); err != nil {
return nil, err
@@ -178,6 +181,112 @@ func (q *Queries) FlattenTasksByExternalIds(ctx context.Context, db DBTX, arg Fl
return items, nil
}
const getRunsListRecursive = `-- name: GetRunsListRecursive :many
WITH RECURSIVE all_runs AS (
-- seed term
SELECT
t.id,
t.inserted_at,
t.tenant_id,
t.external_id,
t.parent_task_external_id,
0 AS depth
FROM
v1_lookup_table_olap lt
JOIN v1_tasks_olap t
ON t.inserted_at = lt.inserted_at
AND t.id = lt.task_id
WHERE
lt.external_id = ANY($2::uuid[])
UNION ALL
-- single recursive term for both DAG- and TASK-driven children
SELECT
t.id,
t.inserted_at,
t.tenant_id,
t.external_id,
t.parent_task_external_id,
ar.depth + 1 AS depth
FROM
v1_runs_olap r
JOIN all_runs ar ON ar.external_id = r.parent_task_external_id
-- only present when r.kind = 'DAG'
LEFT JOIN v1_dag_to_task_olap dt ON r.kind = 'DAG' AND r.id = dt.dag_id AND r.inserted_at = dt.dag_inserted_at
-- pick the correct task row for either branch
JOIN v1_tasks_olap t
ON (
r.kind = 'DAG'
AND t.id = dt.task_id
AND t.inserted_at = dt.task_inserted_at
)
OR (
r.kind = 'TASK'
AND t.id = r.id
AND t.inserted_at = r.inserted_at
)
WHERE
r.tenant_id = $1::uuid
AND ar.depth < $3::int
)
SELECT
tenant_id,
id,
inserted_at,
external_id,
parent_task_external_id,
depth
FROM
all_runs
WHERE
tenant_id = $1::uuid
`
type GetRunsListRecursiveParams struct {
Tenantid pgtype.UUID `json:"tenantid"`
Taskexternalids []pgtype.UUID `json:"taskexternalids"`
Depth int32 `json:"depth"`
}
type GetRunsListRecursiveRow struct {
TenantID pgtype.UUID `json:"tenant_id"`
ID int64 `json:"id"`
InsertedAt pgtype.Timestamptz `json:"inserted_at"`
ExternalID pgtype.UUID `json:"external_id"`
ParentTaskExternalID pgtype.UUID `json:"parent_task_external_id"`
Depth int32 `json:"depth"`
}
func (q *Queries) GetRunsListRecursive(ctx context.Context, db DBTX, arg GetRunsListRecursiveParams) ([]*GetRunsListRecursiveRow, error) {
rows, err := db.Query(ctx, getRunsListRecursive, arg.Tenantid, arg.Taskexternalids, arg.Depth)
if err != nil {
return nil, err
}
defer rows.Close()
var items []*GetRunsListRecursiveRow
for rows.Next() {
var i GetRunsListRecursiveRow
if err := rows.Scan(
&i.TenantID,
&i.ID,
&i.InsertedAt,
&i.ExternalID,
&i.ParentTaskExternalID,
&i.Depth,
); err != nil {
return nil, err
}
items = append(items, &i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const getTaskPointMetrics = `-- name: GetTaskPointMetrics :many
SELECT
DATE_BIN(
@@ -239,7 +348,7 @@ WITH task_external_ids AS (
SELECT external_id
FROM v1_runs_olap
WHERE (
$4::UUID IS NULL OR parent_task_external_id = $4::UUID
$5::UUID IS NULL OR parent_task_external_id = $5::UUID
)
)
SELECT
@@ -254,7 +363,10 @@ WHERE
tenant_id = $1::UUID
AND inserted_at >= $2::TIMESTAMPTZ
AND (
$3::UUID[] IS NULL OR workflow_id = ANY($3::UUID[])
$3::TIMESTAMPTZ IS NULL OR inserted_at <= $3::TIMESTAMPTZ
)
AND (
$4::UUID[] IS NULL OR workflow_id = ANY($4::UUID[])
)
AND external_id IN (
SELECT external_id
@@ -266,6 +378,7 @@ GROUP BY tenant_id
type GetTenantStatusMetricsParams struct {
Tenantid pgtype.UUID `json:"tenantid"`
Createdafter pgtype.Timestamptz `json:"createdafter"`
CreatedBefore pgtype.Timestamptz `json:"createdBefore"`
WorkflowIds []pgtype.UUID `json:"workflowIds"`
ParentTaskExternalId pgtype.UUID `json:"parentTaskExternalId"`
}
@@ -283,6 +396,7 @@ func (q *Queries) GetTenantStatusMetrics(ctx context.Context, db DBTX, arg GetTe
row := db.QueryRow(ctx, getTenantStatusMetrics,
arg.Tenantid,
arg.Createdafter,
arg.CreatedBefore,
arg.WorkflowIds,
arg.ParentTaskExternalId,
)
@@ -1092,7 +1206,9 @@ WITH input AS (
t.display_name,
t.input,
t.additional_metadata,
t.readable_status
t.readable_status,
t.parent_task_external_id,
t.workflow_run_id
FROM
v1_tasks_olap t
JOIN
@@ -1116,10 +1232,10 @@ WITH input AS (
relevant_events e
GROUP BY
e.tenant_id, e.task_id, e.task_inserted_at
), finished_ats AS (
), queued_ats AS (
SELECT
e.task_id::bigint,
MAX(e.event_timestamp) AS finished_at
MAX(e.event_timestamp) AS queued_at
FROM
relevant_events e
JOIN
@@ -1129,7 +1245,7 @@ WITH input AS (
AND e.task_inserted_at = mrc.task_inserted_at
AND e.retry_count = mrc.max_retry_count
WHERE
e.readable_status = ANY(ARRAY['COMPLETED', 'FAILED', 'CANCELLED']::v1_readable_status_olap[])
e.event_type = 'QUEUED'
GROUP BY e.task_id
), started_ats AS (
SELECT
@@ -1146,6 +1262,21 @@ WITH input AS (
WHERE
e.event_type = 'STARTED'
GROUP BY e.task_id
), finished_ats AS (
SELECT
e.task_id::bigint,
MAX(e.event_timestamp) AS finished_at
FROM
relevant_events e
JOIN
max_retry_counts mrc ON
e.tenant_id = mrc.tenant_id
AND e.task_id = mrc.task_id
AND e.task_inserted_at = mrc.task_inserted_at
AND e.retry_count = mrc.max_retry_count
WHERE
e.readable_status = ANY(ARRAY['COMPLETED', 'FAILED', 'CANCELLED']::v1_readable_status_olap[])
GROUP BY e.task_id
), error_message AS (
SELECT
DISTINCT ON (e.task_id) e.task_id::bigint,
@@ -1173,7 +1304,6 @@ WITH input AS (
GROUP BY
task_id
)
SELECT
t.tenant_id,
t.id,
@@ -1190,10 +1320,13 @@ SELECT
t.sticky,
t.display_name,
t.additional_metadata,
t.parent_task_external_id,
t.input,
t.readable_status::v1_readable_status_olap as status,
t.workflow_run_id,
f.finished_at::timestamptz as finished_at,
s.started_at::timestamptz as started_at,
q.queued_at::timestamptz as queued_at,
e.error_message as error_message,
o.output::jsonb as output
FROM
@@ -1202,6 +1335,8 @@ LEFT JOIN
finished_ats f ON f.task_id = t.id
LEFT JOIN
started_ats s ON s.task_id = t.id
LEFT JOIN
queued_ats q ON q.task_id = t.id
LEFT JOIN
error_message e ON e.task_id = t.id
LEFT JOIN
@@ -1216,27 +1351,30 @@ type PopulateTaskRunDataParams struct {
}
type PopulateTaskRunDataRow struct {
TenantID pgtype.UUID `json:"tenant_id"`
ID int64 `json:"id"`
InsertedAt pgtype.Timestamptz `json:"inserted_at"`
ExternalID pgtype.UUID `json:"external_id"`
Queue string `json:"queue"`
ActionID string `json:"action_id"`
StepID pgtype.UUID `json:"step_id"`
WorkflowID pgtype.UUID `json:"workflow_id"`
WorkflowVersionID pgtype.UUID `json:"workflow_version_id"`
ScheduleTimeout string `json:"schedule_timeout"`
StepTimeout pgtype.Text `json:"step_timeout"`
Priority pgtype.Int4 `json:"priority"`
Sticky V1StickyStrategyOlap `json:"sticky"`
DisplayName string `json:"display_name"`
AdditionalMetadata []byte `json:"additional_metadata"`
Input []byte `json:"input"`
Status V1ReadableStatusOlap `json:"status"`
FinishedAt pgtype.Timestamptz `json:"finished_at"`
StartedAt pgtype.Timestamptz `json:"started_at"`
ErrorMessage pgtype.Text `json:"error_message"`
Output []byte `json:"output"`
TenantID pgtype.UUID `json:"tenant_id"`
ID int64 `json:"id"`
InsertedAt pgtype.Timestamptz `json:"inserted_at"`
ExternalID pgtype.UUID `json:"external_id"`
Queue string `json:"queue"`
ActionID string `json:"action_id"`
StepID pgtype.UUID `json:"step_id"`
WorkflowID pgtype.UUID `json:"workflow_id"`
WorkflowVersionID pgtype.UUID `json:"workflow_version_id"`
ScheduleTimeout string `json:"schedule_timeout"`
StepTimeout pgtype.Text `json:"step_timeout"`
Priority pgtype.Int4 `json:"priority"`
Sticky V1StickyStrategyOlap `json:"sticky"`
DisplayName string `json:"display_name"`
AdditionalMetadata []byte `json:"additional_metadata"`
ParentTaskExternalID pgtype.UUID `json:"parent_task_external_id"`
Input []byte `json:"input"`
Status V1ReadableStatusOlap `json:"status"`
WorkflowRunID pgtype.UUID `json:"workflow_run_id"`
FinishedAt pgtype.Timestamptz `json:"finished_at"`
StartedAt pgtype.Timestamptz `json:"started_at"`
QueuedAt pgtype.Timestamptz `json:"queued_at"`
ErrorMessage pgtype.Text `json:"error_message"`
Output []byte `json:"output"`
}
func (q *Queries) PopulateTaskRunData(ctx context.Context, db DBTX, arg PopulateTaskRunDataParams) ([]*PopulateTaskRunDataRow, error) {
@@ -1264,10 +1402,13 @@ func (q *Queries) PopulateTaskRunData(ctx context.Context, db DBTX, arg Populate
&i.Sticky,
&i.DisplayName,
&i.AdditionalMetadata,
&i.ParentTaskExternalID,
&i.Input,
&i.Status,
&i.WorkflowRunID,
&i.FinishedAt,
&i.StartedAt,
&i.QueuedAt,
&i.ErrorMessage,
&i.Output,
); err != nil {
+3 -3
View File
@@ -148,7 +148,7 @@ func (r *TriggerRepositoryImpl) TriggerFromEvents(ctx context.Context, tenantId
for _, opt := range opts {
triggerConverter := &TriggeredByEvent{
l: r.l,
eventId: opt.EventId,
eventID: opt.EventId,
eventKey: workflow.EventKey,
}
@@ -309,7 +309,7 @@ type TriggeredBy interface {
type TriggeredByEvent struct {
l *zerolog.Logger
eventId string
eventID string
eventKey string
}
@@ -338,7 +338,7 @@ func cleanAdditionalMetadata(additionalMetadata []byte) map[string]interface{} {
func (t *TriggeredByEvent) ToMetadata(additionalMetadata []byte) []byte {
res := cleanAdditionalMetadata(additionalMetadata)
res["hatchet__event_id"] = t.eventId
res["hatchet__event_id"] = t.eventID
res["hatchet__event_key"] = t.eventKey
resBytes, err := json.Marshal(res)