Skip to content

Commit

Permalink
feat: show successful resource delete message (#212)
Browse files Browse the repository at this point in the history
Show successful delete resource message plaintext response
  • Loading branch information
dbolson authored Apr 29, 2024
1 parent e2a1bfa commit c1c3c1a
Show file tree
Hide file tree
Showing 13 changed files with 253 additions and 194 deletions.
6 changes: 1 addition & 5 deletions cmd/environments/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,7 @@ func runGet(
return errors.NewError(output)
}

output, err := output.CmdOutputSingular(
viper.GetString(cliflags.OutputFlag),
response,
output.SingularPlaintextOutputFn,
)
output, err := output.CmdOutput("get", viper.GetString(cliflags.OutputFlag), response)
if err != nil {
return errors.NewError(err.Error())
}
Expand Down
6 changes: 1 addition & 5 deletions cmd/flags/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,7 @@ func runCreate(client flags.Client) func(*cobra.Command, []string) error {
return errors.NewError(output)
}

output, err := output.CmdOutputCreate(
viper.GetString(cliflags.OutputFlag),
response,
output.SingularPlaintextOutputFn,
)
output, err := output.CmdOutput("create", viper.GetString(cliflags.OutputFlag), response)
if err != nil {
return errors.NewError(err.Error())
}
Expand Down
6 changes: 1 addition & 5 deletions cmd/flags/get.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,7 @@ func runGet(client flags.Client) func(*cobra.Command, []string) error {
return errors.NewError(output)
}

output, err := output.CmdOutputSingular(
viper.GetString(cliflags.OutputFlag),
response,
output.SingularPlaintextOutputFn,
)
output, err := output.CmdOutput("get", viper.GetString(cliflags.OutputFlag), response)
if err != nil {
return errors.NewError(err.Error())
}
Expand Down
6 changes: 1 addition & 5 deletions cmd/flags/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,7 @@ func runUpdate(client flags.Client) func(*cobra.Command, []string) error {
return errors.NewError(output)
}

output, err := output.CmdOutputUpdate(
viper.GetString(cliflags.OutputFlag),
response,
output.SingularPlaintextOutputFn,
)
output, err := output.CmdOutput("update", viper.GetString(cliflags.OutputFlag), response)
if err != nil {
return errors.NewError(err.Error())
}
Expand Down
6 changes: 1 addition & 5 deletions cmd/members/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,7 @@ func runCreate(client members.Client) func(*cobra.Command, []string) error {
return errors.NewError(output)
}

output, err := output.CmdOutputSingular(
viper.GetString(cliflags.OutputFlag),
response,
output.SingularPlaintextOutputFn,
)
output, err := output.CmdOutput("create", viper.GetString(cliflags.OutputFlag), response)
if err != nil {
return errors.NewError(err.Error())
}
Expand Down
6 changes: 1 addition & 5 deletions cmd/members/invite.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,7 @@ func runInvite(client members.Client) func(*cobra.Command, []string) error {
return errors.NewError(output)
}

output, err := output.CmdOutputMultiple(
viper.GetString(cliflags.OutputFlag),
response,
output.MultipleEmailPlaintextOutputFn,
)
output, err := output.CmdOutput("create", viper.GetString(cliflags.OutputFlag), response)
if err != nil {
return errors.NewError(err.Error())
}
Expand Down
6 changes: 1 addition & 5 deletions cmd/projects/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,7 @@ func runCreate(client projects.Client) func(*cobra.Command, []string) error {
return errors.NewError(output)
}

output, err := output.CmdOutputSingular(
viper.GetString(cliflags.OutputFlag),
response,
output.SingularPlaintextOutputFn,
)
output, err := output.CmdOutput("create", viper.GetString(cliflags.OutputFlag), response)
if err != nil {
return errors.NewError(err.Error())
}
Expand Down
6 changes: 1 addition & 5 deletions cmd/projects/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,7 @@ func runList(client projects.Client) func(*cobra.Command, []string) error {
return errors.NewError(output)
}

output, err := output.CmdOutputMultiple(
viper.GetString(cliflags.OutputFlag),
response,
output.MultiplePlaintextOutputFn,
)
output, err := output.CmdOutput("list", viper.GetString(cliflags.OutputFlag), response)
if err != nil {
return errors.NewError(err.Error())
}
Expand Down
4 changes: 2 additions & 2 deletions internal/members/members.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ func (c MembersClient) Create(ctx context.Context, accessToken string, baseURI s
if err != nil {
return nil, errors.NewLDAPIError(err)
}
memberJson, err := json.Marshal(members.Items)
membersJson, err := json.Marshal(members)
if err != nil {
return nil, err
}

return memberJson, nil
return membersJson, nil
}
58 changes: 3 additions & 55 deletions internal/output/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,6 @@ type resources struct {
Items []resource `json:"items"`
}

// resourcesBare is for responses that return a list of resources at the top level of the response,
// not as a value of an "items" property.
type resourcesBare []resource

// CmdOutputSingular builds a command response based on the flag the user provided and the shape of
// the input. The expected shape is a single JSON object.
func CmdOutputSingular(outputKind string, input []byte, fn PlaintextOutputFn) (string, error) {
Expand All @@ -73,67 +69,19 @@ func CmdOutputSingular(outputKind string, input []byte, fn PlaintextOutputFn) (s
return "", err
}

return outputFromKind(outputKind, "", SingularOutputter{
outputFn: fn,
resource: r,
resourceJSON: input,
})
}

// CmdOutputCreate builds a command response based on the flag the user provided and the shape of
// the input with a successfully created message. The expected shape is a single JSON object.
func CmdOutputCreate(outputKind string, input []byte, fn PlaintextOutputFn) (string, error) {
return cmdOutputWithMessage(outputKind, "Successfully created ", input, fn)
}

// CmdOutputUpdate builds a command response based on the flag the user provided and the shape of
// the input with a successfully created message. The expected shape is a single JSON object.
func CmdOutputUpdate(outputKind string, input []byte, fn PlaintextOutputFn) (string, error) {
return cmdOutputWithMessage(outputKind, "Successfully updated ", input, fn)
}

func cmdOutputWithMessage(outputKind string, message string, input []byte, fn PlaintextOutputFn) (string, error) {
var r resource
err := json.Unmarshal(input, &r)
if err != nil {
return "", err
}

return outputFromKind(outputKind, message, SingularOutputter{
return outputFromKind(outputKind, SingularOutputter{
outputFn: fn,
resource: r,
resourceJSON: input,
})
}

// CmdOutputMultiple builds a command response based on the flag the user provided and the shape of
// the input. The expected shape is a list of JSON objects.
func CmdOutputMultiple(outputKind string, input []byte, fn PlaintextOutputFn) (string, error) {
var r resources
err := json.Unmarshal(input, &r)
if err != nil {
// sometimes a response doesn't include each item in an "items" property
var rr resourcesBare
err := json.Unmarshal(input, &rr)
if err != nil {
return "", err
}
r.Items = rr
}

return outputFromKind(outputKind, "", MultipleOutputter{
outputFn: fn,
resources: r,
resourceJSON: input,
})
}

func outputFromKind(outputKind string, additional string, o Outputter) (string, error) {
func outputFromKind(outputKind string, o Outputter) (string, error) {
switch outputKind {
case "json":
return o.JSON(), nil
case "plaintext":
return additional + o.String(), nil
return o.String(), nil
}

return "", ErrInvalidOutputKind
Expand Down
97 changes: 0 additions & 97 deletions internal/output/output_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,100 +64,3 @@ func TestCmdOutputSingular(t *testing.T) {
})
}
}

func TestCmdOutput_WithSuccessMessage(t *testing.T) {
tests := map[string]struct {
expected string
fn func(string, []byte, output.PlaintextOutputFn) (string, error)
input string
outputKind string
}{
"when creating with json": {
expected: `{
"key": "test-key",
"name": "test-name"
}`,
fn: output.CmdOutputCreate,
input: `{
"key": "test-key",
"name": "test-name"
}`,
outputKind: "json",
},
"when creating with plaintext": {
expected: "Successfully created test-name (test-key)",
fn: output.CmdOutputCreate,
input: `{
"key": "test-key",
"name": "test-name"
}`,
outputKind: "plaintext",
},
"when updating with json": {
expected: `{
"key": "test-key",
"name": "test-name"
}`,
fn: output.CmdOutputUpdate,
input: `{
"key": "test-key",
"name": "test-name"
}`,
outputKind: "json",
},
"when updating with plaintext": {
expected: "Successfully updated test-name (test-key)",
fn: output.CmdOutputUpdate,
input: `{
"key": "test-key",
"name": "test-name"
}`,
outputKind: "plaintext",
},
}
for name, tt := range tests {
tt := tt
t.Run(name, func(t *testing.T) {
output, err := tt.fn(
tt.outputKind,
[]byte(tt.input),
output.SingularPlaintextOutputFn,
)

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

func TestCmdOutputMultiple(t *testing.T) {
tests := map[string]struct {
expected string
fn output.PlaintextOutputFn
input string
}{
"with multiple emails not as items property": {
expected: "* test-email1 (test-id1)\n* test-email2 (test-id2)",
fn: output.MultipleEmailPlaintextOutputFn,
input: `[{"_id": "test-id1", "email": "test-email1"}, {"_id": "test-id2", "email": "test-email2"}]`,
},
"with multiple items": {
expected: "* test-name1 (test-key1)\n* test-name2 (test-key2)",
fn: output.MultiplePlaintextOutputFn,
input: `{"items": [{"key": "test-key1", "name": "test-name1"}, {"key": "test-key2", "name": "test-name2"}]}`,
},
}
for name, tt := range tests {
tt := tt
t.Run(name, func(t *testing.T) {
output, err := output.CmdOutputMultiple(
"plaintext",
[]byte(tt.input),
tt.fn,
)

require.NoError(t, err)
assert.Equal(t, tt.expected, output)
})
}
}
68 changes: 68 additions & 0 deletions internal/output/resource_output.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package output

import (
"encoding/json"
"fmt"
"strings"
)

// CmdOutput returns a response from a resource create action formatted based on the
// output flag along with an optional message based on the action.
func CmdOutput(action string, outputKind string, input []byte) (string, error) {
if outputKind == "json" {
return string(input), nil
}

var (
maybeResource resource
maybeResources resources
isMultipleResponse bool
)

// unmarshal either a singular resource or a list of them
err := json.Unmarshal(input, &maybeResource)
_, isMultipleResponse = maybeResource["items"]
if err != nil || isMultipleResponse {
err := json.Unmarshal(input, &maybeResources)
if err != nil {
return "", err
}
}

var successMessage string
switch action {
case "create":
successMessage = "Successfully created"
case "delete":
successMessage = "Successfully deleted"
case "update":
successMessage = "Successfully updated"
default:
// no success message
}

if isMultipleResponse {
// the response could have various properties we want to show
outputFn := MultiplePlaintextOutputFn
if _, ok := maybeResources.Items[0]["email"]; ok {
outputFn = MultipleEmailPlaintextOutputFn
}

items := make([]string, 0, len(maybeResources.Items))
for _, i := range maybeResources.Items {
items = append(items, outputFn(i))
}

return plaintextOutput("\n"+strings.Join(items, "\n"), successMessage), nil
}

return plaintextOutput(SingularPlaintextOutputFn(maybeResource), successMessage), nil
}

func plaintextOutput(out string, successMessage string) string {
if successMessage != "" {
return fmt.Sprintf("%s %s", successMessage, out)
}

return out
}
Loading

0 comments on commit c1c3c1a

Please sign in to comment.