From 8a069c9f85578c22e6c7f787eab7348a3483fe2b Mon Sep 17 00:00:00 2001 From: Ara Park Date: Mon, 11 Dec 2023 19:37:51 +0900 Subject: [PATCH] feat: Add 'get' command for retrieving and displaying applied resources (#46) --- cmd/printer/table.go | 9 ++++ cmd/uniflow/apply/cmd.go | 11 +---- cmd/uniflow/apply/cmd_test.go | 4 +- cmd/uniflow/cmd.go | 11 +++-- cmd/uniflow/get/cmd.go | 83 +++++++++++++++++++++++++++++++++++ cmd/uniflow/get/cmd_test.go | 55 +++++++++++++++++++++++ cmd/uniflow/get/flag.go | 5 +++ 7 files changed, 164 insertions(+), 14 deletions(-) create mode 100644 cmd/uniflow/get/cmd.go create mode 100644 cmd/uniflow/get/cmd_test.go create mode 100644 cmd/uniflow/get/flag.go diff --git a/cmd/printer/table.go b/cmd/printer/table.go index 901539b7..818da3ef 100644 --- a/cmd/printer/table.go +++ b/cmd/printer/table.go @@ -21,6 +21,15 @@ type TablePrinter struct { formats []*jsonata.Expr } +// SpecTableColumnDefinitions defines columns for displaying spec information. +var SpecTableColumnDefinitions = []TableColumnDefinition{ + {Name: "id", Format: "$.id"}, + {Name: "kind", Format: "$.kind"}, + {Name: "namespace", Format: "$.namespace"}, + {Name: "name", Format: "$.name"}, + {Name: "links", Format: "$.links"}, +} + // style is the default style configuration for the table. var style = table.Style{ Name: "StyleDefault", diff --git a/cmd/uniflow/apply/cmd.go b/cmd/uniflow/apply/cmd.go index 404e3fd3..adb57397 100644 --- a/cmd/uniflow/apply/cmd.go +++ b/cmd/uniflow/apply/cmd.go @@ -23,15 +23,6 @@ type Config struct { FS fs.FS } -// SpecTableColumnDefinitions defines columns for displaying spec information. -var SpecTableColumnDefinitions = []printer.TableColumnDefinition{ - {Name: "id", Format: "$.id"}, - {Name: "kind", Format: "$.kind"}, - {Name: "namespace", Format: "$.namespace"}, - {Name: "name", Format: "$.name"}, - {Name: "links", Format: "$.links"}, -} - // NewCmd creates a new cobra.Command for the apply command. func NewCmd(config Config) *cobra.Command { cmd := &cobra.Command{ @@ -150,7 +141,7 @@ func applySpecs(ctx context.Context, st *storage.Storage, specs []scheme.Spec) e } func printSpecTable(cmd *cobra.Command, specs []scheme.Spec) error { - tablePrinter, err := printer.NewTable(SpecTableColumnDefinitions) + tablePrinter, err := printer.NewTable(printer.SpecTableColumnDefinitions) if err != nil { return err } diff --git a/cmd/uniflow/apply/cmd_test.go b/cmd/uniflow/apply/cmd_test.go index 1743e566..4c783afb 100644 --- a/cmd/uniflow/apply/cmd_test.go +++ b/cmd/uniflow/apply/cmd_test.go @@ -51,8 +51,8 @@ func TestExecute(t *testing.T) { cmd := NewCmd(Config{ Scheme: s, - FS: fsys, Database: db, + FS: fsys, }) cmd.SetOut(output) cmd.SetErr(output) @@ -65,4 +65,6 @@ func TestExecute(t *testing.T) { r, err := st.FindOne(context.Background(), storage.Where[string](scheme.KeyName).EQ(spec.GetName())) assert.NoError(t, err) assert.NotNil(t, r) + + assert.Contains(t, output.String(), spec.Name) } diff --git a/cmd/uniflow/cmd.go b/cmd/uniflow/cmd.go index 84dbe6a9..c438c527 100644 --- a/cmd/uniflow/cmd.go +++ b/cmd/uniflow/cmd.go @@ -4,6 +4,7 @@ import ( "io/fs" "github.com/siyul-park/uniflow/cmd/uniflow/apply" + "github.com/siyul-park/uniflow/cmd/uniflow/get" "github.com/siyul-park/uniflow/cmd/uniflow/start" "github.com/siyul-park/uniflow/pkg/database" "github.com/siyul-park/uniflow/pkg/hook" @@ -32,15 +33,19 @@ func NewCmd(config Config) *cobra.Command { Long: "Create your uniflow and integrate it anywhere!", } - cmd.AddCommand(start.NewCmd(start.Config{ + cmd.AddCommand(apply.NewCmd(apply.Config{ Scheme: sc, - Hook: hk, Database: db, FS: fsys, })) - cmd.AddCommand(apply.NewCmd(apply.Config{ + cmd.AddCommand(get.NewCmd(get.Config{ Scheme: sc, Database: db, + })) + cmd.AddCommand(start.NewCmd(start.Config{ + Scheme: sc, + Hook: hk, + Database: db, FS: fsys, })) diff --git a/cmd/uniflow/get/cmd.go b/cmd/uniflow/get/cmd.go new file mode 100644 index 00000000..f232c06c --- /dev/null +++ b/cmd/uniflow/get/cmd.go @@ -0,0 +1,83 @@ +package get + +import ( + "fmt" + + "github.com/siyul-park/uniflow/cmd/flag" + "github.com/siyul-park/uniflow/cmd/printer" + "github.com/siyul-park/uniflow/pkg/database" + "github.com/siyul-park/uniflow/pkg/scheme" + "github.com/siyul-park/uniflow/pkg/storage" + "github.com/spf13/cobra" +) + +// Config represents the configuration for the get command. +type Config struct { + Scheme *scheme.Scheme + Database database.Database +} + +// NewCmd creates a new cobra.Command for the get command. +func NewCmd(config Config) *cobra.Command { + cmd := &cobra.Command{ + Use: "get", + Short: "Get and display applied resources", + RunE: runGetCommand(config), + } + + cmd.PersistentFlags().StringP(FlagNamespace, flag.ToShorthand(FlagNamespace), "", "Set the resource's namespace. If not set, use all namespace") + + return cmd +} + +func runGetCommand(config Config) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, _ []string) error { + ctx := cmd.Context() + + ns, err := cmd.Flags().GetString(FlagNamespace) + if err != nil { + return err + } + + st, err := storage.New(ctx, storage.Config{ + Scheme: config.Scheme, + Database: config.Database, + }) + if err != nil { + return err + } + + filter := createNamespaceFilter(ns) + specs, err := st.FindMany(ctx, filter) + if err != nil { + return err + } + + return printSpecTable(cmd, specs) + } +} + +func createNamespaceFilter(ns string) *storage.Filter { + if ns == "" { + return nil + } + return storage.Where[string](scheme.KeyNamespace).EQ(ns) +} + +func printSpecTable(cmd *cobra.Command, specs []scheme.Spec) error { + tablePrinter, err := printer.NewTable(printer.SpecTableColumnDefinitions) + if err != nil { + return err + } + + table, err := tablePrinter.Print(specs) + if err != nil { + return err + } + + if _, err := fmt.Fprint(cmd.OutOrStdout(), table); err != nil { + return err + } + + return nil +} diff --git a/cmd/uniflow/get/cmd_test.go b/cmd/uniflow/get/cmd_test.go new file mode 100644 index 00000000..325b042d --- /dev/null +++ b/cmd/uniflow/get/cmd_test.go @@ -0,0 +1,55 @@ +package get + +import ( + "bytes" + "context" + "testing" + + "github.com/go-faker/faker/v4" + "github.com/siyul-park/uniflow/pkg/database/memdb" + "github.com/siyul-park/uniflow/pkg/node" + "github.com/siyul-park/uniflow/pkg/scheme" + "github.com/siyul-park/uniflow/pkg/storage" + "github.com/stretchr/testify/assert" +) + +func TestExecute(t *testing.T) { + s := scheme.New() + db := memdb.New("") + + st, _ := storage.New(context.Background(), storage.Config{ + Scheme: s, + Database: db, + }) + + kind := faker.Word() + + spec := &scheme.SpecMeta{ + Kind: kind, + Namespace: scheme.DefaultNamespace, + Name: faker.Word(), + } + + codec := scheme.CodecFunc(func(spec scheme.Spec) (node.Node, error) { + return node.NewOneToOneNode(nil), nil + }) + + s.AddKnownType(kind, &scheme.SpecMeta{}) + s.AddCodec(kind, codec) + + id, _ := st.InsertOne(context.Background(), spec) + + output := new(bytes.Buffer) + + cmd := NewCmd(Config{ + Scheme: s, + Database: db, + }) + cmd.SetOut(output) + cmd.SetErr(output) + + err := cmd.Execute() + assert.NoError(t, err) + + assert.Contains(t, output.String(), id.String()) +} diff --git a/cmd/uniflow/get/flag.go b/cmd/uniflow/get/flag.go new file mode 100644 index 00000000..aa4e9cc8 --- /dev/null +++ b/cmd/uniflow/get/flag.go @@ -0,0 +1,5 @@ +package get + +const ( + FlagNamespace = "namespace" +)