Skip to content

Commit

Permalink
feat(release): newrelic#45 WIP add support for command chaining
Browse files Browse the repository at this point in the history
This PR adds utility methods and logic to read stdin
for details needed in commands, allowing each command
ouput to be piped to the next command

newrelic#45
  • Loading branch information
nicolasjhampton committed Aug 31, 2020
1 parent 2790761 commit 0dcfa72
Show file tree
Hide file tree
Showing 5 changed files with 331 additions and 2 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ require (
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.7.1
github.com/stretchr/testify v1.6.1
github.com/tidwall/gjson v1.6.1
golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899
golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305
gopkg.in/yaml.v2 v2.3.0
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -769,6 +769,12 @@ github.com/tdakkota/asciicheck v0.0.0-20200416190851-d7f85be797a2 h1:Xr9gkxfOP0K
github.com/tdakkota/asciicheck v0.0.0-20200416190851-d7f85be797a2/go.mod h1:yHp0ai0Z9gUljN3o0xMhYJnH/IcvkdTBOX2fmJ93JEM=
github.com/tetafro/godot v0.4.8 h1:h61+hQraWhdI6WYqMwAwZYCE5yxL6a9/Orw4REbabSU=
github.com/tetafro/godot v0.4.8/go.mod h1:/7NLHhv08H1+8DNj0MElpAACw1ajsCuf3TKNQxA5S+0=
github.com/tidwall/gjson v1.6.1 h1:LRbvNuNuvAiISWg6gxLEFuCe72UKy5hDqhxW/8183ws=
github.com/tidwall/gjson v1.6.1/go.mod h1:BaHyNc5bjzYkPqgLq7mdVzeiRtULKULXLgZFKsxEHI0=
github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc=
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e h1:RumXZ56IrCj4CL+g1b9OL/oH0QnsF976bC8xQFYUD5Q=
github.com/timakin/bodyclose v0.0.0-20190930140734-f7f2e9bca95e/go.mod h1:Qimiffbc6q9tBWlVV6x0P9sat/ao1xEkREYPPj9hphk=
github.com/tj/assert v0.0.0-20171129193455-018094318fb0 h1:Rw8kxzWo1mr6FSaYXjQELRe88y2KdfynXdnK72rdjtA=
Expand Down
27 changes: 25 additions & 2 deletions internal/entities/command_tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package entities

import (
"errors"
"os"
"strings"

log "github.com/sirupsen/logrus"
Expand All @@ -17,6 +18,8 @@ import (
var (
entityTag string
entityTags []string
guid string
inPipe bool
)

var cmdTags = &cobra.Command{
Expand All @@ -40,7 +43,11 @@ The get command returns JSON output of the tags for the requested entity.
Example: "newrelic entity tags get --guid <entityGUID>",
Run: func(cmd *cobra.Command, args []string) {
client.WithClient(func(nrClient *newrelic.NewRelic) {
if inPipe {
entityGUID = guid
}
tags, err := nrClient.Entities.ListTags(entityGUID)

utils.LogIfFatal(err)

utils.LogIfError(output.Print(tags))
Expand Down Expand Up @@ -200,8 +207,24 @@ func init() {
Command.AddCommand(cmdTags)

cmdTags.AddCommand(cmdTagsGet)
cmdTagsGet.Flags().StringVarP(&entityGUID, "guid", "g", "", "the entity GUID to retrieve tags for")
utils.LogIfError(cmdTagsGet.MarkFlagRequired("guid"))

fi, err := os.Stdin.Stat()
if err != nil {
panic(err)
}

inPipe = (fi.Mode() & os.ModeCharDevice) == 0

if inPipe {
guidArray, err := utils.ReadStdin(os.Stdin, []string{"guid"})
if err != nil {
panic(err)
}
guid = guidArray[0]["guid"]
} else {
cmdTagsGet.Flags().StringVarP(&entityGUID, "guid", "g", "", "the entity GUID to retrieve tags for")
utils.LogIfError(cmdTagsGet.MarkFlagRequired("guid"))
}

cmdTags.AddCommand(cmdTagsDelete)
cmdTagsDelete.Flags().StringVarP(&entityGUID, "guid", "g", "", "the entity GUID to delete tags on")
Expand Down
67 changes: 67 additions & 0 deletions internal/utils/utils.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package utils

import (
"bufio"
"fmt"
"io"
"reflect"
"strings"

log "github.com/sirupsen/logrus"
"github.com/tidwall/gjson"
)

type StructToMapCallback func(item interface{}, fields []string) map[string]interface{}
Expand Down Expand Up @@ -39,6 +43,69 @@ func StructToMap(item interface{}, fields []string) map[string]interface{} {
return mapped
}

type PipeReader interface {
ReadPipe(r io.Reader) (string, error)
}

type StdinPipeReader struct{}

func (spr StdinPipeReader) ReadPipe(r io.Reader) (string, error) {
text := ""
var err error = nil
scanner := bufio.NewScanner(r)

for scanner.Scan() {
text = fmt.Sprintf("%s%s ", text, strings.TrimSpace(scanner.Text()))
}

if scanErr := scanner.Err(); scanErr != nil {
err = scanErr
}

return strings.TrimSpace(text), err
}

func JSONToFilteredMap(r string, selectors []string) ([]map[string]string, error) {
text := r

// always start with an array of values
if strings.HasPrefix(text, "{") {
text = fmt.Sprintf("[ %s ]", text)
}

// returns []gjson.Result
jsonArray := gjson.Parse(text).Array()

resultsArray := make([]map[string]string, len(jsonArray))

for index, resultObj := range jsonArray {
resultMap := make(map[string]string)
for _, selector := range selectors {
// Convert every value to a string
resultMap[selector] = resultObj.Get(selector).String()
}
resultsArray[index] = resultMap
}

return resultsArray, nil
}

func ReadStdin(reader io.Reader, selectorList []string) ([]map[string]string, error) {
pipe := StdinPipeReader{}

jsonString, pipeErr := pipe.ReadPipe(reader)
if pipeErr != nil {
return nil, pipeErr
}

filteredMap, mapErr := JSONToFilteredMap(jsonString, selectorList)
if mapErr != nil {
return nil, mapErr
}

return filteredMap, nil
}

// LogIfError wraps the err nil check to cleanup the code.
// Logs at Error level
func LogIfError(err error) {
Expand Down
Loading

0 comments on commit 0dcfa72

Please sign in to comment.