Skip to content

Commit 56d6eec

Browse files
committed
feat: add GET /app endpoint to retrieve authenticated app details
1 parent ef607b0 commit 56d6eec

File tree

6 files changed

+170
-17
lines changed

6 files changed

+170
-17
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ target
1111
.idea
1212
*.iml
1313

14+
## VS Code
15+
.vscode
16+
1417
## Logs
1518
*.log
1619

@@ -29,4 +32,4 @@ pom.xml.releaseBackup
2932
release.properties
3033

3134
# macOS
32-
.DS_Store
35+
.DS_Store

src/main/java/com/spotify/github/v3/checks/App.java

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
* Licensed under the Apache License, Version 2.0 (the "License");
88
* you may not use this file except in compliance with the License.
99
* You may obtain a copy of the License at
10-
*
10+
*
1111
* http://www.apache.org/licenses/LICENSE-2.0
12-
*
12+
*
1313
* Unless required by applicable law or agreed to in writing, software
1414
* distributed under the License is distributed on an "AS IS" BASIS,
1515
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -22,6 +22,7 @@
2222

2323
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
2424
import com.spotify.github.GithubStyle;
25+
import com.spotify.github.v3.User;
2526
import java.time.ZonedDateTime;
2627
import java.util.List;
2728
import java.util.Map;
@@ -112,4 +113,53 @@ public interface App {
112113
* @return the optional count
113114
*/
114115
Optional<Integer> installationsCount();
116+
117+
/**
118+
* The client ID of the GitHub App.
119+
*
120+
* @return the optional client ID
121+
*/
122+
Optional<String> clientId();
123+
124+
/**
125+
* The name of the single file the GitHub App can access (if applicable).
126+
*
127+
* @return the optional single file name
128+
*/
129+
Optional<String> singleFileName();
130+
131+
/**
132+
* Whether the GitHub App has access to multiple single files.
133+
*
134+
* @return the optional boolean
135+
*/
136+
Optional<Boolean> hasMultipleSingleFiles();
137+
138+
/**
139+
* The list of single file paths the GitHub App can access.
140+
*
141+
* @return the optional list of file paths
142+
*/
143+
Optional<List<String>> singleFilePaths();
144+
145+
/**
146+
* The slug name of the GitHub App.
147+
*
148+
* @return the optional app slug
149+
*/
150+
Optional<String> appSlug();
151+
152+
/**
153+
* The date the App was suspended.
154+
*
155+
* @return the optional suspended date
156+
*/
157+
Optional<ZonedDateTime> suspendedAt();
158+
159+
/**
160+
* The user who suspended the App.
161+
*
162+
* @return the optional user
163+
*/
164+
Optional<User> suspendedBy();
115165
}

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,15 @@ public UserClient createUserClient(final String owner) {
588588
return UserClient.create(this, owner);
589589
}
590590

591+
/**
592+
* Create GitHub App API client
593+
*
594+
* @return GitHub App API client
595+
*/
596+
public GithubAppClient createGithubAppClient() {
597+
return new GithubAppClient(this);
598+
}
599+
591600
Json json() {
592601
return json;
593602
}
@@ -1017,7 +1026,9 @@ private CompletableFuture<String> getAuthorizationHeader(final String path) {
10171026
}
10181027

10191028
private boolean isJwtRequest(final String path) {
1020-
return path.startsWith("/app/installation") || path.endsWith("installation");
1029+
return path.equals("/app")
1030+
|| path.startsWith("/app/installation")
1031+
|| path.endsWith("installation");
10211032
}
10221033

10231034
/**

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

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
* Licensed under the Apache License, Version 2.0 (the "License");
88
* you may not use this file except in compliance with the License.
99
* You may obtain a copy of the License at
10-
*
10+
*
1111
* http://www.apache.org/licenses/LICENSE-2.0
12-
*
12+
*
1313
* Unless required by applicable law or agreed to in writing, software
1414
* distributed under the License is distributed on an "AS IS" BASIS,
1515
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -24,6 +24,7 @@
2424
import com.google.common.collect.ImmutableMap;
2525
import com.spotify.github.v3.apps.InstallationRepositoriesResponse;
2626
import com.spotify.github.v3.checks.AccessToken;
27+
import com.spotify.github.v3.checks.App;
2728
import com.spotify.github.v3.checks.Installation;
2829
import java.util.List;
2930
import java.util.Map;
@@ -34,6 +35,7 @@
3435
/** Apps API client */
3536
public class GithubAppClient {
3637

38+
private static final String GET_AUTHENTICATED_APP_URL = "/app";
3739
private static final String GET_INSTALLATION_BY_ID_URL = "/app/installations/%s";
3840
private static final String GET_ACCESS_TOKEN_URL = "/app/installations/%s/access_tokens";
3941
private static final String GET_INSTALLATIONS_URL = "/app/installations?per_page=100";
@@ -48,7 +50,7 @@ public class GithubAppClient {
4850
private static final String GET_INSTALLATION_USER_URL = "/users/%s/installation";
4951

5052
private final GitHubClient github;
51-
private final String owner;
53+
private final Optional<String> maybeOwner;
5254
private final Optional<String> maybeRepo;
5355

5456
private final Map<String, String> extraHeaders =
@@ -59,13 +61,19 @@ public class GithubAppClient {
5961

6062
GithubAppClient(final GitHubClient github, final String owner, final String repo) {
6163
this.github = github;
62-
this.owner = owner;
64+
this.maybeOwner = Optional.of(owner);
6365
this.maybeRepo = Optional.of(repo);
6466
}
6567

6668
GithubAppClient(final GitHubClient github, final String owner) {
6769
this.github = github;
68-
this.owner = owner;
70+
this.maybeOwner = Optional.of(owner);
71+
this.maybeRepo = Optional.empty();
72+
}
73+
74+
GithubAppClient(final GitHubClient github) {
75+
this.github = github;
76+
this.maybeOwner = Optional.empty();
6977
this.maybeRepo = Optional.empty();
7078
}
7179

@@ -99,29 +107,32 @@ public CompletableFuture<Installation> getInstallation(final Integer installatio
99107

100108
/**
101109
* Get an installation of a repo
110+
*
102111
* @return an Installation
103112
*/
104113
private CompletableFuture<Installation> getRepoInstallation(final String repo) {
105114
return github.request(
106-
String.format(GET_INSTALLATION_REPO_URL, owner, repo), Installation.class);
115+
String.format(GET_INSTALLATION_REPO_URL, maybeOwner.get(), repo), Installation.class);
107116
}
108117

109118
/**
110119
* Get an installation of an org
120+
*
111121
* @return an Installation
112122
*/
113123
private CompletableFuture<Installation> getOrgInstallation() {
114124
return github.request(
115-
String.format(GET_INSTALLATION_ORG_URL, owner), Installation.class);
125+
String.format(GET_INSTALLATION_ORG_URL, maybeOwner.get()), Installation.class);
116126
}
117127

118-
/**
128+
/**
119129
* Get an installation of a user
130+
*
120131
* @return an Installation
121132
*/
122133
public CompletableFuture<Installation> getUserInstallation() {
123134
return github.request(
124-
String.format(GET_INSTALLATION_USER_URL, owner), Installation.class);
135+
String.format(GET_INSTALLATION_USER_URL, maybeOwner.get()), Installation.class);
125136
}
126137

127138
/**
@@ -146,4 +157,17 @@ public CompletableFuture<InstallationRepositoriesResponse> listAccessibleReposit
146157
return GitHubClient.scopeForInstallationId(github, installationId)
147158
.request(LIST_ACCESSIBLE_REPOS_URL, InstallationRepositoriesResponse.class, extraHeaders);
148159
}
160+
161+
/**
162+
* Get the authenticated GitHub App.
163+
*
164+
* <p>Returns the authenticated app. You must use a JWT to access this endpoint.
165+
*
166+
* <p>see https://docs.github.com/en/rest/apps/apps#get-the-authenticated-app
167+
*
168+
* @return the authenticated App
169+
*/
170+
public CompletableFuture<App> getAuthenticatedApp() {
171+
return github.request(GET_AUTHENTICATED_APP_URL, App.class);
172+
}
149173
}

src/test/java/com/spotify/github/v3/clients/GithubAppClientTest.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
* Licensed under the Apache License, Version 2.0 (the "License");
88
* you may not use this file except in compliance with the License.
99
* You may obtain a copy of the License at
10-
*
10+
*
1111
* http://www.apache.org/licenses/LICENSE-2.0
12-
*
12+
*
1313
* Unless required by applicable law or agreed to in writing, software
1414
* distributed under the License is distributed on an "AS IS" BASIS,
1515
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@@ -30,6 +30,7 @@
3030
import com.google.common.io.Resources;
3131
import com.spotify.github.FixtureHelper;
3232
import com.spotify.github.v3.apps.InstallationRepositoriesResponse;
33+
import com.spotify.github.v3.checks.App;
3334
import com.spotify.github.v3.checks.Installation;
3435
import java.io.File;
3536
import java.io.IOException;
@@ -110,8 +111,7 @@ public void listAccessibleRepositories() throws Exception {
110111
.setResponseCode(200)
111112
.setBody(FixtureHelper.loadFixture("githubapp/accessible-repositories.json")));
112113

113-
InstallationRepositoriesResponse response =
114-
client.listAccessibleRepositories(1234).join();
114+
InstallationRepositoriesResponse response = client.listAccessibleRepositories(1234).join();
115115

116116
assertThat(response.totalCount(), is(2));
117117
assertThat(response.repositories().size(), is(2));
@@ -160,4 +160,26 @@ public void getInstallationByInstallationId() throws Exception {
160160
RecordedRequest recordedRequest = mockServer.takeRequest(1, TimeUnit.MILLISECONDS);
161161
assertThat(recordedRequest.getRequestUrl().encodedPath(), is("/app/installations/1234"));
162162
}
163+
164+
@Test
165+
public void getAuthenticatedApp() throws Exception {
166+
mockServer.enqueue(
167+
new MockResponse()
168+
.setResponseCode(200)
169+
.setBody(FixtureHelper.loadFixture("githubapp/authenticated-app.json")));
170+
171+
App app = client.getAuthenticatedApp().join();
172+
173+
assertThat(app.id(), is(1));
174+
assertThat(app.slug().get(), is("octoapp"));
175+
assertThat(app.name(), is("Octocat App"));
176+
assertThat(app.clientId().get(), is("Iv1.8a61f9b3a7aba766"));
177+
assertThat(app.installationsCount().get(), is(5));
178+
179+
RecordedRequest recordedRequest = mockServer.takeRequest(1, TimeUnit.MILLISECONDS);
180+
assertThat(recordedRequest.getRequestUrl().encodedPath(), is("/app"));
181+
assertThat(
182+
recordedRequest.getHeaders().values("Authorization").get(0).startsWith("Bearer "),
183+
is(true));
184+
}
163185
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
{
2+
"id": 1,
3+
"slug": "octoapp",
4+
"node_id": "MDExOkludGVncmF0aW9uMQ==",
5+
"owner": {
6+
"login": "github",
7+
"id": 1,
8+
"node_id": "MDEyOk9yZ2FuaXphdGlvbjE=",
9+
"url": "https://api.github.com/orgs/github",
10+
"repos_url": "https://api.github.com/orgs/github/repos",
11+
"events_url": "https://api.github.com/orgs/github/events",
12+
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
13+
"gravatar_id": "",
14+
"html_url": "https://github.com/github",
15+
"followers_url": "https://api.github.com/users/github/followers",
16+
"following_url": "https://api.github.com/users/github/following{/other_user}",
17+
"gists_url": "https://api.github.com/users/github/gists{/gist_id}",
18+
"starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}",
19+
"subscriptions_url": "https://api.github.com/users/github/subscriptions",
20+
"organizations_url": "https://api.github.com/users/github/orgs",
21+
"received_events_url": "https://api.github.com/users/github/received_events",
22+
"type": "User",
23+
"site_admin": true
24+
},
25+
"name": "Octocat App",
26+
"description": "Test GitHub App",
27+
"external_url": "https://example.com",
28+
"html_url": "https://github.com/apps/octoapp",
29+
"created_at": "2017-07-08T16:18:44-04:00",
30+
"updated_at": "2017-07-08T16:18:44-04:00",
31+
"permissions": {
32+
"metadata": "read",
33+
"contents": "read",
34+
"issues": "write",
35+
"single_file": "write"
36+
},
37+
"events": ["push", "pull_request"],
38+
"installations_count": 5,
39+
"client_id": "Iv1.8a61f9b3a7aba766",
40+
"client_secret": "1726be1638095a19edd134c77bde3aa2ece1e5d8",
41+
"webhook_secret": "e340154128314309424b7c8e90325147d99fdafa",
42+
"pem": "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAuEPzOUE+kiEH1WLiMeBytTEF856j0hgg...\n-----END RSA PRIVATE KEY-----"
43+
}

0 commit comments

Comments
 (0)