Skip to content

Commit c7fb162

Browse files
authored
Add curated TUI table overrides for list commands (#4731)
## Why PR #4729 adds an interactive table for list commands, but its default behavior is to fall through to template rendering when no table configuration is registered. For the commands people use most, the table should start with the fields they actually care about, and it should enable search where the backend supports it. These 15 commands were selected based on CLI usage as some of the most frequently used list commands by customer count. ## Changes Before: interactive list commands used template rendering with no curated table configuration. Now: 15 common list commands register curated columns, and jobs list plus pipelines list-pipelines also enable / search in the TUI. This PR builds on #4729. It does not change the table engine itself. It adds per-command overrides that: - Choose the columns and column order for each command - Preserve the existing non-interactive template output - Enable server-side search where the API supports filtering Commands covered: jobs list, clusters list, pipelines list-pipelines, warehouses list, catalogs list, schemas list, tables list, apps list, repos list, instance-pools list, serving-endpoints list, volumes list, external-locations list, alerts list, workspace list ### Post-review fixes - Disable TUI search when `--filter` is already set on pipelines (composite filters are unsupported) - Clear stale pagination state (`PageToken`, `Offset`) before constructing search iterators - Add backslash escaping to pipelines LIKE filter - Extract repeated type assertions in apps Extract closures to local variables ## Test plan - `go build ./...` - Manual smoke test: verify curated columns for jobs list, clusters list, catalogs list, warehouses list, serving-endpoints list - Manual smoke test: verify / search for jobs list and pipelines list-pipelines - `make checks` passes - `make lintfull` passes (0 issues)
1 parent ed23efc commit c7fb162

19 files changed

Lines changed: 615 additions & 0 deletions

File tree

cmd/workspace/alerts/overrides.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package alerts
2+
3+
import (
4+
"github.com/databricks/cli/libs/cmdio"
5+
"github.com/databricks/cli/libs/tableview"
6+
"github.com/databricks/databricks-sdk-go/service/sql"
7+
"github.com/spf13/cobra"
8+
)
9+
10+
func listOverride(listCmd *cobra.Command, listReq *sql.ListAlertsRequest) {
11+
listCmd.Annotations["template"] = cmdio.Heredoc(`
12+
{{range .}}{{green "%s" .Id}} {{.DisplayName}} {{.State}}
13+
{{end}}`)
14+
15+
columns := []tableview.ColumnDef{
16+
{Header: "ID", Extract: func(v any) string {
17+
return v.(sql.ListAlertsResponseAlert).Id
18+
}},
19+
{Header: "Name", Extract: func(v any) string {
20+
return v.(sql.ListAlertsResponseAlert).DisplayName
21+
}},
22+
{Header: "State", Extract: func(v any) string {
23+
return string(v.(sql.ListAlertsResponseAlert).State)
24+
}},
25+
}
26+
27+
tableview.RegisterConfig(listCmd, tableview.TableConfig{Columns: columns})
28+
}
29+
30+
func init() {
31+
listOverrides = append(listOverrides, listOverride)
32+
}

cmd/workspace/apps/overrides.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55

66
appsCli "github.com/databricks/cli/cmd/apps"
77
"github.com/databricks/cli/libs/cmdio"
8+
"github.com/databricks/cli/libs/tableview"
89
"github.com/databricks/databricks-sdk-go/service/apps"
910
"github.com/spf13/cobra"
1011
)
@@ -15,6 +16,33 @@ func listOverride(listCmd *cobra.Command, listReq *apps.ListAppsRequest) {
1516
listCmd.Annotations["template"] = cmdio.Heredoc(`
1617
{{range .}}{{.Name | green}} {{.Url}} {{if .ComputeStatus}}{{if eq .ComputeStatus.State "ACTIVE"}}{{green "%s" .ComputeStatus.State }}{{else}}{{blue "%s" .ComputeStatus.State}}{{end}}{{end}} {{if .ActiveDeployment}}{{if eq .ActiveDeployment.Status.State "SUCCEEDED"}}{{green "%s" .ActiveDeployment.Status.State }}{{else}}{{blue "%s" .ActiveDeployment.Status.State}}{{end}}{{end}}
1718
{{end}}`)
19+
20+
columns := []tableview.ColumnDef{
21+
{Header: "Name", Extract: func(v any) string {
22+
a := v.(apps.App)
23+
return a.Name
24+
}},
25+
{Header: "URL", Extract: func(v any) string {
26+
a := v.(apps.App)
27+
return a.Url
28+
}},
29+
{Header: "Compute Status", Extract: func(v any) string {
30+
a := v.(apps.App)
31+
if a.ComputeStatus != nil {
32+
return string(a.ComputeStatus.State)
33+
}
34+
return ""
35+
}},
36+
{Header: "Deploy Status", Extract: func(v any) string {
37+
a := v.(apps.App)
38+
if a.ActiveDeployment != nil && a.ActiveDeployment.Status != nil {
39+
return string(a.ActiveDeployment.Status.State)
40+
}
41+
return ""
42+
}},
43+
}
44+
45+
tableview.RegisterConfig(listCmd, tableview.TableConfig{Columns: columns})
1846
}
1947

2048
func listDeploymentsOverride(listDeploymentsCmd *cobra.Command, listDeploymentsReq *apps.ListAppDeploymentsRequest) {
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package apps
2+
3+
import (
4+
"testing"
5+
6+
"github.com/databricks/cli/libs/tableview"
7+
sdkapps "github.com/databricks/databricks-sdk-go/service/apps"
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestListTableConfig(t *testing.T) {
13+
cmd := newList()
14+
15+
cfg := tableview.GetConfig(cmd)
16+
require.NotNil(t, cfg)
17+
require.Len(t, cfg.Columns, 4)
18+
19+
tests := []struct {
20+
name string
21+
app sdkapps.App
22+
wantName string
23+
wantURL string
24+
wantCompute string
25+
wantDeploy string
26+
}{
27+
{
28+
name: "with nested fields",
29+
app: sdkapps.App{
30+
Name: "test-app",
31+
Url: "https://example.com",
32+
ComputeStatus: &sdkapps.ComputeStatus{
33+
State: sdkapps.ComputeStateActive,
34+
},
35+
ActiveDeployment: &sdkapps.AppDeployment{
36+
Status: &sdkapps.AppDeploymentStatus{
37+
State: sdkapps.AppDeploymentStateSucceeded,
38+
},
39+
},
40+
},
41+
wantName: "test-app",
42+
wantURL: "https://example.com",
43+
wantCompute: "ACTIVE",
44+
wantDeploy: "SUCCEEDED",
45+
},
46+
{
47+
name: "nil nested fields",
48+
app: sdkapps.App{
49+
Name: "test-app",
50+
Url: "https://example.com",
51+
ActiveDeployment: &sdkapps.AppDeployment{},
52+
},
53+
wantName: "test-app",
54+
wantURL: "https://example.com",
55+
wantCompute: "",
56+
wantDeploy: "",
57+
},
58+
}
59+
60+
for _, tt := range tests {
61+
t.Run(tt.name, func(t *testing.T) {
62+
assert.Equal(t, tt.wantName, cfg.Columns[0].Extract(tt.app))
63+
assert.Equal(t, tt.wantURL, cfg.Columns[1].Extract(tt.app))
64+
assert.Equal(t, tt.wantCompute, cfg.Columns[2].Extract(tt.app))
65+
assert.Equal(t, tt.wantDeploy, cfg.Columns[3].Extract(tt.app))
66+
})
67+
}
68+
}

cmd/workspace/catalogs/overrides.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package catalogs
22

33
import (
44
"github.com/databricks/cli/libs/cmdio"
5+
"github.com/databricks/cli/libs/tableview"
56
"github.com/databricks/databricks-sdk-go/service/catalog"
67
"github.com/spf13/cobra"
78
)
@@ -12,6 +13,20 @@ func listOverride(listCmd *cobra.Command, listReq *catalog.ListCatalogsRequest)
1213
listCmd.Annotations["template"] = cmdio.Heredoc(`
1314
{{range .}}{{.Name|green}} {{blue "%s" .CatalogType}} {{.Comment}}
1415
{{end}}`)
16+
17+
columns := []tableview.ColumnDef{
18+
{Header: "Name", Extract: func(v any) string {
19+
return v.(catalog.CatalogInfo).Name
20+
}},
21+
{Header: "Type", Extract: func(v any) string {
22+
return string(v.(catalog.CatalogInfo).CatalogType)
23+
}},
24+
{Header: "Comment", MaxWidth: 40, Extract: func(v any) string {
25+
return v.(catalog.CatalogInfo).Comment
26+
}},
27+
}
28+
29+
tableview.RegisterConfig(listCmd, tableview.TableConfig{Columns: columns})
1530
}
1631

1732
func init() {

cmd/workspace/clusters/overrides.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"strings"
55

66
"github.com/databricks/cli/libs/cmdio"
7+
"github.com/databricks/cli/libs/tableview"
78
"github.com/databricks/databricks-sdk-go/service/compute"
89
"github.com/spf13/cobra"
910
)
@@ -17,6 +18,20 @@ func listOverride(listCmd *cobra.Command, listReq *compute.ListClustersRequest)
1718
{{range .}}{{.ClusterId | green}} {{.ClusterName | cyan}} {{if eq .State "RUNNING"}}{{green "%s" .State}}{{else if eq .State "TERMINATED"}}{{red "%s" .State}}{{else}}{{blue "%s" .State}}{{end}}
1819
{{end}}`)
1920

21+
columns := []tableview.ColumnDef{
22+
{Header: "Cluster ID", Extract: func(v any) string {
23+
return v.(compute.ClusterDetails).ClusterId
24+
}},
25+
{Header: "Name", Extract: func(v any) string {
26+
return v.(compute.ClusterDetails).ClusterName
27+
}},
28+
{Header: "State", Extract: func(v any) string {
29+
return string(v.(compute.ClusterDetails).State)
30+
}},
31+
}
32+
33+
tableview.RegisterConfig(listCmd, tableview.TableConfig{Columns: columns})
34+
2035
listReq.FilterBy = &compute.ListClustersFilterBy{}
2136
listCmd.Flags().BoolVar(&listReq.FilterBy.IsPinned, "is-pinned", false, "Filter clusters by pinned status")
2237
listCmd.Flags().StringVar(&listReq.FilterBy.PolicyId, "policy-id", "", "Filter clusters by policy id")

cmd/workspace/external-locations/overrides.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package external_locations
22

33
import (
44
"github.com/databricks/cli/libs/cmdio"
5+
"github.com/databricks/cli/libs/tableview"
56
"github.com/databricks/databricks-sdk-go/service/catalog"
67
"github.com/spf13/cobra"
78
)
@@ -12,6 +13,20 @@ func listOverride(listCmd *cobra.Command, listReq *catalog.ListExternalLocations
1213
listCmd.Annotations["template"] = cmdio.Heredoc(`
1314
{{range .}}{{.Name|green}} {{.CredentialName|cyan}} {{.Url}}
1415
{{end}}`)
16+
17+
columns := []tableview.ColumnDef{
18+
{Header: "Name", Extract: func(v any) string {
19+
return v.(catalog.ExternalLocationInfo).Name
20+
}},
21+
{Header: "Credential", Extract: func(v any) string {
22+
return v.(catalog.ExternalLocationInfo).CredentialName
23+
}},
24+
{Header: "URL", Extract: func(v any) string {
25+
return v.(catalog.ExternalLocationInfo).Url
26+
}},
27+
}
28+
29+
tableview.RegisterConfig(listCmd, tableview.TableConfig{Columns: columns})
1530
}
1631

1732
func init() {

cmd/workspace/instance-pools/overrides.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,32 @@ package instance_pools
22

33
import (
44
"github.com/databricks/cli/libs/cmdio"
5+
"github.com/databricks/cli/libs/tableview"
6+
"github.com/databricks/databricks-sdk-go/service/compute"
57
"github.com/spf13/cobra"
68
)
79

810
func listOverride(listCmd *cobra.Command) {
911
listCmd.Annotations["template"] = cmdio.Heredoc(`
1012
{{range .}}{{.InstancePoolId|green}} {{.InstancePoolName}} {{.NodeTypeId}} {{.State}}
1113
{{end}}`)
14+
15+
columns := []tableview.ColumnDef{
16+
{Header: "Pool ID", Extract: func(v any) string {
17+
return v.(compute.InstancePoolAndStats).InstancePoolId
18+
}},
19+
{Header: "Name", Extract: func(v any) string {
20+
return v.(compute.InstancePoolAndStats).InstancePoolName
21+
}},
22+
{Header: "Node Type", Extract: func(v any) string {
23+
return v.(compute.InstancePoolAndStats).NodeTypeId
24+
}},
25+
{Header: "State", Extract: func(v any) string {
26+
return string(v.(compute.InstancePoolAndStats).State)
27+
}},
28+
}
29+
30+
tableview.RegisterConfig(listCmd, tableview.TableConfig{Columns: columns})
1231
}
1332

1433
func init() {

cmd/workspace/jobs/overrides.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
package jobs
22

33
import (
4+
"context"
5+
"strconv"
6+
7+
"github.com/databricks/cli/libs/cmdctx"
48
"github.com/databricks/cli/libs/cmdio"
9+
"github.com/databricks/cli/libs/tableview"
510
"github.com/databricks/databricks-sdk-go/service/jobs"
611
"github.com/spf13/cobra"
712
)
@@ -10,6 +15,33 @@ func listOverride(listCmd *cobra.Command, listReq *jobs.ListJobsRequest) {
1015
listCmd.Annotations["template"] = cmdio.Heredoc(`
1116
{{range .}}{{green "%d" .JobId}} {{.Settings.Name}}
1217
{{end}}`)
18+
19+
columns := []tableview.ColumnDef{
20+
{Header: "Job ID", Extract: func(v any) string {
21+
return strconv.FormatInt(v.(jobs.BaseJob).JobId, 10)
22+
}},
23+
{Header: "Name", Extract: func(v any) string {
24+
if v.(jobs.BaseJob).Settings != nil {
25+
return v.(jobs.BaseJob).Settings.Name
26+
}
27+
return ""
28+
}},
29+
}
30+
31+
tableview.RegisterConfig(listCmd, tableview.TableConfig{
32+
Columns: columns,
33+
Search: &tableview.SearchConfig{
34+
Placeholder: "Search by exact name...",
35+
NewIterator: func(ctx context.Context, query string) tableview.RowIterator {
36+
req := *listReq
37+
req.Name = query
38+
req.PageToken = ""
39+
req.Offset = 0
40+
w := cmdctx.WorkspaceClient(ctx)
41+
return tableview.WrapIterator(w.Jobs.List(ctx, req), columns)
42+
},
43+
},
44+
})
1345
}
1446

1547
func listRunsOverride(listRunsCmd *cobra.Command, listRunsReq *jobs.ListRunsRequest) {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package jobs
2+
3+
import (
4+
"testing"
5+
6+
"github.com/databricks/cli/libs/tableview"
7+
sdkjobs "github.com/databricks/databricks-sdk-go/service/jobs"
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestListTableConfig(t *testing.T) {
13+
cmd := newList()
14+
15+
cfg := tableview.GetConfig(cmd)
16+
require.NotNil(t, cfg)
17+
require.Len(t, cfg.Columns, 2)
18+
19+
tests := []struct {
20+
name string
21+
job sdkjobs.BaseJob
22+
wantID string
23+
wantName string
24+
}{
25+
{
26+
name: "with settings",
27+
job: sdkjobs.BaseJob{
28+
JobId: 123,
29+
Settings: &sdkjobs.JobSettings{Name: "test-job"},
30+
},
31+
wantID: "123",
32+
wantName: "test-job",
33+
},
34+
{
35+
name: "nil settings",
36+
job: sdkjobs.BaseJob{
37+
JobId: 456,
38+
},
39+
wantID: "456",
40+
wantName: "",
41+
},
42+
}
43+
44+
for _, tt := range tests {
45+
t.Run(tt.name, func(t *testing.T) {
46+
assert.Equal(t, tt.wantID, cfg.Columns[0].Extract(tt.job))
47+
assert.Equal(t, tt.wantName, cfg.Columns[1].Extract(tt.job))
48+
})
49+
}
50+
}

0 commit comments

Comments
 (0)