diff --git a/api/v2/api.gen.go b/api/v2/api.gen.go index 707142eac..492e4ab6f 100644 --- a/api/v2/api.gen.go +++ b/api/v2/api.gen.go @@ -612,6 +612,9 @@ type DAGRunDetails struct { // StatusLabel Human-readable status description for the DAG-run StatusLabel StatusLabel `json:"statusLabel"` + // Tags List of tags for categorizing and filtering DAG runs + Tags *[]string `json:"tags,omitempty"` + // WorkerId ID of the worker that executed this DAG-run ('local' for local execution) WorkerId *string `json:"workerId,omitempty"` } @@ -674,6 +677,9 @@ type DAGRunSummary struct { // StatusLabel Human-readable status description for the DAG-run StatusLabel StatusLabel `json:"statusLabel"` + // Tags List of tags for categorizing and filtering DAG runs + Tags *[]string `json:"tags,omitempty"` + // WorkerId ID of the worker that executed this DAG-run ('local' for local execution) WorkerId *string `json:"workerId,omitempty"` } @@ -12911,221 +12917,221 @@ var swaggerSpec = []string{ "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/v2/api.yaml b/api/v2/api.yaml index ec6116fc1..345816c11 100644 --- a/api/v2/api.yaml +++ b/api/v2/api.yaml @@ -4138,6 +4138,11 @@ components: workerId: type: string description: "ID of the worker that executed this DAG-run ('local' for local execution)" + tags: + type: array + items: + type: string + description: "List of tags for categorizing and filtering DAG runs" required: - rootDAGRunName - rootDAGRunId diff --git a/internal/cmd/migrator.go b/internal/cmd/migrator.go index 9211afdde..d2400b32f 100644 --- a/internal/cmd/migrator.go +++ b/internal/cmd/migrator.go @@ -254,19 +254,29 @@ func (m *historyMigrator) convertStatus(legacy *legacymodel.Status, dag *core.DA createdAt = startedAt.UnixMilli() } + // Set metadata from DAG if available + var tags []string + var preconditions []*core.Condition + if dag != nil { + tags = dag.Tags + preconditions = dag.Preconditions + } + status := &exec.DAGRunStatus{ - Name: legacy.Name, - DAGRunID: legacy.RequestID, - Status: legacy.Status, - PID: exec.PID(legacy.PID), - Log: legacy.Log, - Nodes: make([]*exec.Node, 0), - Params: legacy.Params, - ParamsList: legacy.ParamsList, - CreatedAt: createdAt, - StartedAt: formatTime(startedAt), - FinishedAt: formatTime(finishedAt), - QueuedAt: formatTime(startedAt), // Use StartedAt as QueuedAt for migration + Name: legacy.Name, + DAGRunID: legacy.RequestID, + Status: legacy.Status, + PID: exec.PID(legacy.PID), + Log: legacy.Log, + Nodes: make([]*exec.Node, 0), + Params: legacy.Params, + ParamsList: legacy.ParamsList, + CreatedAt: createdAt, + StartedAt: formatTime(startedAt), + FinishedAt: formatTime(finishedAt), + QueuedAt: formatTime(startedAt), // Use StartedAt as QueuedAt for migration + Tags: tags, + Preconditions: preconditions, } // Convert nodes @@ -288,11 +298,6 @@ func (m *historyMigrator) convertStatus(legacy *legacymodel.Status, dag *core.DA status.OnCancel = m.convertNode(legacy.OnCancel) } - // Set preconditions from DAG if available - if dag != nil { - status.Preconditions = dag.Preconditions - } - return status } diff --git a/internal/core/exec/runstatus.go b/internal/core/exec/runstatus.go index ba55fa7fe..3f64d2978 100644 --- a/internal/core/exec/runstatus.go +++ b/internal/core/exec/runstatus.go @@ -29,6 +29,7 @@ func InitialStatus(dag *core.DAG) DAGRunStatus { StartedAt: stringutil.FormatTime(time.Time{}), FinishedAt: stringutil.FormatTime(time.Time{}), Preconditions: dag.Preconditions, + Tags: dag.Tags, } } @@ -59,6 +60,7 @@ type DAGRunStatus struct { Params string `json:"params,omitempty"` ParamsList []string `json:"paramsList,omitempty"` Preconditions []*core.Condition `json:"preconditions,omitempty"` + Tags []string `json:"tags,omitempty"` } // DAGRun returns a reference to the dag-run associated with this status diff --git a/internal/core/spec/step.go b/internal/core/spec/step.go index 86ec23b1b..8747faa65 100644 --- a/internal/core/spec/step.go +++ b/internal/core/spec/step.go @@ -852,6 +852,28 @@ func buildMultipleCommands(val []any, result *core.Step) error { strVal = tv case int, int64, uint64, float64, bool: strVal = fmt.Sprintf("%v", tv) + case map[string]any: + if len(tv) == 1 { + for k, val := range tv { + switch v2 := val.(type) { + case string, int, int64, uint64, float64, bool: + strVal = fmt.Sprintf("%s: %v", k, v2) + default: + // Nested maps or arrays are too complex, fall through to error + return core.NewValidationError( + fmt.Sprintf("command[%d]", i), + v, + fmt.Errorf("command array elements must be strings. If this contains a colon, wrap it in quotes. Got nested %T", v2), + ) + } + } + } else { + return core.NewValidationError( + fmt.Sprintf("command[%d]", i), + v, + fmt.Errorf("command array elements must be strings. If this contains a colon, wrap it in quotes"), + ) + } default: return core.NewValidationError( fmt.Sprintf("command[%d]", i), diff --git a/internal/core/spec/step_test.go b/internal/core/spec/step_test.go index 0c0822989..aec8aa2f7 100644 --- a/internal/core/spec/step_test.go +++ b/internal/core/spec/step_test.go @@ -1167,8 +1167,16 @@ func TestBuildMultipleCommands(t *testing.T) { }, }, { - name: "RejectsMapType", + name: "AcceptsSingleKeyMap", commands: []any{"echo hello", map[string]any{"key": "value"}}, + expectedCommands: []core.CommandEntry{ + {Command: "echo", Args: []string{"hello"}, CmdWithArgs: "echo hello"}, + {Command: "key:", Args: []string{"value"}, CmdWithArgs: "key: value"}, + }, + }, + { + name: "RejectsMultiKeyMap", + commands: []any{"echo hello", map[string]any{"key1": "val1", "key2": "val2"}}, wantErr: true, }, { diff --git a/internal/intg/mltcmd_test.go b/internal/intg/mltcmd_test.go index 2eb91be12..7317da55c 100644 --- a/internal/intg/mltcmd_test.go +++ b/internal/intg/mltcmd_test.go @@ -185,6 +185,24 @@ steps: "STEP2_OUT": "from step2\ndone", }) }) + + t.Run("CommandWithColonInArray", func(t *testing.T) { + t.Parallel() + + dag := th.DAG(t, ` +steps: + - name: colon-test + command: + - echo SATID: 123 + output: OUT +`) + agent := dag.Agent() + agent.RunSuccess(t) + dag.AssertLatestStatus(t, core.Succeeded) + dag.AssertOutputs(t, map[string]any{ + "OUT": "SATID: 123", + }) + }) } func TestMultipleCommands_Docker(t *testing.T) { diff --git a/internal/persis/filedagrun/setup_test.go b/internal/persis/filedagrun/setup_test.go index c30f134f7..d3422b842 100644 --- a/internal/persis/filedagrun/setup_test.go +++ b/internal/persis/filedagrun/setup_test.go @@ -39,7 +39,13 @@ func (th StoreTest) CreateAttempt(t *testing.T, ts time.Time, dagRunID string, s t.Helper() dag := th.DAG("test_DAG") - attempt, err := th.Store.CreateAttempt(th.Context, dag.DAG, ts, dagRunID, exec.NewDAGRunAttemptOptions{}) + return th.CreateAttemptWithDAG(t, ts, dagRunID, s, dag.DAG) +} + +func (th StoreTest) CreateAttemptWithDAG(t *testing.T, ts time.Time, dagRunID string, s core.Status, dag *core.DAG) *Attempt { + t.Helper() + + attempt, err := th.Store.CreateAttempt(th.Context, dag, ts, dagRunID, exec.NewDAGRunAttemptOptions{}) require.NoError(t, err) err = attempt.Open(th.Context) @@ -49,7 +55,7 @@ func (th StoreTest) CreateAttempt(t *testing.T, ts time.Time, dagRunID string, s _ = attempt.Close(th.Context) }() - dagRunStatus := exec.InitialStatus(dag.DAG) + dagRunStatus := exec.InitialStatus(dag) dagRunStatus.DAGRunID = dagRunID dagRunStatus.Status = s diff --git a/internal/persis/filedagrun/store.go b/internal/persis/filedagrun/store.go index 7e0a5c92d..a0af65dfb 100644 --- a/internal/persis/filedagrun/store.go +++ b/internal/persis/filedagrun/store.go @@ -203,6 +203,28 @@ func (store *Store) collectStatusesFromRoots( tag.Error(err)) continue } + + // Filter by tags (AND logic) + if len(opts.Tags) > 0 { + match := true + for _, t := range opts.Tags { + found := false + for _, st := range status.Tags { + if st == t { + found = true + break + } + } + if !found { + match = false + break + } + } + if !match { + continue + } + } + if !hasStatusFilter { statuses = append(statuses, status) continue diff --git a/internal/persis/filedagrun/store_test.go b/internal/persis/filedagrun/store_test.go index f17754c2e..9105adcca 100644 --- a/internal/persis/filedagrun/store_test.go +++ b/internal/persis/filedagrun/store_test.go @@ -522,6 +522,59 @@ func TestListStatuses(t *testing.T) { assert.Equal(t, "dagrun-id-2", statuses[1].DAGRunID) assert.Equal(t, "dagrun-id-1", statuses[2].DAGRunID) }) + + t.Run("FilterByTags", func(t *testing.T) { + th := setupTestStore(t) + + ts := time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC) + + // Create runs with different tags + run1 := th.DAG("dag1") + run1.Tags = []string{"prod", "batch"} + th.CreateAttemptWithDAG(t, ts, "run-1", core.Succeeded, run1.DAG) + + run2 := th.DAG("dag2") + run2.Tags = []string{"prod", "api"} + th.CreateAttemptWithDAG(t, ts, "run-2", core.Succeeded, run2.DAG) + + run3 := th.DAG("dag3") + run3.Tags = []string{"dev"} + th.CreateAttemptWithDAG(t, ts, "run-3", core.Succeeded, run3.DAG) + + // Filter by tag "prod" (should match run-1 and run-2) + statuses, err := th.Store.ListStatuses(th.Context, + exec.WithTags([]string{"prod"}), + exec.WithFrom(exec.NewUTC(ts)), + ) + require.NoError(t, err) + assert.Len(t, statuses, 2) + + // Filter by tags "prod" AND "batch" (should match only run-1) + statuses, err = th.Store.ListStatuses(th.Context, + exec.WithTags([]string{"prod", "batch"}), + exec.WithFrom(exec.NewUTC(ts)), + ) + require.NoError(t, err) + assert.Len(t, statuses, 1) + assert.Equal(t, "run-1", statuses[0].DAGRunID) + + // Filter by tag "dev" (should match only run-3) + statuses, err = th.Store.ListStatuses(th.Context, + exec.WithTags([]string{"dev"}), + exec.WithFrom(exec.NewUTC(ts)), + ) + require.NoError(t, err) + assert.Len(t, statuses, 1) + assert.Equal(t, "run-3", statuses[0].DAGRunID) + + // Filter by tag "nonexistent" (should match nothing) + statuses, err = th.Store.ListStatuses(th.Context, + exec.WithTags([]string{"nonexistent"}), + exec.WithFrom(exec.NewUTC(ts)), + ) + require.NoError(t, err) + assert.Empty(t, statuses) + }) } func TestLatestStatusTimezone(t *testing.T) { diff --git a/internal/service/coordinator/handler.go b/internal/service/coordinator/handler.go index 1d632f28a..82eb055f9 100644 --- a/internal/service/coordinator/handler.go +++ b/internal/service/coordinator/handler.go @@ -315,7 +315,7 @@ func (h *Handler) createAttemptForTask(ctx context.Context, task *coordinatorv1. return fmt.Errorf("failed to open attempt: %w", err) } - h.writeInitialStatus(ctx, attempt, dag.Name, task.DagRunId, task.AttemptKey, exec.DAGRunRef{}) + h.writeInitialStatus(ctx, attempt, dag.Name, task.DagRunId, task.AttemptKey, exec.DAGRunRef{}, dag.Tags) h.attemptsMu.Lock() h.openAttempts[task.DagRunId] = attempt @@ -367,7 +367,7 @@ func (h *Handler) createSubAttemptForTask(ctx context.Context, task *coordinator return fmt.Errorf("failed to open sub-attempt: %w", err) } - h.writeInitialStatus(ctx, attempt, task.Target, task.DagRunId, task.AttemptKey, rootRef) + h.writeInitialStatus(ctx, attempt, task.Target, task.DagRunId, task.AttemptKey, rootRef, dag.Tags) h.attemptsMu.Lock() h.openAttempts[task.DagRunId] = attempt @@ -385,7 +385,7 @@ func (h *Handler) createSubAttemptForTask(ctx context.Context, task *coordinator // writeInitialStatus writes an initial NotStarted status to the attempt. // This ensures the status file is not empty when read before the worker reports its first status. -func (h *Handler) writeInitialStatus(ctx context.Context, attempt exec.DAGRunAttempt, dagName, dagRunID, attemptKey string, root exec.DAGRunRef) { +func (h *Handler) writeInitialStatus(ctx context.Context, attempt exec.DAGRunAttempt, dagName, dagRunID, attemptKey string, root exec.DAGRunRef, tags []string) { initialStatus := exec.DAGRunStatus{ Name: dagName, DAGRunID: dagRunID, @@ -394,6 +394,7 @@ func (h *Handler) writeInitialStatus(ctx context.Context, attempt exec.DAGRunAtt Status: core.NotStarted, StartedAt: time.Now().UTC().Format(time.RFC3339), Root: root, + Tags: tags, } if err := attempt.Write(ctx, initialStatus); err != nil { logger.Warn(ctx, "Failed to write initial status", tag.Error(err), tag.RunID(dagRunID)) diff --git a/internal/service/frontend/api/v2/dagruns.go b/internal/service/frontend/api/v2/dagruns.go index 4c6008c88..47be99017 100644 --- a/internal/service/frontend/api/v2/dagruns.go +++ b/internal/service/frontend/api/v2/dagruns.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "log/slog" - "math" "net/http" "os" "path/filepath" @@ -334,90 +333,18 @@ func (a *API) ListDAGRunsByName(ctx context.Context, request api.ListDAGRunsByNa } func (a *API) listDAGRuns(ctx context.Context, opts []exec.ListDAGRunStatusesOption) ([]api.DAGRunSummary, error) { - // Extract tags filter and build store options (tags are filtered client-side) - tagsFilter, storeOpts := extractTagsFilter(opts) - - // Build allowed DAG names set if tags filter is specified - allowedDAGNames, err := a.buildAllowedDAGNames(ctx, tagsFilter) - if err != nil { - return nil, err - } - if tagsFilter != nil && len(allowedDAGNames) == 0 { - return []api.DAGRunSummary{}, nil - } - - statuses, err := a.dagRunStore.ListStatuses(ctx, storeOpts...) + statuses, err := a.dagRunStore.ListStatuses(ctx, opts...) if err != nil { return nil, fmt.Errorf("error listing dag-runs: %w", err) } dagRuns := make([]api.DAGRunSummary, 0, len(statuses)) for _, status := range statuses { - if allowedDAGNames != nil { - if _, ok := allowedDAGNames[status.Name]; !ok { - continue - } - } dagRuns = append(dagRuns, toDAGRunSummary(*status)) } return dagRuns, nil } -// extractTagsFilter separates tag filter options from other options. -// Tags are filtered client-side since the store doesn't support tag filtering. -func extractTagsFilter(opts []exec.ListDAGRunStatusesOption) ([]string, []exec.ListDAGRunStatusesOption) { - var tagsFilter []string - storeOpts := make([]exec.ListDAGRunStatusesOption, 0, len(opts)) - - for _, opt := range opts { - tempOpts := exec.ListDAGRunStatusesOptions{} - opt(&tempOpts) - if len(tempOpts.Tags) > 0 { - tagsFilter = tempOpts.Tags - } else { - storeOpts = append(storeOpts, opt) - } - } - return tagsFilter, storeOpts -} - -// buildAllowedDAGNames returns a set of DAG names that match all specified tags. -// Returns nil if no tags filter is specified (all DAGs allowed). -func (a *API) buildAllowedDAGNames(ctx context.Context, tagsFilter []string) (map[string]struct{}, error) { - if len(tagsFilter) == 0 { - return nil, nil - } - - // Use a large page size to get all DAGs in one request - paginator := exec.NewPaginator(1, math.MaxInt) - result, _, err := a.dagStore.List(ctx, exec.ListDAGsOptions{Paginator: &paginator}) - if err != nil { - return nil, fmt.Errorf("error getting DAGs for tag filter: %w", err) - } - - allowedNames := make(map[string]struct{}) - for _, dag := range result.Items { - if hasAllTags(dag.Tags, tagsFilter) { - allowedNames[dag.Name] = struct{}{} - } - } - return allowedNames, nil -} - -// hasAllTags checks if dagTags contains all requiredTags (case-insensitive) -func hasAllTags(dagTags, requiredTags []string) bool { - tagSet := make(map[string]struct{}, len(dagTags)) - for _, t := range dagTags { - tagSet[strings.ToLower(t)] = struct{}{} - } - for _, req := range requiredTags { - if _, ok := tagSet[strings.ToLower(req)]; !ok { - return false - } - } - return true -} - // parseCommaSeparatedTags parses a comma-separated string of tags into a slice. // Tags are normalized to lowercase and deduplicated. func parseCommaSeparatedTags(tagsParam *string) []string { diff --git a/internal/service/frontend/api/v2/transformer.go b/internal/service/frontend/api/v2/transformer.go index ea52e99ca..3273e29e1 100644 --- a/internal/service/frontend/api/v2/transformer.go +++ b/internal/service/frontend/api/v2/transformer.go @@ -147,6 +147,7 @@ func toDAGRunSummary(s exec.DAGRunStatus) api.DAGRunSummary { Status: api.Status(s.Status), StatusLabel: api.StatusLabel(s.Status.String()), WorkerId: ptrOf(s.WorkerID), + Tags: &s.Tags, } } @@ -179,6 +180,7 @@ func toDAGRunDetails(s exec.DAGRunStatus) api.DAGRunDetails { OnFailure: ptrOf(toNode(s.OnFailure)), OnCancel: ptrOf(toNode(s.OnCancel)), OnExit: ptrOf(toNode(s.OnExit)), + Tags: &s.Tags, } } diff --git a/ui/src/api/v2/schema.ts b/ui/src/api/v2/schema.ts index 548e256d5..8b5f894cf 100644 --- a/ui/src/api/v2/schema.ts +++ b/ui/src/api/v2/schema.ts @@ -721,6 +721,26 @@ export interface paths { patch?: never; trace?: never; }; + "/dag-runs/{name}/{dagRunId}/log/download": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Download full execution log of a DAG-run + * @description Downloads the entire execution log file for a DAG-run + */ + get: operations["downloadDAGRunLog"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/dag-runs/{name}/{dagRunId}/outputs": { parameters: { query?: never; @@ -801,6 +821,26 @@ export interface paths { patch?: never; trace?: never; }; + "/dag-runs/{name}/{dagRunId}/steps/{stepName}/log/download": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Download log for a specific step in a DAG-run + * @description Downloads the entire log file for an individual step in a DAG-run + */ + get: operations["downloadDAGRunStepLog"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/dag-runs/{name}/{dagRunId}/steps/{stepName}/messages": { parameters: { query?: never; @@ -921,6 +961,26 @@ export interface paths { patch?: never; trace?: never; }; + "/dag-runs/{name}/{dagRunId}/sub-dag-runs/{subDAGRunId}/log/download": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Download log for a specific sub DAG-run + * @description Downloads the entire log file for an individual sub DAG-run + */ + get: operations["downloadSubDAGRunLog"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/dag-runs/{name}/{dagRunId}/sub-dag-runs/{subDAGRunId}/steps/{stepName}/log": { parameters: { query?: never; @@ -941,6 +1001,26 @@ export interface paths { patch?: never; trace?: never; }; + "/dag-runs/{name}/{dagRunId}/sub-dag-runs/{subDAGRunId}/steps/{stepName}/log/download": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** + * Download log for a specific step in a sub DAG-run + * @description Downloads the entire log file for an individual step in a sub DAG-run + */ + get: operations["downloadSubDAGRunStepLog"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/dag-runs/{name}/{dagRunId}/sub-dag-runs/{subDAGRunId}/steps/{stepName}/messages": { parameters: { query?: never; @@ -1694,6 +1774,8 @@ export interface components { params?: string; /** @description ID of the worker that executed this DAG-run ('local' for local execution) */ workerId?: string; + /** @description List of tags for categorizing and filtering DAG runs */ + tags?: string[]; }; /** @description Detailed status of a DAG-run including sub DAG-run nodes */ DAGRunDetails: components["schemas"]["DAGRunSummary"] & { @@ -4480,6 +4562,54 @@ export interface operations { }; }; }; + downloadDAGRunLog: { + parameters: { + query?: { + /** @description name of the remote node */ + remoteNode?: components["parameters"]["RemoteNode"]; + }; + header?: never; + path: { + /** @description name of the DAG */ + name: components["parameters"]["DAGName"]; + /** @description ID of the DAG-run or 'latest' to get the most recent DAG-run */ + dagRunId: components["parameters"]["DAGRunId"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Log file content */ + 200: { + headers: { + /** @description Attachment filename */ + "Content-Disposition"?: string; + [name: string]: unknown; + }; + content: { + "text/plain": string; + }; + }; + /** @description Log file not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Generic error response */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; getDAGRunOutputs: { parameters: { query?: { @@ -4662,6 +4792,58 @@ export interface operations { }; }; }; + downloadDAGRunStepLog: { + parameters: { + query?: { + /** @description name of the remote node */ + remoteNode?: components["parameters"]["RemoteNode"]; + /** @description Whether to return stdout or stderr logs */ + stream?: components["parameters"]["Stream"]; + }; + header?: never; + path: { + /** @description name of the DAG */ + name: components["parameters"]["DAGName"]; + /** @description ID of the DAG-run or 'latest' to get the most recent DAG-run */ + dagRunId: components["parameters"]["DAGRunId"]; + /** @description name of the step */ + stepName: components["parameters"]["StepName"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Log file content */ + 200: { + headers: { + /** @description Attachment filename */ + "Content-Disposition"?: string; + [name: string]: unknown; + }; + content: { + "text/plain": string; + }; + }; + /** @description Log file not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Generic error response */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; getDAGRunStepMessages: { parameters: { query?: { @@ -4999,6 +5181,56 @@ export interface operations { }; }; }; + downloadSubDAGRunLog: { + parameters: { + query?: { + /** @description name of the remote node */ + remoteNode?: components["parameters"]["RemoteNode"]; + }; + header?: never; + path: { + /** @description name of the DAG */ + name: components["parameters"]["DAGName"]; + /** @description ID of the DAG-run or 'latest' to get the most recent DAG-run */ + dagRunId: components["parameters"]["DAGRunId"]; + /** @description ID of the sub DAG-run to download the log for */ + subDAGRunId: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Log file content */ + 200: { + headers: { + /** @description Attachment filename */ + "Content-Disposition"?: string; + [name: string]: unknown; + }; + content: { + "text/plain": string; + }; + }; + /** @description Log file not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Generic error response */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; getSubDAGRunStepLog: { parameters: { query?: { @@ -5059,6 +5291,60 @@ export interface operations { }; }; }; + downloadSubDAGRunStepLog: { + parameters: { + query?: { + /** @description name of the remote node */ + remoteNode?: components["parameters"]["RemoteNode"]; + /** @description Whether to return stdout or stderr logs */ + stream?: components["parameters"]["Stream"]; + }; + header?: never; + path: { + /** @description name of the DAG */ + name: components["parameters"]["DAGName"]; + /** @description ID of the DAG-run or 'latest' to get the most recent DAG-run */ + dagRunId: components["parameters"]["DAGRunId"]; + /** @description ID of the sub DAG-run to download the step log for */ + subDAGRunId: string; + /** @description name of the step */ + stepName: components["parameters"]["StepName"]; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Log file content */ + 200: { + headers: { + /** @description Attachment filename */ + "Content-Disposition"?: string; + [name: string]: unknown; + }; + content: { + "text/plain": string; + }; + }; + /** @description Log file not found */ + 404: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + /** @description Generic error response */ + default: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["Error"]; + }; + }; + }; + }; getSubDAGRunStepMessages: { parameters: { query?: { diff --git a/ui/src/features/dags/components/dag-details/DAGDetailsContent.tsx b/ui/src/features/dags/components/dag-details/DAGDetailsContent.tsx index f19a1d46a..aff1d0cdf 100644 --- a/ui/src/features/dags/components/dag-details/DAGDetailsContent.tsx +++ b/ui/src/features/dags/components/dag-details/DAGDetailsContent.tsx @@ -108,9 +108,9 @@ const DAGDetailsContent: React.FC = ({ navigateToStatusTab={navigateToStatusTab} /> )} -
+
{/* Desktop Tabs (lg and up) */} -
+
{isModal ? ( = ({
-
+