-
Notifications
You must be signed in to change notification settings - Fork 76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(release): Issue #45 add support for command chaining #368
feat(release): Issue #45 add support for command chaining #368
Conversation
|
Oh interesting, thanks for putting this up @nicolasjhampton. One idea that @sanderblue and I talked about was having a "mother" parent object that would nest each of the kinds of objects we expect to want to take over STDIN. In this way, the shape of the received object could change, and each command would know what it would need to expect. I didn't play with this idea to know if it would work though. |
@zlesnr That sounds like it's necessary for encoding/json and most parsers in go. I did the same thing, just without the struct list. I got around needing to know the shape by finding a parser that didn't require a struct that knows the shape of the input. That way, as long as it's valid json in the stdin with the attribute you need at the first level of the object, the command can read only the attributes it needs, never having to know the full shape of the data coming in. https://github.com/tidwall/gjson |
If this is a good general direction and there's still buy-in for this work, I'd like to clean up some code, make another helper method or two, add more error handling and tests, and finish out the entity commands as a proof of concept. |
1842472
to
db3b4c5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really neat. I checked out the code to play with it a bit, and I like what you're doing here. Some of the methods are hard to read, or to know what they do in the schema of things, and so at least I'd think they need some documentation about their purpose. I left a few comments, so let me know what you think.
internal/pipe/pipe_test.go
Outdated
} | ||
|
||
for _, c := range cases { | ||
input := c["input"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest that the test cases here be a struct rather than a map. Then we can refer to fields on the struct for the expected test cases, and it's a little cleaner to read.
For an example, you can see the test struct here: https://github.com/newrelic/tutone/blob/master/internal/schema/schema_test.go#L19
That one remains a little ugly due to the whitespace handling that is part of the test, so indenting has to be specific, but you can get the idea from the struct there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like that, then the test cases can be named. I'll work on that.
utils.LogIfFatal(err) | ||
|
||
utils.LogIfError(output.Print(tags)) | ||
// Temporary until bulk actions can be build into newrelic-client-go |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What work needs to be one in the client so that we can avoid the temporary but here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Most of the API in the newrelic-client-go library aren't designed to deal with multiple entity guids. In this tag command alone, bulk entity commands for nrClient.Entities.ListTags, nrClient.Entities.DeleteTags, nrClient.Entities.DeleteTagValues, nrClient.Entities.AddTags, and nrClient.Entities.ReplaceTags need to be created.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A yes, methods that will take a list of guids to operate on.
|
||
var pipeInput map[string][]string = map[string][]string{} | ||
|
||
func Get(inputKey string) ([]string, bool) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method looks to be called once unless I overlooked another place. What do you think about folding this logic into the Exists()
method?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Get method would be called everytime a command needs to get a value. It's called once because I've only used this module on one command. It's just good, clean, self-explanatory API design.
internal/pipe/pipe.go
Outdated
|
||
var GetInput = getPipeInputFactory(stdinPipeReader{input: os.Stdin}, pipeInputExists) | ||
|
||
var pipeInput map[string][]string = map[string][]string{} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The logic that populates this variable is a little hard to read. If multiple getPipeInputFactory
functions are created, the calls to those methods would potentially clobber this variable and then anything relying on it might go a little pear shaped. Even in the case of GetInput()
being called with different arguments I think would change the content of this variable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getPipeInputFactory
is a lowercase function, meaning it's private to this module and can't be called outside of it. The only time it's ran is when this module is loaded. I do see how GetInput could be ran twice to alter pipeInput, however. I'll add code to prevent pipeInput from being overridden if GetInput is ran twice.
fc41f15
to
e6a8b25
Compare
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. docs(release): add docs comments to pipe package docs(release): fix spelling errors
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great. Thanks for adding some method docs too! Let me know when its no longer WIP and I'll run the tests locally before merge.
} | ||
|
||
for _, c := range cases { | ||
mockStdin := strings.NewReader(c.Input) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is neat.
The tests passed in #380. Merging. |
What
This PR adds a pipe module to read stdin for piping input to commands, allowing each command
be piped to the next. I've also implemented this pipe logic for one command, the
entity tags get
command. You can try this functionality by running:
newrelic entity search --name [app name] | newrelic entity tags get
Next work needed
Currently the above action only returns tags for the first entity found. Work has to be done in
newrelic-client-go to add bulk queries to the API. Specifically for this tag query, something like this query needs
to be added (this is incorrect, working on it in a different repo):
For the entity tag command alone, nrClient.Entities.ListTags, nrClient.Entities.DeleteTags, nrClient.Entities.DeleteTags, nrClient.Entities.DeleteTagValues, nrClient.Entities.AddTags, and nrClient.Entities.ReplaceTags all need bulk versions written that can take multiple entity guids.
Documentation
Doc comments included for all of the Public and some of the private API
#45