|
25 | 25 |
|
26 | 26 | import com.fasterxml.jackson.core.type.TypeReference;
|
27 | 27 | import com.spotify.github.Tracer;
|
| 28 | +import com.spotify.github.async.Async; |
28 | 29 | import com.spotify.github.jackson.Json;
|
29 | 30 | import com.spotify.github.v3.Team;
|
30 | 31 | import com.spotify.github.v3.User;
|
31 | 32 | import com.spotify.github.v3.checks.AccessToken;
|
| 33 | +import com.spotify.github.v3.checks.Installation; |
32 | 34 | import com.spotify.github.v3.comment.Comment;
|
33 | 35 | import com.spotify.github.v3.exceptions.ReadOnlyRepositoryException;
|
34 | 36 | import com.spotify.github.v3.exceptions.RequestNotOkException;
|
|
53 | 55 | import java.util.Objects;
|
54 | 56 | import java.util.Optional;
|
55 | 57 | import java.util.concurrent.CompletableFuture;
|
| 58 | +import java.util.concurrent.CompletionStage; |
56 | 59 | import java.util.concurrent.ConcurrentHashMap;
|
57 | 60 | import java.util.concurrent.atomic.AtomicBoolean;
|
58 | 61 | import java.util.function.Consumer;
|
|
71 | 74 | public class GitHubClient {
|
72 | 75 |
|
73 | 76 | private static final int EXPIRY_MARGIN_IN_MINUTES = 5;
|
| 77 | + private static final int HTTP_NOT_FOUND = 404; |
74 | 78 |
|
75 | 79 | private Tracer tracer = NoopTracer.INSTANCE;
|
76 | 80 |
|
@@ -367,6 +371,37 @@ public GitHubClient withScopeForInstallationId(final int installationId) {
|
367 | 371 | installationId);
|
368 | 372 | }
|
369 | 373 |
|
| 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 | + |
370 | 405 | public GitHubClient withTracer(final Tracer tracer) {
|
371 | 406 | this.tracer = tracer;
|
372 | 407 | return this;
|
|
0 commit comments