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

Refactor Requester into multiple smaller classes #697

Merged
merged 17 commits into from
Feb 20, 2020

Conversation

bitwiseman
Copy link
Member

@bitwiseman bitwiseman commented Feb 13, 2020

Description

Refactoring from GitHub and Requester being monolithic centers of work to having the work split among smaller classes. GitHub is still the root of interactions, but holds a reference to a GitHubClient that has all the connection and communication. Requester still remains as a thin helper that will be removed in a future change.

Improvements

  • Abstraction/encapsulation of HttpUrlConnection interactions in GitHubHttpUrlConnectionClient
  • Builders and mostly immutable instances - better thread safety and clarity
  • Client/Request/Response model in line with modern Http frameworks
  • PagedIterable is the sole entry point for retrieval of all item arrays, lists, or iterables.
  • *Iterable and *Iterator classes encapsulate all the work done to retrieve pages of items

This change encapsulates most of the dependencies on HttpUrlConnection as much as possible, to allow moving to other more modern frameworks at some point. This also separates the concerns more clearly.

There's still some work to be done here, but this change is more than enough for now.

@bitwiseman bitwiseman force-pushed the tast/response-info branch 3 times, most recently from 62eb088 to 7591215 Compare February 13, 2020 06:10

/**
* A builder pattern for making HTTP call and parsing its output.
* A thin helper for {@link GitHubRequest.Builder} that includes {@link GitHubClient}.
Copy link
Member Author

@bitwiseman bitwiseman Feb 13, 2020

Choose a reason for hiding this comment

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

This is the really the summary of this change - a refactoring from GitHub and Requester being monolithic centers of work to having the work split between GitHubClient, GitHubRequest, GitHubRequest.Builder, GitHubResponse, GitHubResponse.ResponseInfo, and various *Iterables and *Iterators. Requester still remains as a thin helper and GitHub is still large but is mostly customer facing methods with GitHubClient handling internals.

@bitwiseman bitwiseman changed the title Tast/response info Refactor Requester into multiple smaller classes Feb 13, 2020
@bitwiseman bitwiseman marked this pull request as ready for review February 13, 2020 07:27
*/
@Nonnull
static URL getApiURL(String apiUrl, String tailApiUrl) throws MalformedURLException {
if (tailApiUrl.startsWith("/")) {
Copy link
Contributor

@kshultzCB kshultzCB Feb 16, 2020

Choose a reason for hiding this comment

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

Is there a circumstance where tailApiUrl will not start with a "/"? I didn't see any tests in GitHubConnectionTest that behave this way, so just wondering how we'd run up against this, and what it looks like when we do.

Copy link
Member Author

Choose a reason for hiding this comment

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

Good question. There are a small number of cases where the API returns a URL that is then used for other things.

https://github.com/github-api/github-api/pull/697/files#diff-64dfb4dfbe21520e8735454a113c45a7R548-R560

https://github.com/github-api/github-api/blob/ff3136df7069d22d937daa8f2cddc357c777e07e/src/main/java/org/kohsuke/github/GHContent.java#L225-L227

https://github.com/github-api/github-api/blob/ff3136df7069d22d937daa8f2cddc357c777e07e/src/main/java/org/kohsuke/github/GHContent.java#L236-L240

Also, for paginated result, the full url for the next page is provided in the Link header , so when we call getApiURL we want to use that url, whatever it happens to be.

https://github.com/github-api/github-api/blob/dc33e28452532b94c6d46e435c192ae043849ca9/src/main/java/org/kohsuke/github/GitHubPageResponseIterator.java#L68-L77

I don't love this lack of clarity, but I didn't feel confident that I had tests for all cases where that URL didn't start with /, so I've kept the behavior where this method just handles it. That said, I should change L77 to use setRawUrl. Thanks!

// by convention Java constant names are upper cases, but github uses
// lower-case constants. GitHub also uses '-', which in Java we always
// replace with '_'
return en.toString().toLowerCase(Locale.ENGLISH).replace('_', '-');
Copy link
Contributor

Choose a reason for hiding this comment

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

Comments like this are super helpful. :)

Are there other places where we might need to do such a conversion? I only see one instance of replace('_', '-') in the whole plugin. Might be fine, it just surprised me that there aren't more.

Copy link
Member Author

Choose a reason for hiding this comment

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

The other places are mostly done automatically by Jackson.

https://github.com/github-api/github-api/pull/697/files#diff-04099021093ec34af9d55a855cc1e6fdR85-R90

Hm, the toLowerCase may not be needed due to line 88. And line 89 means that, given a field like create_date, Jackson will automatically look for a field with the name createdDate and then fall back to create_date if it doesn't find one.

However, this setting was only added recently, there are still a lot private fields with underscores in them.

Copy link
Member Author

Choose a reason for hiding this comment

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

Opened #700 to clean this up.

}
return (B) this;
}

Copy link
Contributor

Choose a reason for hiding this comment

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

This is a somewhat arbitrary place for me to put this question, but it's as good as any. There are a lot of with(thing, thing) methods here with different parameters. Why is that? I mean, I'm sure there's a perfectly good reason for it, I just am having trouble putting it together on my own.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure. These were here when I arrived. I'm not sure it they are still all needed, per se. I think I tried to remove them at one point and started getting errors ... but I don't remember now.

Copy link
Member Author

Choose a reason for hiding this comment

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

Opened #699 to come back to this.


current = base.next();
wrapUp(current);
if ((current == null || current.length <= pos) && base.hasNext()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Not really specific to this PR, but, I could use a quick overview of the relationships between current, pos, and base. I thought I had it figured out for a minute but I guess I don't.

Copy link
Member Author

Choose a reason for hiding this comment

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

I can pick better names for these and give them a little more commenting.

Copy link
Contributor

@kshultzCB kshultzCB left a comment

Choose a reason for hiding this comment

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

Mostly this is me making sure I actually understand what's going on in here. :)

@bitwiseman
Copy link
Member Author

From my discussion of this with @kshultzCB - This is very much a refactor with no significant performance changes.

@bitwiseman
Copy link
Member Author

@kshultzCB Thanks! Great feedback. Made updates to address and filed a couple of issue to track future work.

We don't need two layers of PageIterator just to get the final response.
Also made iterators thread-safe.
And added more detailed comments.
Copy link
Contributor

@kshultzCB kshultzCB left a comment

Choose a reason for hiding this comment

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

Things are getting pretty desperate when I'm left to nitpick at comments. :) I love the additional clarity here, especially in PagedIterator, it's super helpful.

Comment on lines 127 to 128
* If {@link #next} is not {@code null}, no further action is need. If {@link #next} is {@code null} and
* {@link #nextRequest} is {@code null}, there are no more pages to fetch.
Copy link
Contributor

Choose a reason for hiding this comment

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

Pedantic documentation hat, engage: replace "need" with "needed."

* {@link #nextRequest} is {@code null}, there are no more pages to fetch.
* </p>
* <p>
* Otherwise, a new response page is fetched using {@link #nextRequest}. The response is then check to see if there
Copy link
Contributor

Choose a reason for hiding this comment

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

Pedantic documentation hat once more: replace "check" with "checked."

These were not synchronized before we should leave them fix this in a future change
Copy link
Contributor

@kshultzCB kshultzCB left a comment

Choose a reason for hiding this comment

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

+1 to leaving this as a refactor, and tabling the thread-safe idea for a different time.

@bitwiseman bitwiseman merged commit 9018d72 into hub4j:master Feb 20, 2020
@bitwiseman bitwiseman deleted the tast/response-info branch February 20, 2020 23:59
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.

2 participants