mattermost-plugin-github icon indicating copy to clipboard operation
mattermost-plugin-github copied to clipboard

GH-325 - Use GitHub's GraphQL service

Open srgyrn opened this issue 5 years ago • 39 comments

Summary

This pull request adds functionality to use Github GraphQL API and replaces REST API with GraphQL API where pull request details are retrieved.

The new graphql package communicates with GraphQL API via https://github.com/shurcooL/githubv4. Anything related to this third party package are encapsulated for decoupling reasons:

  • Query struct needed by githubv4 can be created in Go code with the new graph/query.
  • Response from githubv4 is converted to a local type Response (server/plugin/internal/graphql/response.go). All githubv4 types are converted to native data types.

Ticket Link

https://github.com/mattermost/mattermost-plugin-github/issues/325

srgyrn avatar Nov 11 '20 14:11 srgyrn

Hello @srgyrn,

Thanks for your pull request! A Core Committer will review your pull request soon. For code contributions, you can learn more about the review process here.

mattermod avatar Nov 11 '20 14:11 mattermod

Codecov Report

Merging #404 (cd7f26e) into master (4b4bc21) will increase coverage by 5.08%. The diff coverage is 49.89%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master     #404      +/-   ##
==========================================
+ Coverage   19.34%   24.42%   +5.08%     
==========================================
  Files          11       21      +10     
  Lines        2766     3132     +366     
==========================================
+ Hits          535      765     +230     
- Misses       2193     2305     +112     
- Partials       38       62      +24     
Impacted Files Coverage Δ
server/plugin/api.go 8.12% <0.00%> (+0.66%) :arrow_up:
server/plugin/internal/graphql/client.go 0.00% <0.00%> (ø)
server/plugin/plugin.go 4.62% <0.00%> (-0.22%) :arrow_down:
server/plugin/search/pullrequest.go 0.00% <0.00%> (ø)
server/plugin/search/queries.go 0.00% <0.00%> (ø)
server/plugin/internal/graphql/query/union.go 62.50% <62.50%> (ø)
server/plugin/internal/graphql/response.go 63.33% <63.33%> (ø)
server/plugin/internal/graphql/query/builder.go 72.34% <72.34%> (ø)
server/plugin/internal/graphql/query/node.go 87.50% <87.50%> (ø)
server/plugin/internal/graphql/query/scalar.go 88.23% <88.23%> (ø)
... and 16 more

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 4b4bc21...dccb242. Read the comment docs.

codecov-io avatar Nov 11 '20 14:11 codecov-io

@srgyrn That you very much for raising this PR! Given the sheer size I will need a few days to properly review it.

hanzei avatar Nov 12 '20 10:11 hanzei

@reflog I'm wondering if you are interested in also reviewing this PR as your worked with GraphQL in the past IIRC.

hanzei avatar Nov 12 '20 10:11 hanzei

@srgyrn thanks for your contribution! Before we dive into the review - I wanted to ask - what prompted it? What are we trying to fix? It seems (from a cursory glance at the code) that quite alot of new code was added, so I want to understand the benefit first.

Thanks again!

reflog avatar Nov 12 '20 11:11 reflog

@reflog I was there when the ticket was created. The main problem is that for fetching all the needed information for the PRs and issues we now have to do 3 different calls for each open issue assigned. That can consume the easily the allowed rates and takes significant time to load.

Using GraphQL potentially will solve this, since we are able to get the whole information we need in just one query.

You can get a bit more of insight in the linked GH issue.

larkox avatar Nov 12 '20 11:11 larkox

@larkox - awesome. Thanks for the details. I'll get on the review

reflog avatar Nov 12 '20 11:11 reflog

@srgyrn question - what is the reason for implementing so many 'generic' graphql methods, when all we need to do is to construct a struct with proper tags, like:

var q struct {
	RepositoryOwner struct {
		Login        string
		Organization struct {
			Description string
		} `graphql:"... on Organization"`
		User struct {
			Bio string
		} `graphql:"... on User"`
	} `graphql:"repositoryOwner(login: \"github\")"`
}

and execute it? I don't see the reason for having this 'query builder' pattern and scalar related stuff in this plugin.

reflog avatar Nov 17 '20 09:11 reflog

Hey @reflog,

Having worked in projects before which communicate with Elasticsearch without a client like Elastica, I've seen codebases end up getting polluted with so many, so long and sometimes redundant json queries. Also, using the structs expected by githubv4 within the project's code will make the project very dependent to this third party package. You can see in the example structs given in githubv4's repo, package specific data types like githubv4.String, githubv4.HTML or enums are used. So anyone who wants to contribute to the project will have to know and/or learn about them too.

True, my suggested way makes query creating longer and involves more error checks than just writing down a struct. However, it leaves the project's dependency on a third party package just at the query builder bit. Creating scalar queries is all about copying and pasting names and values from the GraphQL Explorer. More complex queries (object, union...) is all about setting children in them.

With the query builder, you don't need to worry about package specific data types at all once required ones are added to the decorator/scalartypes.go. The same thing goes for struct tags as well. You can simply add a new one with validations so there won't be an unnecessary call to the GraphQL API if an unexpected value is set.

srgyrn avatar Nov 17 '20 12:11 srgyrn

@srgyrn - in general, I agree with you. Having better abstractions is great for large projects. This is however seems to me like an overkill for this plugin. But I am about 0/5 on this. Let's pull a couple more people in for the discussion. cc @ethervoid @agnivade

reflog avatar Nov 24 '20 08:11 reflog

I really don't have any knowledge about GraphQL, but from a software design perspective - the tradeoff here is codebase size vs too many abstractions. For a big project that heavily uses GraphQL queries, it makes sense to write a low-level library to manage boilerplates in a proper way. But for a small project which uses GraphQL sparingly, I would lean towards using the structs to keep things simple and maintainable.

There is a cost to more code that we add. That has more chance of bugs, more lines of code to maintain and debug. The benefits of additional code should outweigh these costs.

I really don't have much context on the future direction that this plugin will take or how much more GraphQL might be used in future, so I will leave it to the plugin owners to take a call on that.

@srgyrn - In any case, it is wonderful to see the effort that you have put in to write so much code and also tests for it. Thank you for your time :+1:

agnivade avatar Nov 24 '20 08:11 agnivade

First of all, I love the work you've done here, and seeing the number of tests you made to cover it is amazing. Thank you very much

Regarding the PR itself, I agree with you in the base, it's really useful to decouple third-party libraries and I like the idea but I also agree with the comment from other reviewers, this looks a little overkill for this plugin.

If I may suggest I'd do something like this. Decouple the library using a service so we can mock that up and test it properly. You can also encapsulate the queries inside and in case the situation explodes as you said from past experiences we can connect a new "Client" and use the builder.

Again I want to stress that I think this is amazing work and has a lot of effort poured into it. Thank you

ethervoid avatar Nov 24 '20 10:11 ethervoid

@reflog @agnivade @ethervoid Thank you for your time and feedback :)

@ethervoid Shall I remove the query builder and integrate the package like the example you've suggested or close the PR?

srgyrn avatar Nov 25 '20 11:11 srgyrn

@srgyrn thank you! Please don't close the PR, just simplify it if possible. @ethervoid suggestion sounds good

reflog avatar Nov 25 '20 11:11 reflog

Agree with @reflog , you can work on this PR :) . Thank you!

ethervoid avatar Nov 25 '20 11:11 ethervoid

This PR has been automatically labelled "stale" because it hasn't had recent activity. A core team member will check in on the status of the PR to help with questions. Thank you for your contribution!

/cc @jasonblais @jfrerich @emilyacook

mattermod avatar Dec 15 '20 01:12 mattermod

Can we get a consensus or proposal from the reviewers that will provide actionable items for the PR author.

Thanks for your work, @srgyrn

jfrerich avatar Dec 18 '20 22:12 jfrerich

The suggested proposal is right here https://github.com/mattermost/mattermost-plugin-github/pull/404#issuecomment-732816812.

agnivade avatar Dec 19 '20 03:12 agnivade

Hello everyone, I've been a bit busy at work but started working on the requested changes this weekend. I should be pushing them next week. I'm sorry I haven't been responsive enough.

srgyrn avatar Dec 20 '20 22:12 srgyrn

Hello @hanzei, I did the requested changes. The golangci-lint fails but it seems to be a false positive:

  • https://github.com/golangci/golangci-lint/issues/1517
  • https://gitlab.com/opennota/check#known-limitations

srgyrn avatar Dec 26 '20 11:12 srgyrn

This PR has been automatically labelled "stale" because it hasn't had recent activity. A core team member will check in on the status of the PR to help with questions. Thank you for your contribution!

/cc @jasonblais @jfrerich @emilyacook

mattermod avatar Jan 12 '21 01:01 mattermod

@hanzei, gentle ping for review :)

jfrerich avatar Jan 13 '21 17:01 jfrerich

This PR has been automatically labelled "stale" because it hasn't had recent activity. A core team member will check in on the status of the PR to help with questions. Thank you for your contribution!

/cc @jasonblais @jfrerich @emilyacook

mattermod avatar Jan 24 '21 01:01 mattermod

@srgyrn, some folks are just getting back from a break but will get to this shortly. @hanzei, gentle reminder to review

jfrerich avatar Jan 26 '21 16:01 jfrerich

This PR has been automatically labelled "stale" because it hasn't had recent activity. A core team member will check in on the status of the PR to help with questions. Thank you for your contribution!

/cc @jasonblais @jfrerich @emilyacook

mattermod avatar Feb 06 '21 01:02 mattermod

This PR has been automatically labelled "stale" because it hasn't had recent activity. A core team member will check in on the status of the PR to help with questions. Thank you for your contribution!

/cc @jasonblais @jfrerich @emilyacook

mattermod avatar Feb 26 '21 01:02 mattermod

@DHaussermann may you please take a look and finish the review cycle? Thx!

ethervoid avatar Mar 02 '21 08:03 ethervoid

@srgyrn Awesome work! This is a great performance improvement and simplifies the communication with GitHub. I have one comment on the functionality behavior.

Before this PR, the functionality worked like:

  • The user visits a tab on the LHS such as "Your Open Pull Requests"
  • The frontend sends a request to fetch PR details and provides a list of PR number / repo pairs (specific to the current tab's category)
  • The backend performs an HTTP request per each pair

Now the backend is using GraphQL to fetch all at once, but it is not taking into account the pairs being sent by the client. Is the backend fetching/returning more than necessary in the GraphQL query? Or is this negligible due to the dataset generally being on the same order of size across each tab category? cc @hanzei

mickmister avatar Mar 03 '21 15:03 mickmister

/update-branch

DHaussermann avatar Mar 04 '21 19:03 DHaussermann

/update-branch

hanzei avatar Mar 16 '21 19:03 hanzei