8000 Add `--json` export flag for issues and pull requests by mislav · Pull Request #3414 · cli/cli · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add --json export flag for issues and pull requests #3414

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

Merged
merged 16 commits into from
Apr 14, 2021
Merged

Add --json export flag for issues and pull requests #3414

merged 16 commits into from
Apr 14, 2021

Conversation

mislav
Copy link
Contributor
@mislav mislav commented Apr 13, 2021

The --json flag accepts a list of GraphQL fields to query for and output in JSON format. To get the list of available flags, run the command with a blank value for --json. Additional --jq and --template flags are available for post-processing JSON just like in gh api.

Screen Shot 2021-04-13 at 20 33 45

References #385
Fixes #2306, fixes #1532

Limitations:

  • Not all GraphQL fields on issues/PRs are available. Only a preset of known fields are available for fetching. Use the gh api command to directly access parts of the GitHub API that are not exposed through this functionality.
  • When nested data is requested (e.g. labels on an issue, reviews on a pull request), we only request up to 100 records per collection since this is the maximum that the GitHub API will serve us.

TODO:

  • tests
  • enable --json for status command
  • add shared help section explaining --jq and --template flags

mislav added 7 commits April 13, 2021 16:48
The `--json` flag accepts a list of GraphQL fields to query for and
output in JSON format. To get the list of available flags, run the
command with a blank value for `--json`. Additional `--jq` and
`--template` flags are available just like in `gh api`.
@vilmibm vilmibm requested review from samcoe and vilmibm April 13, 2021 19:53
Copy link
Contributor
@samcoe samcoe left a comment

Choose a reason for hiding this comment

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

Overall I really like this functionality and it worked great while testing it out. Most of my comments revolve around how we could change some of the structure of the code to be easier to test and have a greater separation of responsibilities.

@@ -33,6 +30,7 @@ type Issue struct {
Body string
CreatedAt time.Time
UpdatedAt time.Time
ClosedAt *time.Time
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be time.Time? Feels odd that the other fields are not pointers but this one is.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The other time fields are guaranteed to be present (non-null), but this one isn't, and by marking it a pointer we grant it a possible null state. This matter for the exported JSON, because otherwise a zero-value timestamp would get exported.

"statusCheckRollup",
)

func PullRequestGraphQL(fields []string) string {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can come up with a better name for this function, maybe FieldsToGraphQL or something. It is used for Issue so using PullRequest in the name was a bit confusing.

Template string
}

func (e *ExportFormat) Write(w io.Writer, data interface{}, colorEnabled bool) error {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we could make this easier to use and more testable with an interface. I was thinking something like

type Exporter interface {
    Export(io.Writer, ExportFormat, interface{}) error
}

Then we can do something similar to Browser where the default one gets set in the factory, or specified in the NewCmd___ functions, and overridden in tests where needed.

Additionally I think the colorEnabled option should be part of the ExportFormat.

Copy link
Contributor
@samcoe samcoe Apr 13, 2021

Choose a reason for hiding this comment

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

Another possibility is that the Exporter interface could do the work that api/export_pr.go is doing now. The interface could have additional functions like ExportIssues and ExportPullRequests that match up with the ExportData functions there and then those functions could call the Export function once the issue/pr has been massaged into the right format.

Copy link
Contributor

Choose a reason for hiding this comment

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

Probably could move this all into the export package as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point about the interface! I have changed this to an interface per your recommendation.

All of your suggestions are pretty good, but in this short time period I won't have time to refactor how the exporter works (e.g. to fold export_pr.go into the exporter functionality). This would be a good follow-up item.

@@ -31,6 +31,7 @@ type ListOptions struct {
Browser browser

WebMode bool
Export *cmdutil.ExportFormat
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this would be a bit clearer named ExportFormat or ExportFmt.

@@ -19,6 +19,8 @@ type StatusOptions struct {
Config func() (config.Config, error)
IO *iostreams.IOStreams
BaseRepo func() (ghrepo.Interface, error)

Export *cmdutil.ExportFormat
Copy link
Contributor

Choose a reason for hiding this comment

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

Same ExportFormat naming comment as above.

error
}

func AddJSONFlags(cmd *cobra.Command, exportTarget **ExportFormat, fields []string) {
8000 Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like we often want to use the json field passed in by the user or a set of default fields, what do you think about passing the default fields into this function and using them as the default for the json flag? If we did this we would probably also want to make sure that in the case when the json flag is not specified we don't return nil but rather a ExportFormat struct with some indication, maybe a new field, that states if a non-default format was specified.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's a good thought but I'd like to avoid specifying the default at the flag level because the flag serves two purposes at once: to opt into JSON export mode, and to control what data is fetched. If there was a default at the flag level, the reader might mis-interpret it that those default fields somehow pertain to the export, whereas they don't.

I'm fine for now passing default fields through a separate mechanism, because normal fetch represents a different mode of operation than the --json mode.

Comment on lines +48 to +49
ClosedAt *time.Time
MergedAt *time.Time
Copy link
Contributor

Choose a reason for hiding this comment

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

Same question about pointers as above.

This comment was marked as spam.

@milkly1235writer

This comment has been minimized.

@mislav mislav requested a review from samcoe April 14, 2021 16:54
Copy link
Contributor
@samcoe samcoe left a comment

Choose a reason for hiding this comment

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

Thanks for making the requested changes! This looks good to me. I just added some comments about adding some non-happy-path case tests.

if longText == "" {
longText = command.Short
}
if longText != "" && command.LocalFlags().Lookup("jq") != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

Seems like this might have meant to look up the json flag instead of the jq flag?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch! But I also wanted to apply this help text addition to the gh api command, which has --jq and --template but no --json.

outputJSON string
}{
{
name: "simple",
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would be nice to add two additional tests. One where a field is requested that is not in the input JSON just to verify that it does not blow up and a second where the field requested is not a valid field on an issue. Same goes for the PullRequest tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call! I've added an assertion that it's impossible to specify invalid fields in --json arguments. That should take care of unsanitized values making it through to the exporter.

As for the GraphQL builder and exporters themselves, they support arbitrary values so that they are easily extensible with new fields without special-casing them. However, requesting an invalid field would likely fail on the GraphQL request phase.

want string
}{
{
name: "empty",
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we could add one additional test for when the fields requested are invalid.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Output merge commit hash for Merged PR's in gh pr view Provide all metadata fields in scripted pr view
4 participants
0