mirror of
https://github.com/coder/coder.git
synced 2026-09-21 20:51:01 +08:00
fix(cli/cliui): handle typed nil and null time in tables (#15984)
This commit is contained in:
+14
-1
@@ -9,6 +9,8 @@ import (
|
||||
"github.com/fatih/structtag"
|
||||
"github.com/jedib0t/go-pretty/v6/table"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
)
|
||||
|
||||
// Table creates a new table with standardized styles.
|
||||
@@ -195,6 +197,12 @@ func renderTable(out any, sort string, headers table.Row, filterColumns []string
|
||||
if val != nil {
|
||||
v = val.Format(time.RFC3339)
|
||||
}
|
||||
case codersdk.NullTime:
|
||||
if val.Valid {
|
||||
v = val.Time.Format(time.RFC3339)
|
||||
} else {
|
||||
v = nil
|
||||
}
|
||||
case *int64:
|
||||
if val != nil {
|
||||
v = *val
|
||||
@@ -204,8 +212,13 @@ func renderTable(out any, sort string, headers table.Row, filterColumns []string
|
||||
v = val.String()
|
||||
}
|
||||
case fmt.Stringer:
|
||||
if val != nil {
|
||||
// Protect against typed nils since fmt.Stringer is an interface.
|
||||
vv := reflect.ValueOf(v)
|
||||
nilPtr := vv.Kind() == reflect.Ptr && vv.IsNil()
|
||||
if val != nil && !nilPtr {
|
||||
v = val.String()
|
||||
} else if nilPtr {
|
||||
v = nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+38
-27
@@ -1,6 +1,7 @@
|
||||
package cliui_test
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/coder/coder/v2/cli/cliui"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
)
|
||||
|
||||
type stringWrapper struct {
|
||||
@@ -24,18 +26,20 @@ func (s stringWrapper) String() string {
|
||||
}
|
||||
|
||||
type tableTest1 struct {
|
||||
Name string `table:"name,default_sort"`
|
||||
NotIncluded string // no table tag
|
||||
Age int `table:"age"`
|
||||
Roles []string `table:"roles"`
|
||||
Sub1 tableTest2 `table:"sub_1,recursive"`
|
||||
Sub2 *tableTest2 `table:"sub_2,recursive"`
|
||||
Sub3 tableTest3 `table:"sub 3,recursive"`
|
||||
Sub4 tableTest2 `table:"sub 4"` // not recursive
|
||||
Name string `table:"name,default_sort"`
|
||||
AltName *stringWrapper `table:"alt_name"`
|
||||
NotIncluded string // no table tag
|
||||
Age int `table:"age"`
|
||||
Roles []string `table:"roles"`
|
||||
Sub1 tableTest2 `table:"sub_1,recursive"`
|
||||
Sub2 *tableTest2 `table:"sub_2,recursive"`
|
||||
Sub3 tableTest3 `table:"sub 3,recursive"`
|
||||
Sub4 tableTest2 `table:"sub 4"` // not recursive
|
||||
|
||||
// Types with special formatting.
|
||||
Time time.Time `table:"time"`
|
||||
TimePtr *time.Time `table:"time_ptr"`
|
||||
Time time.Time `table:"time"`
|
||||
TimePtr *time.Time `table:"time_ptr"`
|
||||
NullTime codersdk.NullTime `table:"null_time"`
|
||||
}
|
||||
|
||||
type tableTest2 struct {
|
||||
@@ -62,9 +66,10 @@ func Test_DisplayTable(t *testing.T) {
|
||||
// Not sorted by name or age to test sorting.
|
||||
in := []tableTest1{
|
||||
{
|
||||
Name: "bar",
|
||||
Age: 20,
|
||||
Roles: []string{"a"},
|
||||
Name: "bar",
|
||||
AltName: &stringWrapper{str: "bar alt"},
|
||||
Age: 20,
|
||||
Roles: []string{"a"},
|
||||
Sub1: tableTest2{
|
||||
Name: stringWrapper{str: "bar1"},
|
||||
Age: 21,
|
||||
@@ -82,6 +87,12 @@ func Test_DisplayTable(t *testing.T) {
|
||||
},
|
||||
Time: someTime,
|
||||
TimePtr: nil,
|
||||
NullTime: codersdk.NullTime{
|
||||
NullTime: sql.NullTime{
|
||||
Time: someTime,
|
||||
Valid: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "foo",
|
||||
@@ -138,10 +149,10 @@ func Test_DisplayTable(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
expected := `
|
||||
NAME AGE ROLES SUB 1 NAME SUB 1 AGE SUB 2 NAME SUB 2 AGE SUB 3 INNER NAME SUB 3 INNER AGE SUB 4 TIME TIME PTR
|
||||
bar 20 [a] bar1 21 <nil> <nil> bar3 23 {bar4 24 } 2022-08-02T15:49:10Z <nil>
|
||||
baz 30 [] baz1 31 <nil> <nil> baz3 33 {baz4 34 } 2022-08-02T15:49:10Z <nil>
|
||||
foo 10 [a, b, c] foo1 11 foo2 12 foo3 13 {foo4 14 } 2022-08-02T15:49:10Z 2022-08-02T15:49:10Z
|
||||
NAME ALT NAME AGE ROLES SUB 1 NAME SUB 1 AGE SUB 2 NAME SUB 2 AGE SUB 3 INNER NAME SUB 3 INNER AGE SUB 4 TIME TIME PTR NULL TIME
|
||||
bar bar alt 20 [a] bar1 21 <nil> <nil> bar3 23 {bar4 24 } 2022-08-02T15:49:10Z <nil> 2022-08-02T15:49:10Z
|
||||
baz <nil> 30 [] baz1 31 <nil> <nil> baz3 33 {baz4 34 } 2022-08-02T15:49:10Z <nil> <nil>
|
||||
foo <nil> 10 [a, b, c] foo1 11 foo2 12 foo3 13 {foo4 14 } 2022-08-02T15:49:10Z 2022-08-02T15:49:10Z <nil>
|
||||
`
|
||||
|
||||
// Test with non-pointer values.
|
||||
@@ -165,10 +176,10 @@ foo 10 [a, b, c] foo1 11 foo2 12 foo3
|
||||
t.Parallel()
|
||||
|
||||
expected := `
|
||||
NAME AGE ROLES SUB 1 NAME SUB 1 AGE SUB 2 NAME SUB 2 AGE SUB 3 INNER NAME SUB 3 INNER AGE SUB 4 TIME TIME PTR
|
||||
foo 10 [a, b, c] foo1 11 foo2 12 foo3 13 {foo4 14 } 2022-08-02T15:49:10Z 2022-08-02T15:49:10Z
|
||||
bar 20 [a] bar1 21 <nil> <nil> bar3 23 {bar4 24 } 2022-08-02T15:49:10Z <nil>
|
||||
baz 30 [] baz1 31 <nil> <nil> baz3 33 {baz4 34 } 2022-08-02T15:49:10Z <nil>
|
||||
NAME ALT NAME AGE ROLES SUB 1 NAME SUB 1 AGE SUB 2 NAME SUB 2 AGE SUB 3 INNER NAME SUB 3 INNER AGE SUB 4 TIME TIME PTR NULL TIME
|
||||
foo <nil> 10 [a, b, c] foo1 11 foo2 12 foo3 13 {foo4 14 } 2022-08-02T15:49:10Z 2022-08-02T15:49:10Z <nil>
|
||||
bar bar alt 20 [a] bar1 21 <nil> <nil> bar3 23 {bar4 24 } 2022-08-02T15:49:10Z <nil> 2022-08-02T15:49:10Z
|
||||
baz <nil> 30 [] baz1 31 <nil> <nil> baz3 33 {baz4 34 } 2022-08-02T15:49:10Z <nil> <nil>
|
||||
`
|
||||
|
||||
out, err := cliui.DisplayTable(in, "age", nil)
|
||||
@@ -235,12 +246,12 @@ Alice 25
|
||||
t.Run("WithSeparator", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
expected := `
|
||||
NAME AGE ROLES SUB 1 NAME SUB 1 AGE SUB 2 NAME SUB 2 AGE SUB 3 INNER NAME SUB 3 INNER AGE SUB 4 TIME TIME PTR
|
||||
bar 20 [a] bar1 21 <nil> <nil> bar3 23 {bar4 24 } 2022-08-02T15:49:10Z <nil>
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
baz 30 [] baz1 31 <nil> <nil> baz3 33 {baz4 34 } 2022-08-02T15:49:10Z <nil>
|
||||
---------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
foo 10 [a, b, c] foo1 11 foo2 12 foo3 13 {foo4 14 } 2022-08-02T15:49:10Z 2022-08-02T15:49:10Z
|
||||
NAME ALT NAME AGE ROLES SUB 1 NAME SUB 1 AGE SUB 2 NAME SUB 2 AGE SUB 3 INNER NAME SUB 3 INNER AGE SUB 4 TIME TIME PTR NULL TIME
|
||||
bar bar alt 20 [a] bar1 21 <nil> <nil> bar3 23 {bar4 24 } 2022-08-02T15:49:10Z <nil> 2022-08-02T15:49:10Z
|
||||
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
baz <nil> 30 [] baz1 31 <nil> <nil> baz3 33 {baz4 34 } 2022-08-02T15:49:10Z <nil> <nil>
|
||||
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
foo <nil> 10 [a, b, c] foo1 11 foo2 12 foo3 13 {foo4 14 } 2022-08-02T15:49:10Z 2022-08-02T15:49:10Z <nil>
|
||||
`
|
||||
|
||||
var inlineIn []any
|
||||
|
||||
Reference in New Issue
Block a user