Skip to content

Commit 178662d

Browse files
Add asAppScopedClient() function for convenience
1 parent 7cf19e5 commit 178662d

File tree

2 files changed

+52
-0
lines changed

2 files changed

+52
-0
lines changed

src/main/java/com/spotify/github/async/Async.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
package com.spotify.github.async;
2222

23+
import java.util.concurrent.CompletableFuture;
24+
import java.util.function.Function;
2325
import java.util.stream.Stream;
2426

2527
import static java.util.stream.StreamSupport.stream;
@@ -34,4 +36,19 @@ public static <T> Stream<T> streamFromPaginatingIterable(final Iterable<AsyncPag
3436
return stream(iterable.spliterator(), false)
3537
.flatMap(page -> stream(page.spliterator(), false));
3638
}
39+
40+
public static <T> CompletableFuture<T> exceptionallyCompose(
41+
final CompletableFuture<T> future, final Function<Throwable, CompletableFuture<T>> handler) {
42+
43+
return future
44+
.handle(
45+
(result, throwable) -> {
46+
if (throwable != null) {
47+
return handler.apply(throwable);
48+
} else {
49+
return CompletableFuture.completedFuture(result);
50+
}
51+
})
52+
.thenCompose(Function.identity());
53+
}
3754
}

src/main/java/com/spotify/github/v3/clients/GitHubClient.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@
2525

2626
import com.fasterxml.jackson.core.type.TypeReference;
2727
import com.spotify.github.Tracer;
28+
import com.spotify.github.async.Async;
2829
import com.spotify.github.jackson.Json;
2930
import com.spotify.github.v3.Team;
3031
import com.spotify.github.v3.User;
3132
import com.spotify.github.v3.checks.AccessToken;
33+
import com.spotify.github.v3.checks.Installation;
3234
import com.spotify.github.v3.comment.Comment;
3335
import com.spotify.github.v3.exceptions.ReadOnlyRepositoryException;
3436
import com.spotify.github.v3.exceptions.RequestNotOkException;
@@ -53,6 +55,7 @@
5355
import java.util.Objects;
5456
import java.util.Optional;
5557
import java.util.concurrent.CompletableFuture;
58+
import java.util.concurrent.CompletionStage;
5659
import java.util.concurrent.ConcurrentHashMap;
5760
import java.util.concurrent.atomic.AtomicBoolean;
5861
import java.util.function.Consumer;
@@ -71,6 +74,7 @@
7174
public class GitHubClient {
7275

7376
private static final int EXPIRY_MARGIN_IN_MINUTES = 5;
77+
private static final int HTTP_NOT_FOUND = 404;
7478

7579
private Tracer tracer = NoopTracer.INSTANCE;
7680

@@ -367,6 +371,37 @@ public GitHubClient withScopeForInstallationId(final int installationId) {
367371
installationId);
368372
}
369373

374+
/**
375+
* This is for clients authenticated as a GitHub App: when performing operations,
376+
* the "installation" of the App must be specified.
377+
* This returns a {@code GitHubClient} that has been scoped to the
378+
* user's/organization's installation of the app, if any.
379+
*/
380+
public CompletionStage<Optional<GitHubClient>> asAppScopedClient(final String owner) {
381+
return Async.exceptionallyCompose(this
382+
.createOrganisationClient(owner)
383+
.createGithubAppClient()
384+
.getInstallation()
385+
.thenApply(Installation::id), e -> {
386+
if (e.getCause() instanceof RequestNotOkException && ((RequestNotOkException) e).statusCode() == HTTP_NOT_FOUND) {
387+
return this
388+
.createUserClient(owner)
389+
.createGithubAppClient()
390+
.getUserInstallation()
391+
.thenApply(Installation::id);
392+
}
393+
return CompletableFuture.failedFuture(e);
394+
})
395+
.thenApply(id -> Optional.of(this.withScopeForInstallationId(id)))
396+
.exceptionally(
397+
e -> {
398+
if (e.getCause() instanceof RequestNotOkException && ((RequestNotOkException) e).statusCode() == HTTP_NOT_FOUND) {
399+
return Optional.empty();
400+
}
401+
throw new RuntimeException(e);
402+
});
403+
}
404+
370405
public GitHubClient withTracer(final Tracer tracer) {
371406
this.tracer = tracer;
372407
return this;

0 commit comments

Comments
 (0)