From 62310fafd4e471cdf23d272293dc55e9f10ed0b2 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Fri, 22 Mar 2024 08:51:32 -0700 Subject: [PATCH 1/3] Add flags create command --- cmd/flags/create.go | 78 +++++++++++++++++++++++++++++++++++ cmd/flags/flags.go | 15 +++++++ cmd/projects/create.go | 14 ++----- cmd/projects/projects.go | 4 +- cmd/root.go | 5 ++- internal/flags/flags.go | 59 ++++++++++++++++++++++++++ internal/setup/flag_toggle.go | 2 +- internal/setup/flags.go | 4 +- 8 files changed, 162 insertions(+), 19 deletions(-) create mode 100644 cmd/flags/create.go create mode 100644 cmd/flags/flags.go create mode 100644 internal/flags/flags.go diff --git a/cmd/flags/create.go b/cmd/flags/create.go new file mode 100644 index 00000000..7b36019e --- /dev/null +++ b/cmd/flags/create.go @@ -0,0 +1,78 @@ +package flags + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + + "ld-cli/internal/flags" +) + +func NewCreateCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "create", + Short: "Create a new flag", + Long: "Create a new flag", + // PreRunE: validate, + RunE: runCreate, + } + + cmd.Flags().StringP("data", "d", "", "Input data in JSON") + err := cmd.MarkFlagRequired("data") + if err != nil { + panic(err) + } + err = viper.BindPFlag("data", cmd.Flags().Lookup("data")) + if err != nil { + panic(err) + } + + cmd.Flags().String("projKey", "", "Input data in JSON") + err = cmd.MarkFlagRequired("projKey") + if err != nil { + panic(err) + } + err = viper.BindPFlag("projKey", cmd.Flags().Lookup("projKey")) + if err != nil { + panic(err) + } + + return cmd +} + +type inputData struct { + Name string `json:"name"` + Key string `json:"key"` +} + +func runCreate(cmd *cobra.Command, args []string) error { + client := flags.NewClient( + viper.GetString("accessToken"), + viper.GetString("baseUri"), + ) + + var data inputData + err := json.Unmarshal([]byte(cmd.Flags().Lookup("data").Value.String()), &data) + // err := json.Unmarshal([]byte(viper.GetString("data")), &data) + if err != nil { + return err + } + projKey := viper.GetString("projKey") + + response, err := client.Create( + context.Background(), + data.Name, + data.Key, + projKey, + ) + if err != nil { + return err + } + + fmt.Fprintf(cmd.OutOrStdout(), string(response)+"\n") + + return nil +} diff --git a/cmd/flags/flags.go b/cmd/flags/flags.go new file mode 100644 index 00000000..544faf7d --- /dev/null +++ b/cmd/flags/flags.go @@ -0,0 +1,15 @@ +package flags + +import "github.com/spf13/cobra" + +func NewFlagsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "flags", + Short: "Make requests (list, create, etc.) on flags", + Long: "Make requests (list, create, etc.) on flags", + } + + cmd.AddCommand(NewCreateCmd()) + + return cmd +} diff --git a/cmd/projects/create.go b/cmd/projects/create.go index 3174cf4c..2a36867e 100644 --- a/cmd/projects/create.go +++ b/cmd/projects/create.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "github.com/spf13/cobra" "github.com/spf13/viper" @@ -19,14 +20,7 @@ func NewCreateCmd() *cobra.Command { RunE: runCreate, } - var data string - cmd.Flags().StringVarP( - &data, - "data", - "d", - "", - "Input data in JSON", - ) + cmd.Flags().StringP("data", "d", "", "Input data in JSON") err := cmd.MarkFlagRequired("data") if err != nil { panic(err) @@ -50,10 +44,8 @@ func runCreate(cmd *cobra.Command, args []string) error { viper.GetString("baseUri"), ) - dataStr := viper.GetString("data") - var data inputData - err := json.Unmarshal([]byte(dataStr), &data) + err := json.Unmarshal([]byte(viper.GetString("data")), &data) if err != nil { return err } diff --git a/cmd/projects/projects.go b/cmd/projects/projects.go index 55d970ae..7fd0b523 100644 --- a/cmd/projects/projects.go +++ b/cmd/projects/projects.go @@ -1,8 +1,6 @@ package projects -import ( - "github.com/spf13/cobra" -) +import "github.com/spf13/cobra" func NewProjectsCmd() *cobra.Command { cmd := &cobra.Command{ diff --git a/cmd/root.go b/cmd/root.go index aefa2e9a..8bf40165 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -5,12 +5,12 @@ import ( "fmt" "os" - errs "ld-cli/internal/errors" - "github.com/spf13/cobra" "github.com/spf13/viper" + "ld-cli/cmd/flags" "ld-cli/cmd/projects" + errs "ld-cli/internal/errors" ) func newRootCommand() *cobra.Command { @@ -60,6 +60,7 @@ func newRootCommand() *cobra.Command { panic(err) } + cmd.AddCommand(flags.NewFlagsCmd()) cmd.AddCommand(projects.NewProjectsCmd()) cmd.AddCommand(setupCmd) diff --git a/internal/flags/flags.go b/internal/flags/flags.go new file mode 100644 index 00000000..73cf059f --- /dev/null +++ b/internal/flags/flags.go @@ -0,0 +1,59 @@ +package flags + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/davecgh/go-spew/spew" + ldapi "github.com/launchdarkly/api-client-go/v14" + + "ld-cli/internal/errors" +) + +type Client interface { + Create(ctx context.Context, name string, key string, projectKey string) ([]byte, error) +} + +type FlagsClient struct { + client *ldapi.APIClient +} + +func NewClient(accessToken string, baseURI string) FlagsClient { + config := ldapi.NewConfiguration() + config.AddDefaultHeader("Authorization", accessToken) + config.Servers[0].URL = baseURI + client := ldapi.NewAPIClient(config) + + return FlagsClient{ + client: client, + } +} + +func (c FlagsClient) Create( + ctx context.Context, + name string, + key string, + projectKey string, +) ([]byte, error) { + post := ldapi.NewFeatureFlagBody(name, key) + flag, _, err := c.client.FeatureFlagsApi.PostFeatureFlag(ctx, projectKey).FeatureFlagBody(*post).Execute() + if err != nil { + fmt.Println(">>> err") + spew.Dump(err) + switch err.Error() { + case "401 Unauthorized": + return nil, errors.ErrUnauthorized + case "403 Forbidden": + return nil, errors.ErrForbidden + default: + return nil, err + } + } + responseJSON, err := json.Marshal(flag) + if err != nil { + return nil, err + } + + return responseJSON, nil +} diff --git a/internal/setup/flag_toggle.go b/internal/setup/flag_toggle.go index 3d9ba8ad..cc2f4243 100644 --- a/internal/setup/flag_toggle.go +++ b/internal/setup/flag_toggle.go @@ -62,7 +62,7 @@ func (m flagToggleModel) View() string { return title + "\n\n" + toggleStyle.Render(toggle) + m.flagKey + furtherInstructions } -func (m flagToggleModel) toggleFlag() error { +func (m flagToggleModel) toggleFlag() error { //nolint:unused url := fmt.Sprintf("http://localhost/api/v2/flags/default/%s", m.flagKey) c := &http.Client{ Timeout: 10 * time.Second, diff --git a/internal/setup/flags.go b/internal/setup/flags.go index 193f8a1f..0b8de841 100644 --- a/internal/setup/flags.go +++ b/internal/setup/flags.go @@ -82,9 +82,9 @@ func (m flagModel) View() string { ) + "\n" } -const apiToken = "" +const apiToken = "" //nolint:unused -func (m flagModel) createFlag() error { +func (m flagModel) createFlag() error { //nolint:unused url := "http://localhost/api/v2/flags/default" c := &http.Client{ Timeout: 10 * time.Second, From 33da26c7040090c6ccbf1499c1ddc8d15fff1d86 Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Fri, 22 Mar 2024 08:53:33 -0700 Subject: [PATCH 2/3] Validate input --- cmd/flags/create.go | 23 ++++++++++++++++++----- cmd/projects/list.go | 1 + 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/cmd/flags/create.go b/cmd/flags/create.go index 7b36019e..c0ea0307 100644 --- a/cmd/flags/create.go +++ b/cmd/flags/create.go @@ -4,20 +4,22 @@ import ( "context" "encoding/json" "fmt" + "net/url" "github.com/spf13/cobra" "github.com/spf13/viper" + "ld-cli/internal/errors" "ld-cli/internal/flags" ) func NewCreateCmd() *cobra.Command { cmd := &cobra.Command{ - Use: "create", - Short: "Create a new flag", - Long: "Create a new flag", - // PreRunE: validate, - RunE: runCreate, + Use: "create", + Short: "Create a new flag", + Long: "Create a new flag", + PreRunE: validate, + RunE: runCreate, } cmd.Flags().StringP("data", "d", "", "Input data in JSON") @@ -76,3 +78,14 @@ func runCreate(cmd *cobra.Command, args []string) error { return nil } + +// validate ensures the flags are valid before using them. +// TODO: refactor with projects validate(). +func validate(cmd *cobra.Command, args []string) error { + _, err := url.ParseRequestURI(viper.GetString("baseUri")) + if err != nil { + return errors.ErrInvalidBaseURI + } + + return nil +} diff --git a/cmd/projects/list.go b/cmd/projects/list.go index cfb9fc12..5fbd3f70 100644 --- a/cmd/projects/list.go +++ b/cmd/projects/list.go @@ -25,6 +25,7 @@ func NewListCmd() *cobra.Command { } // validate ensures the flags are valid before using them. +// TODO: refactor with flags validate(). func validate(cmd *cobra.Command, args []string) error { _, err := url.ParseRequestURI(viper.GetString("baseUri")) if err != nil { From 56f8604c8d22d6d3426356a98d731debf84d9a2a Mon Sep 17 00:00:00 2001 From: Danny Olson Date: Fri, 22 Mar 2024 10:34:56 -0700 Subject: [PATCH 3/3] Clean up --- cmd/flags/create.go | 2 +- internal/flags/flags.go | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/cmd/flags/create.go b/cmd/flags/create.go index c0ea0307..5b2c5493 100644 --- a/cmd/flags/create.go +++ b/cmd/flags/create.go @@ -32,7 +32,7 @@ func NewCreateCmd() *cobra.Command { panic(err) } - cmd.Flags().String("projKey", "", "Input data in JSON") + cmd.Flags().String("projKey", "", "Project key") err = cmd.MarkFlagRequired("projKey") if err != nil { panic(err) diff --git a/internal/flags/flags.go b/internal/flags/flags.go index 73cf059f..2af80d26 100644 --- a/internal/flags/flags.go +++ b/internal/flags/flags.go @@ -3,9 +3,7 @@ package flags import ( "context" "encoding/json" - "fmt" - "github.com/davecgh/go-spew/spew" ldapi "github.com/launchdarkly/api-client-go/v14" "ld-cli/internal/errors" @@ -39,8 +37,6 @@ func (c FlagsClient) Create( post := ldapi.NewFeatureFlagBody(name, key) flag, _, err := c.client.FeatureFlagsApi.PostFeatureFlag(ctx, projectKey).FeatureFlagBody(*post).Execute() if err != nil { - fmt.Println(">>> err") - spew.Dump(err) switch err.Error() { case "401 Unauthorized": return nil, errors.ErrUnauthorized