Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion cmd/environments/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"ldcli/cmd/validators"
"ldcli/internal/analytics"
"ldcli/internal/environments"
"ldcli/internal/output"
)

func NewGetCmd(
Expand Down Expand Up @@ -68,6 +69,14 @@ func runGet(
return err
}

output, err := output.CmdOutput(
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the implementation. Another PR will add it for all endpoints and add functionality to work for a response with multiple resources.

viper.GetString(cliflags.OutputFlag),
output.NewSingularOutputterFn(response),
)
if err != nil {
return err
}

analyticsTracker.SendEvent(
viper.GetString(cliflags.AccessTokenFlag),
viper.GetString(cliflags.BaseURIFlag),
Expand All @@ -77,7 +86,7 @@ func runGet(
"projectKey": viper.GetString(cliflags.ProjectFlag),
})

fmt.Fprintf(cmd.OutOrStdout(), string(response)+"\n")
fmt.Fprintf(cmd.OutOrStdout(), string(output)+"\n")

return nil
}
Expand Down
14 changes: 9 additions & 5 deletions cmd/environments/get_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,29 @@ func TestGet(t *testing.T) {
"test-env",
"test-proj",
}
stubbedResponse := `{"key": "test-key", "name": "test-name"}`

t.Run("with valid environments calls API", func(t *testing.T) {
t.Run("with valid flags calls API", func(t *testing.T) {
client := environments.MockClient{}
client.
On("Get", mockArgs...).
Return([]byte(cmd.ValidResponse), nil)
Return([]byte(stubbedResponse), nil)
clients := cmd.APIClients{
EnvironmentsClient: &client,
}
args := []string{
"environments", "get",
"--access-token", "testAccessToken",
"--base-uri", "http://test.com",
"--output", "json",
"--environment", "test-env",
"--project", "test-proj",
}

output, err := cmd.CallCmd(t, clients, args)

require.NoError(t, err)
assert.JSONEq(t, `{"valid": true}`, string(output))
assert.JSONEq(t, stubbedResponse, string(output))
})

t.Run("with valid flags from environment variables calls API", func(t *testing.T) {
Expand All @@ -48,20 +50,21 @@ func TestGet(t *testing.T) {
client := environments.MockClient{}
client.
On("Get", mockArgs...).
Return([]byte(cmd.ValidResponse), nil)
Return([]byte(stubbedResponse), nil)
clients := cmd.APIClients{
EnvironmentsClient: &client,
}
args := []string{
"environments", "get",
"--output", "json",
"--environment", "test-env",
"--project", "test-proj",
}

output, err := cmd.CallCmd(t, clients, args)

require.NoError(t, err)
assert.JSONEq(t, `{"valid": true}`, string(output))
assert.JSONEq(t, stubbedResponse, string(output))
})

t.Run("with an error response is an error", func(t *testing.T) {
Expand All @@ -76,6 +79,7 @@ func TestGet(t *testing.T) {
"environments", "get",
"--access-token", "testAccessToken",
"--base-uri", "http://test.com",
"--output", "json",
"--environment", "test-env",
"--project", "test-proj",
}
Expand Down
11 changes: 10 additions & 1 deletion cmd/projects/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"ldcli/cmd/cliflags"
"ldcli/cmd/validators"
"ldcli/internal/output"
"ldcli/internal/projects"
)

Expand All @@ -35,7 +36,15 @@ func runList(client projects.Client) func(*cobra.Command, []string) error {
return err
}

fmt.Fprintf(cmd.OutOrStdout(), string(response)+"\n")
output, err := output.CmdOutput(
viper.GetString(cliflags.OutputFlag),
output.NewMultipleOutputterFn(response),
)
if err != nil {
return err
}

fmt.Fprintf(cmd.OutOrStdout(), string(output)+"\n")

return nil
}
Expand Down
18 changes: 14 additions & 4 deletions cmd/projects/list_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,25 +17,34 @@ func TestList(t *testing.T) {
"testAccessToken",
"http://test.com",
}
stubbedResponse := `{
"items": [
{
"key": "test-key",
"name": "test-name"
}
]
}`

t.Run("with valid flags calls API", func(t *testing.T) {
client := projects.MockClient{}
client.
On("List", mockArgs...).
Return([]byte(cmd.ValidResponse), nil)
Return([]byte(stubbedResponse), nil)
clients := cmd.APIClients{
ProjectsClient: &client,
}
args := []string{
"projects", "list",
"--access-token", "testAccessToken",
"--base-uri", "http://test.com",
"--output", "json",
}

output, err := cmd.CallCmd(t, clients, args)

require.NoError(t, err)
assert.JSONEq(t, `{"valid": true}`, string(output))
assert.JSONEq(t, stubbedResponse, string(output))
})

t.Run("with valid flags from environment variables calls API", func(t *testing.T) {
Expand All @@ -44,19 +53,20 @@ func TestList(t *testing.T) {
client := projects.MockClient{}
client.
On("List", mockArgs...).
Return([]byte(cmd.ValidResponse), nil)
Return([]byte(stubbedResponse), nil)
clients := cmd.APIClients{
ProjectsClient: &client,
}
args := []string{
"projects",
"list",
"--output", "json",
}

output, err := cmd.CallCmd(t, clients, args)

require.NoError(t, err)
assert.JSONEq(t, `{"valid": true}`, string(output))
assert.JSONEq(t, stubbedResponse, string(output))
})

t.Run("with an error response is an error", func(t *testing.T) {
Expand Down
21 changes: 5 additions & 16 deletions cmd/validators/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,27 @@ import (

"ldcli/cmd/cliflags"
errs "ldcli/internal/errors"
"ldcli/internal/output"
)

// Validate is a validator for commands to print an error when the user input is invalid.
func Validate() cobra.PositionalArgs {
return func(cmd *cobra.Command, args []string) error {
rebindFlags(cmd, cmd.ValidArgs) // rebind flags before validating them below
commandPath := getCommandPath(cmd)

_, err := url.ParseRequestURI(viper.GetString(cliflags.BaseURIFlag))
if err != nil {
return CmdError(errs.ErrInvalidBaseURI, commandPath)
return CmdError(errs.ErrInvalidBaseURI, cmd.CommandPath())
}

err = cmd.ValidateRequiredFlags()
if err != nil {
return CmdError(err, commandPath)
return CmdError(err, cmd.CommandPath())
}

err = validateOutput(viper.GetString(cliflags.OutputFlag))
if err != nil {
return CmdError(err, commandPath)
return CmdError(err, cmd.CommandPath())
}

return nil
Expand All @@ -48,25 +48,14 @@ func CmdError(err error, commandPath string) error {
return errors.New(errorMessage)
}

func getCommandPath(cmd *cobra.Command) string {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sneaking this in here based on this comment.

var commandPath string
if cmd.Annotations["scope"] == "plugin" {
commandPath = fmt.Sprintf("stripe %s", cmd.CommandPath())
} else {
commandPath = cmd.CommandPath()
}

return commandPath
}

func validateOutput(outputFlag string) error {
validKinds := map[string]struct{}{
"json": {},
"plaintext": {},
}
_, ok := validKinds[outputFlag]
if !ok {
return errors.New("output is invalid")
return output.ErrInvalidOutputKind
}

return nil
Expand Down
7 changes: 4 additions & 3 deletions internal/environments/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,11 @@ func (c EnvironmentsClient) Get(

}

responseJSON, err := json.Marshal(environment)
output, err := json.Marshal(environment)
if err != nil {
return nil, err
return nil, errors.NewLDAPIError(err)

}

return responseJSON, nil
return output, nil
}
49 changes: 49 additions & 0 deletions internal/output/multiple_outputter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package output

import (
"encoding/json"
"fmt"
)

var multiplePlaintextOutputFn = func(r resource) string {
return fmt.Sprintf("* %s (%s)", r.Name, r.Key)
}

// TODO: rename this to be "cleaner"? -- NewMultipleOutput()
func NewMultipleOutputterFn(input []byte) multipleOutputterFn {
return multipleOutputterFn{
input: input,
}
}

type multipleOutputterFn struct {
input []byte
}

func (o multipleOutputterFn) New() (Outputter, error) {
var r resources
err := json.Unmarshal(o.input, &r)
if err != nil {
return MultipleOutputter{}, err
}

return MultipleOutputter{
outputFn: multiplePlaintextOutputFn,
resources: r,
resourceJSON: o.input,
}, nil
}

type MultipleOutputter struct {
outputFn PlaintextOutputFn
resources resources
resourceJSON []byte
}

func (o MultipleOutputter) JSON() string {
return string(o.resourceJSON)
}

func (o MultipleOutputter) String() string {
return formatColl(o.resources.Items, o.outputFn)
}
59 changes: 59 additions & 0 deletions internal/output/multiple_outputter_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package output_test

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"ldcli/internal/output"
)

func TestMultipleOutputter_JSON(t *testing.T) {
input := []byte(`{
"items": [
{
"key": "test-key1",
"name": "test-name1",
"other": "another-value2"
},
{
"key": "test-key2",
"name": "test-name2",
"other": "another-value2"
}
]
}`)
output, err := output.CmdOutput(
"json",
output.NewMultipleOutputterFn(input),
)

require.NoError(t, err)
assert.JSONEq(t, output, string(input))
}

func TestMultipleOutputter_String(t *testing.T) {
input := []byte(`{
"items": [
{
"key": "test-key1",
"name": "test-name1",
"other": "another-value2"
},
{
"key": "test-key2",
"name": "test-name2",
"other": "another-value2"
}
]
}`)
expected := "* test-name1 (test-key1)\n* test-name2 (test-key2)"
output, err := output.CmdOutput(
"plaintext",
output.NewMultipleOutputterFn(input),
)

require.NoError(t, err)
assert.Equal(t, expected, output)
}
Loading