Skip to content

Conversation

@bitwiseman
Copy link
Member

@bitwiseman bitwiseman commented Mar 2, 2020

Here's another possible way to address #589 . This PR uses GHLabel as an example.

@rmetzger @halkeye @PauloMigAlmeida @recena @res0nance - There's still a bunch to be done here (site currently fails due to missing JavaDocs for example), but if you look at the changes to the tests you can see what this new syntax will look like. My intent is to create a pattern that can be used consistently across the project. What do you think of this design?

Highlights:

  • Support for batching changes. Batching looks like this:
label.update().someName(value).otherName(value).done()
  • Support for single changes. They look like this:
label.set().someName(value); 
label.set().otherName(value)
  • Support for dynamically controlled "update-in-place" vs "update-to-new-instance" behavior (not active or tested yet). As in Add ability to update Github labels #649, there are times people want/expect update-in-place and other times they don't. This design will allow for both, though for now it only does "update-to-new-instance".
  • Significant code reuse - the real win of this POC is that these differing scenarios do not each require wildly differing code paths. Batch/single updating is provided by almost identical code paths. In the case of GHLabel, new instance creation does too (though in other classes creation may diverge more). "update-in-place" vs "update-to-new-instance" behavior actually identical except for one if-else in the the base class. This means that we can get this added functionality without significant added complexity.

Technical notes:

  • Tried to add final to guarantee immutability but decided against it - While guaranteeing immutability via final fields sounds great, it results in a lot of extra code to make it work with Jackson in a way that the compiler doesn't choke on.


t = r.getLabel("test");
t.setDescription("this is also a test");
GHLabel t3 = t.set().description("this is also a test");
Copy link
Member Author

Choose a reason for hiding this comment

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

Single Update


// update works on multiple changes in one call
// t.setColor("");
t2 = t.update().color("000000").description("It is dark!").done();
Copy link
Member Author

Choose a reason for hiding this comment

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

Batch update

return root.createRequest()
.withUrlPath(getApiTailUrl("labels"))
.toIterable(GHLabel[].class, item -> item.wrapUp(this));
return GHLabel.readAll(this);
Copy link
Member Author

Choose a reason for hiding this comment

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

Moved all the calls that create instances of GHLabel into that class.

@res0nance
Copy link
Contributor

Overall I like the idea because of the ability to batch changes together. The api looks very intuitive. The base class is a little confusing for people intending to create a new API but it looks as simple as it can be while providing the benefit of reusability across the project.

@bitwiseman bitwiseman force-pushed the task/builder-updater branch from 6ea9e4c to 9111081 Compare March 4, 2020 06:10
return description;
}

GHLabel wrapUp(GHRepository repo) {
Copy link
Member Author

@bitwiseman bitwiseman Mar 4, 2020

Choose a reason for hiding this comment

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

Replaced wrapUp() with lateBind().
Update: removed need for late bind.

* if there is an I/O Exception
*/
@Nonnull
public R done() throws IOException {
Copy link
Member Author

Choose a reason for hiding this comment

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

Any suggestions for a better method name than done()? send()? submit()?
We can't use commit() because it will almost surely collide with a git "commit" at least conceptually if not literally.

Copy link
Contributor

Choose a reason for hiding this comment

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

BulkChange in core uses commit and abort. abort is not needed. Maybe send is a good choice. or call?

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 two places this will get used look something like this:

GHLabel label = GHLabel.create().name(name).color(color).description(description).xxxx();
label.update().color(color2).description(description2).xxxx();

send() could work. call not as much. I'd just like to make sure we've considered alternatives before releasing this.

Copy link
Member Author

Choose a reason for hiding this comment

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

Okay, well, this api is internal, so changing it later is not a breaking API change. I'll go with the current verbs for now and we can change them later if needed.

Copy link
Member Author

Choose a reason for hiding this comment

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

Hm, doh, no, what was I thinking this is public. Ah, but I can use the same structure we already have for API Previews and ship this now. 😄There we go.

@bitwiseman bitwiseman force-pushed the task/builder-updater branch 3 times, most recently from 53e9da9 to 0b46ce0 Compare March 4, 2020 08:30
@bitwiseman bitwiseman force-pushed the task/builder-updater branch from 5d4da2e to 208ffac Compare March 4, 2020 21:38
@bitwiseman bitwiseman marked this pull request as ready for review March 20, 2020 18:44
@bitwiseman bitwiseman changed the title Proof of concept proposal for Builder/Creator/Updater design Add Builder/Creator/Updater for GHLabel Mar 20, 2020
@bitwiseman bitwiseman force-pushed the task/builder-updater branch from 7c1be13 to fb50ade Compare March 23, 2020 22:39
The guts of this version are a bit ugly but they result reasonable API code without a ton of extra
code needed.
This removes the  from the fields.  Functionally the behavior is unchanged but
it is no longer guaranteed at compile time.  This simplifies streamlines the code slightly,
but at the cost of only being able to assert immutability rather than know it.

However, as we move to using this structure through more of the library, this is may be a better choice.
There are so many places where the GitHub API itself returns partial records or updates them dynamically.
Trying to claim immutability where it doesn't exist is not great either.
It turns out GHLabel instances do not need a reference to their repo, just to root.
@bitwiseman bitwiseman force-pushed the task/builder-updater branch 4 times, most recently from 29019d4 to b823d68 Compare March 24, 2020 18:11
@bitwiseman bitwiseman force-pushed the task/builder-updater branch from b823d68 to 772272f Compare March 24, 2020 19:32
@jglick jglick mentioned this pull request Mar 25, 2020
4 tasks
@bitwiseman bitwiseman merged commit 7a65013 into hub4j:master Mar 25, 2020
@bitwiseman bitwiseman deleted the task/builder-updater branch March 25, 2020 18:56
jglick added a commit to jglick/github-api that referenced this pull request Mar 26, 2020
bitwiseman added a commit that referenced this pull request Mar 26, 2020
#724 broke the site goal from .github/PULL_REQUEST_TEMPLATE.md
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