Skip to content
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

Add support for listing and getting repository/organization webhook deliveries #1934

Merged
merged 15 commits into from
Jul 13, 2021

Conversation

mumoshu
Copy link
Contributor

@mumoshu mumoshu commented Jul 6, 2021

Resolves #1933

To make this PR minimal as possible for ease of reviewing, I intentionally included only four APIs related to the webhook deliveries API:

  • Listing repository hook deliveries
  • Getting a specific repository hook delivery
  • Listing organization hook deliveries
  • GEtting a specific organization hook delivery

That being said, the following methods aren't covered in this PR. If you like, I'd gladly submit subsequent PRs to add all of them.

  • Trigger redelivery of a hook delivery
  • List/get/redelivery for organizational webhooks

Note that changes on the following files are completely generated by running go generate as documented in your guide, so you won't need to read them that carefully.

  • github/github-accessors_test.go
  • github/github-accessors.go

FWIW, you can see how these additional methods can be used to retrieve historical webhook deliveries here:

https://github.com/actions-runner-controller/actions-runner-controller/blob/c3cfab81c85d33c65f7b6f6443da0d2f57fd72db/pkg/githubwebhookdeliveryforwarder/githubwebhookdelivery.go#L96-L139

The part that propagates Cursor from the response to the ListCursorOptions for the next paginated request is beneficial to understanding how the cursor can be used for pagination.

@google-cla google-cla bot added the cla: yes Indication that the PR author has signed a Google Contributor License Agreement. label Jul 6, 2021
return h, resp, nil
}

func (d *HookDelivery) ParseRequestPayload() (interface{}, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is a utility function to ease parsing event payloads, and it is derived from the equivalent function for Events API

// ParsePayload parses the event payload. For recognized event types,
// a value of the corresponding struct type will be returned.
func (e *Event) ParsePayload() (payload interface{}, err error) {

Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Contributor Author

@mumoshu mumoshu Jul 6, 2021

Choose a reason for hiding this comment

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

@gmlewis Hey! Interesting idea. How would you like to do that? That snippet in event.go switch on Type whose the value can be e.g. CheckRunEvent. On the other hand, this one switches on event field where the value is e.g. check_run. They're very similar but different enough to worth a dedicated function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also note that this is almost fully covered by TestHookDelivery_ParsePayload, if that helps.

Copy link
Collaborator

Choose a reason for hiding this comment

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

What I'm thinking here is to make a helper function that both ParsePayload and ParseRequestPayload both call.

The helper method could take an anonymous function to perform the required mapping... one of them would use the eventTypeMethod map found in messages.go.

Let me know if you don't think this is possible and I'll download your PR and see if I can get it to work.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Here's my proposed replacement for this method:

// ParseRequestPayload parses the request payload. For recognized event types,
// a value of the corresponding struct type will be returned.
func (d *HookDelivery) ParseRequestPayload() (interface{}, error) {
	eType, ok := eventTypeMapping[*d.Event]
	if !ok {
		return nil, fmt.Errorf("unsupported event type %q", *d.Event)
	}

	e := &Event{Type: &eType, RawPayload: d.Request.RawPayload}
	return e.ParsePayload()
}

@codecov
Copy link

codecov bot commented Jul 6, 2021

Codecov Report

Merging #1934 (123bafa) into master (05e95d3) will increase coverage by 0.17%.
The diff coverage is 96.49%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1934      +/-   ##
==========================================
+ Coverage   97.65%   97.82%   +0.17%     
==========================================
  Files         105      107       +2     
  Lines        6786     6865      +79     
==========================================
+ Hits         6627     6716      +89     
+ Misses         86       82       -4     
+ Partials       73       67       -6     
Impacted Files Coverage Δ
github/repos_hooks_deliveries.go 93.10% <93.10%> (ø)
github/github.go 97.07% <100.00%> (+0.06%) ⬆️
github/orgs_hooks_deliveries.go 100.00% <100.00%> (ø)
github/pulls.go 97.05% <0.00%> (ø)
github/checks.go 100.00% <0.00%> (ø)
github/activity.go 100.00% <0.00%> (ø)
github/event_types.go 100.00% <0.00%> (ø)
github/code-scanning.go 100.00% <0.00%> (ø)
github/issues_timeline.go 100.00% <0.00%> (ø)
... and 7 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 05e95d3...123bafa. Read the comment docs.

Copy link
Collaborator

@gmlewis gmlewis left a comment

Choose a reason for hiding this comment

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

Thank you, @mumoshu.
I'm thinking that this can be improved by reusing more of the existing event code or refactoring it so that it is easier to reuse.

Also, the drop in code coverage is a bit concerning, but this might get fixed by reducing the amount of new code and reusing more of the existing code.

@@ -481,25 +489,33 @@ func (r *Response) populatePageValues() {
if err != nil {
continue
}
page := url.Query().Get("page")
if page == "" {

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmmm... I'm not sure I'm happy with this section (especially regarding increasing the already-deep indentation levels).

Since "cursor" is the less-common and smaller case, let's please handle it first, and let's revert all the changes made to the handling of the "page" section, except maybe to pull out the shared query first... something like this:

q := url.Query()
if cursor := q.Get("cursor"); cursor != "" {
...
}

page := q.Get("page")
if page == "" {
  continue
}
...

Copy link
Contributor Author

@mumoshu mumoshu Jul 6, 2021

Choose a reason for hiding this comment

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

Thanks! Addressed in f6d688a

type HookDelivery struct {
ID *int64 `json:"id"`
GUID *string `json:"guid"`
DeliveredAt *time.Time `json:"delivered_at"`
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
DeliveredAt *time.Time `json:"delivered_at"`
DeliveredAt *Timestamp `json:"delivered_at"`

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed in ff5a47a !

github/repos_hooks_deliveries.go Show resolved Hide resolved
}

type HookResponse struct {
Header map[string]string `json:"headers,omitempty"`
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
Header map[string]string `json:"headers,omitempty"`
Headers map[string]string `json:"headers,omitempty"`

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in dffbb1c

//
// GitHub API docs: https://docs.github.com/en/rest/reference/repos#list-deliveries-for-a-repository-webhook
func (s *RepositoriesService) ListHookDeliveries(ctx context.Context, owner, repo string, id int64, opts *ListCursorOptions) ([]*HookDelivery, *Response, error) {
u := fmt.Sprintf("repos/%v/%v/hooks/%d/deliveries", owner, repo, id)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
u := fmt.Sprintf("repos/%v/%v/hooks/%d/deliveries", owner, repo, id)
u := fmt.Sprintf("repos/%v/%v/hooks/%v/deliveries", owner, repo, id)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fine! But out of curiosity, why do you prefer using %v for an integer value?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 5f0e1e4 anyway

Copy link
Collaborator

Choose a reason for hiding this comment

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

I prefer %v for the following reasons:

  • more consistent with the rest of the repo,
  • idiomatic Go,
  • and most importantly, it makes it much easier to search for an existing endpoint when you don't have to figure out the types of the segments of the URLs... you can more easily transform a URL from the documentation into a search string into the code base.

Copy link
Contributor Author

@mumoshu mumoshu Jul 6, 2021

Choose a reason for hiding this comment

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

Gotcha! That totally makes sense. Thanks a lot for clarifying.

Regarding consistency with the rest of the repo, JFYI, I found a bunch of %d occurrences in the existing sources like repos_hook.go. Probably you'd want to fix those in another circumstance.

return h, resp, nil
}

func (d *HookDelivery) ParseRequestPayload() (interface{}, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

@ohpintu90
Copy link

Ok

@mumoshu
Copy link
Contributor Author

mumoshu commented Jul 6, 2021

@gmlewis Hey! I've addressed all the comments. Would you mind reviewing once again?

Also, please feel free to squash on merge, or just tell me to do so, if necessary. Thanks!

@mumoshu
Copy link
Contributor Author

mumoshu commented Jul 6, 2021

FYI: I've added 431c77c for improving test coverage

@mumoshu
Copy link
Contributor Author

mumoshu commented Jul 6, 2021

I've added 23922a8 for more code coverage on cursor handling.

All the new codes should have been covered. I have no immediate plan to add more commits now so it should be a good timing for you to review again 😄

@mumoshu
Copy link
Contributor Author

mumoshu commented Jul 6, 2021

Apparently, the last commit 23922a8 is not included in https://app.codecov.io/gh/google/go-github/branch/webhook-deliveries-api yet. But theoretically speaking the code coverage shouldn't get decreased now, as I've already added enough tests to cover all the new code paths.

@gmlewis
Copy link
Collaborator

gmlewis commented Jul 7, 2021

Also, please feel free to squash on merge, or just tell me to do so, if necessary. Thanks!

We always squash and merge in this repo, so don't worry about the number of commits. In fact, we prefer that author's don't force-push so that it is easier for reviewers to see what changes have been made since the last review.

r.Cursor = cursor
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Are "cursor" and "page" mutually exclusive? (I think the answer is "yes" but I could be wrong.)

If so, do you want to add a continue within this if block just to make that clear?
I'm actually fine either way... your call.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks! Yes, I believe so, although it isn't documented anywhere. Added continue in 72c3380

InstallationID *string `json:"installation_id"`
RepositoryID *int64 `json:"repository_id"`

// Request is populated by GetHookDelivery
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit:

Suggested change
// Request is populated by GetHookDelivery
// Request is populated by GetHookDelivery.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 5b86aff 👍


// Request is populated by GetHookDelivery
Request *HookRequest `json:"request,omitempty"`
// Response is populated by GetHookDelivery
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit:

Suggested change
// Response is populated by GetHookDelivery
// Response is populated by GetHookDelivery.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed in 5b86aff 👍

return Stringify(d)
}

type HookRequest struct {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you please add a comment here describing // HookRequest ... ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added in 194eff6 👍

return Stringify(r)
}

type HookResponse struct {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you please add a comment here describing // HookResponse ... ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added in 194eff6 👍

return h, resp, nil
}

func (d *HookDelivery) ParseRequestPayload() (interface{}, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

What I'm thinking here is to make a helper function that both ParsePayload and ParseRequestPayload both call.

The helper method could take an anonymous function to perform the required mapping... one of them would use the eventTypeMethod map found in messages.go.

Let me know if you don't think this is possible and I'll download your PR and see if I can get it to work.

@mumoshu
Copy link
Contributor Author

mumoshu commented Jul 8, 2021

What I'm thinking here is to make a helper function that both ParsePayload and ParseRequestPayload both call.

Sounds good!

The helper method could take an anonymous function to perform the required mapping... one of them would use the eventTypeMethod map found in messages.go.

And would you mind if I also created another, reverse map of eventTypeMethod, named like typeEventMapping, and use that from ParsePayload?

@mumoshu
Copy link
Contributor Author

mumoshu commented Jul 8, 2021

Apparently, another method named ParseWebhook that calls ParsePayload seems to already use eventTypeMapping on its own 🤔 Probably adding second use of eventTypeMapping in an anonymous function defined in ParseWebhook doesn't make much sense?

Perhaps we'd better revise ParseWebHook as well? I'm pretty lost on how I should go.

@gmlewis
Copy link
Collaborator

gmlewis commented Jul 8, 2021

I'm pretty lost on how I should go.

OK. I'll take a look and get back to you.

@gmlewis
Copy link
Collaborator

gmlewis commented Jul 8, 2021

OK. I'll take a look and get back to you.

I think I've got a simple solution for you. It also caught 3 errors in your test table, which I think justifies the reuse of the existing code. I'll add review comments so you can see what I did.

Let me know what you think.

Copy link
Collaborator

@gmlewis gmlewis left a comment

Choose a reason for hiding this comment

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

Hi @mumoshu - here are my suggestions for reusing existing code, along with a few error corrections. Please let me know if they make sense to you.

Please remember to run gofmt after accepting my suggestions, as they will need proper formating.

return h, resp, nil
}

func (d *HookDelivery) ParseRequestPayload() (interface{}, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Here's my proposed replacement for this method:

// ParseRequestPayload parses the request payload. For recognized event types,
// a value of the corresponding struct type will be returned.
func (d *HookDelivery) ParseRequestPayload() (interface{}, error) {
	eType, ok := eventTypeMapping[*d.Event]
	if !ok {
		return nil, fmt.Errorf("unsupported event type %q", *d.Event)
	}

	e := &Event{Type: &eType, RawPayload: d.Request.RawPayload}
	return e.ParsePayload()
}

github/repos_hooks_deliveries_test.go Outdated Show resolved Hide resolved
github/repos_hooks_deliveries_test.go Outdated Show resolved Hide resolved
github/repos_hooks_deliveries_test.go Outdated Show resolved Hide resolved
github/repos_hooks_deliveries_test.go Outdated Show resolved Hide resolved
@mumoshu
Copy link
Contributor Author

mumoshu commented Jul 9, 2021

@gmlewis Thank you so much for taking your time and the suggestions! All looked awesome and I've included your suggestions in the last commit, 1381c0f.

Regarding the change on ParseRequestPayload, I initially had some preconceived idea that it won't be acceptable to create a new Event object just for calling ParsePayload. Apparently, it was the best impl though. Good job!

Copy link
Collaborator

@gmlewis gmlewis left a comment

Choose a reason for hiding this comment

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

Thank you, @mumoshu !
LGTM.

Awaiting second LGTM before merging.

@mumoshu
Copy link
Contributor Author

mumoshu commented Jul 12, 2021

@gmlewis Hey! Would you mind if I added another commit for adding similar support for organization variants of these APIs?

mumoshu@0a3739c.

It already has full test coverage, and I've manually verified it to work as part of actions/actions-runner-controller#682, too.

@Parker77 Hi! If you're reading this and going to put the second LGTM, please go ahead!

I'm just trying to reduce the time required on both ends to make the set of changes related to #1933. If you merged this, I'll just submit another PR for mumoshu@0a3739c.

@gmlewis
Copy link
Collaborator

gmlewis commented Jul 12, 2021

@gmlewis Hey! Would you mind if I added another commit for adding similar support for organization variants of these APIs?

Hi @mumoshu - sure, you can add more commits if you wish... please just push and don't use "force push" so that it will be easier to review more recent additions. Thank you.

@mumoshu
Copy link
Contributor Author

mumoshu commented Jul 12, 2021

@gmlewis Thanks for confirming! I've added 0a3739c and updated the PR description and title a bit to reflect the changes

@mumoshu mumoshu changed the title Add support for listing and getting repository webhook deliveries Add support for listing and getting repository/organization webhook deliveries Jul 12, 2021
Copy link
Collaborator

@gmlewis gmlewis left a comment

Choose a reason for hiding this comment

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

Thank you, @mumoshu !
Just a couple minor tweaks, please.

github/orgs_hooks_deliveries.go Show resolved Hide resolved
github/orgs_hooks_deliveries.go Show resolved Hide resolved
@gmlewis gmlewis requested a review from wesleimp July 12, 2021 12:57
Copy link
Collaborator

@gmlewis gmlewis left a comment

Choose a reason for hiding this comment

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

Thank you, @mumoshu !
LGTM.

Awaiting second LGTM before merging.

Copy link

@Parker77 Parker77 left a comment

Choose a reason for hiding this comment

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

LGTM.

@gmlewis gmlewis merged commit 3ad6169 into google:master Jul 13, 2021
@mumoshu mumoshu deleted the webhook-deliveries-api branch July 14, 2021 21:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes Indication that the PR author has signed a Google Contributor License Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for Webhook Deliveries API
4 participants