forked from newrelic/newrelic-cli
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(release): newrelic#45 add support for command chaining
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 refactor(release): WIP clean up logic in init function This commit refactors the init logic to be cleaner, handle more flags, and prepare for bulk actions feat(release): WIP collect values from multiple results and add tests This commit adds more testing and allows reading values from multiple results refactor(release): WIP create pipe module This commit pulls the pipe input logic out of the utils module and into a new pipe module. It also abstracts the PipeInput map from other modules. feat(release): make newrelic entity tags get cmd take stdin This commit is the last step I can take towards making the entity tags get command return bulk data based on stdin. Work has to be done on newrelic-client-go in order to support bulk guid queries https://github.com/newrelic/newrelic-client-go/blob/13ed6ee7fa172cce9218cf30e98eab6e1805541d/pkg/entities/tags.go#L124 fix(release): make Get and GetInput always return same values This commit checks for the existence of the pipeInput internal state, so even if GetInput is called more than once, nothing is overwritten and Get returns the same values. More testing and test refactors are also included.
- Loading branch information
1 parent
f03c256
commit 421d868
Showing
5 changed files
with
624 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
package pipe | ||
|
||
import ( | ||
"bufio" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"os" | ||
"strings" | ||
|
||
"github.com/tidwall/gjson" | ||
|
||
"github.com/newrelic/newrelic-cli/internal/utils" | ||
) | ||
|
||
type pipeReader interface { | ||
ReadPipe() (string, error) | ||
} | ||
|
||
type stdinPipeReader struct { | ||
input io.Reader | ||
} | ||
|
||
func (spr stdinPipeReader) ReadPipe() (string, error) { | ||
text := "" | ||
var err error = nil | ||
scanner := bufio.NewScanner(spr.input) | ||
|
||
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 | ||
|
||
if !gjson.Valid(text) { | ||
return nil, errors.New("invalid JSON received by stdin") | ||
} | ||
|
||
// 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 { | ||
if value := resultObj.Get(selector); value.Exists() { | ||
// Convert every value to a string | ||
resultMap[selector] = value.String() | ||
} | ||
} | ||
resultsArray[index] = resultMap | ||
} | ||
|
||
return resultsArray, nil | ||
} | ||
|
||
func readStdin(pipe pipeReader, selectorList []string) ([]map[string]string, error) { | ||
jsonString, pipeErr := pipe.ReadPipe() | ||
if pipeErr != nil { | ||
return nil, pipeErr | ||
} | ||
|
||
filteredMap, mapErr := jsonToFilteredMap(jsonString, selectorList) | ||
if mapErr != nil { | ||
return nil, mapErr | ||
} | ||
|
||
return filteredMap, nil | ||
} | ||
|
||
func pipeInputExists() bool { | ||
fi, err := os.Stdin.Stat() | ||
if err != nil { | ||
return false | ||
} | ||
return (fi.Mode() & os.ModeCharDevice) == 0 | ||
} | ||
|
||
func getPipeInputInnerFunc(pipe pipeReader, pipeInputExists bool, acceptedPipeInput []string) map[string][]string { | ||
if pipeInputExists { | ||
pipeInputMap := map[string][]string{} | ||
inputArray, err := readStdin(pipe, acceptedPipeInput) | ||
if err != nil { | ||
utils.LogIfError(err) | ||
return map[string][]string{} | ||
} | ||
for _, key := range acceptedPipeInput { | ||
var collectedItemsForKey []string | ||
for _, value := range inputArray { | ||
collectedItemsForKey = append(collectedItemsForKey, value[key]) | ||
} | ||
pipeInputMap[key] = collectedItemsForKey | ||
} | ||
return pipeInputMap | ||
} | ||
return map[string][]string{} | ||
} | ||
|
||
func getPipeInputFactory(pipe pipeReader, predicate func() bool) func([]string) { | ||
return func(acceptedPipeInput []string) { | ||
if pipeInput == nil { | ||
pipeInput = getPipeInputInnerFunc(pipe, predicate(), acceptedPipeInput) | ||
} | ||
} | ||
} | ||
|
||
var pipeInput map[string][]string | ||
|
||
var GetInput = getPipeInputFactory(stdinPipeReader{input: os.Stdin}, pipeInputExists) | ||
|
||
func Get(inputKey string) ([]string, bool) { | ||
if pipeInput == nil { | ||
return nil, false | ||
} | ||
value, ok := pipeInput[inputKey] | ||
return value, ok | ||
} | ||
|
||
func Exists(inputKey string) bool { | ||
_, ok := Get(inputKey) | ||
return ok | ||
} |
Oops, something went wrong.