Skip to content

Commit e0aee9f

Browse files
committed
Expose MappingReader and MappingWriter
Jenkins Blue Ocean made interesting design choices relating github-api interactions. They mostly reused the existing API and OM, but in a few places they chose to implement their own object mapping independent of this project. This is fine as long as nothing in this project ever changes, including internals such as ObjectMapper configuration or behavior. Recent release have made changes to those internals which break assumptions made in Blue Ocean. This change exposes this project's MappingReader and MappingWriter to allow for a fix to Blue Ocean requiring only minimal changes. This doesn't prevent future changes from breaking Blue Ocean but at least makes them much less likely. Fixes #780
1 parent 76c5192 commit e0aee9f

File tree

10 files changed

+415
-4
lines changed

10 files changed

+415
-4
lines changed

src/main/java/org/kohsuke/github/GHObject.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public abstract class GHObject {
2424
/**
2525
* Capture response HTTP headers on the state object.
2626
*/
27-
protected Map<String, List<String>> responseHeaderFields;
27+
protected transient Map<String, List<String>> responseHeaderFields;
2828

2929
protected String url;
3030
protected long id;

src/main/java/org/kohsuke/github/GHRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
@SuppressFBWarnings(value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD" },
6464
justification = "JSON API")
6565
public class GHRepository extends GHObject {
66-
/* package almost final */ GitHub root;
66+
/* package almost final */ transient GitHub root;
6767

6868
private String nodeId, description, homepage, name, full_name;
6969
private String html_url; // this is the UI

src/main/java/org/kohsuke/github/GitHub.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
*/
2424
package org.kohsuke.github;
2525

26+
import com.fasterxml.jackson.databind.ObjectReader;
27+
import com.fasterxml.jackson.databind.ObjectWriter;
2628
import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
2729

2830
import java.io.*;
@@ -1190,6 +1192,32 @@ public Reader renderMarkdown(String text) throws IOException {
11901192
"UTF-8");
11911193
}
11921194

1195+
/**
1196+
* Do not use this method. This method will be removed and should never have been needed in the first place.
1197+
*
1198+
* @return an {@link ObjectWriter} instance that can be further configured.
1199+
* @deprecated DO NOT USE THIS METHOD. Provided for backward compatibility with projects that did their own jackson
1200+
* mapping of this project's data objects, such as Jenkins Blue Ocean.
1201+
*/
1202+
@Deprecated
1203+
@Nonnull
1204+
public static ObjectWriter getMappingObjectWriter() {
1205+
return GitHubClient.getMappingObjectWriter();
1206+
}
1207+
1208+
/**
1209+
* Do not use this method. This method will be removed and should never have been needed in the first place.
1210+
*
1211+
* @return an {@link ObjectReader} instance that can be further configured.
1212+
* @deprecated DO NOT USE THIS METHOD. Provided for backward compatibility with projects that did their own jackson
1213+
* mapping of this project's data objects, such as Jenkins Blue Ocean.
1214+
*/
1215+
@Deprecated
1216+
@Nonnull
1217+
public static ObjectReader getMappingObjectReader() {
1218+
return GitHubClient.getMappingObjectReader(GitHub.offline());
1219+
}
1220+
11931221
@Nonnull
11941222
GitHubClient getClient() {
11951223
return client;

src/test/java/org/kohsuke/github/GitHubStaticTest.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
import java.util.Date;
99
import java.util.TimeZone;
1010

11-
import static org.hamcrest.CoreMatchers.equalTo;
12-
import static org.hamcrest.CoreMatchers.not;
11+
import static org.hamcrest.CoreMatchers.*;
1312
import static org.hamcrest.core.Is.is;
1413

1514
/**
@@ -131,6 +130,32 @@ public void testGitHubRateLimitShouldReplaceRateLimit() throws Exception {
131130

132131
}
133132

133+
@Test
134+
public void testMappingReaderWriter() throws Exception {
135+
136+
// This test ensures that data objects can be written and read in a raw form from string.
137+
// This behavior is completely unsupported and should not be used but given that some
138+
// clients, such as Jenkins Blue Ocean, have already implemented their own Jackson
139+
// Reader and Writer that bind this library's data objects from outside this library
140+
// this makes sure they don't break.
141+
142+
GHRepository repo = getTempRepository();
143+
assertThat(repo.root, not(nullValue()));
144+
145+
String repoString = GitHub.getMappingObjectWriter().writeValueAsString(repo);
146+
assertThat(repoString, not(nullValue()));
147+
assertThat(repoString, containsString("testMappingReaderWriter"));
148+
149+
GHRepository readRepo = GitHub.getMappingObjectReader().forType(GHRepository.class).readValue(repoString);
150+
151+
// This should never happen if these methods aren't used
152+
assertThat(readRepo.root, nullValue());
153+
154+
String readRepoString = GitHub.getMappingObjectWriter().writeValueAsString(readRepo);
155+
assertThat(readRepoString, equalTo(repoString));
156+
157+
}
158+
134159
static String formatDate(Date dt, String format) {
135160
SimpleDateFormat df = new SimpleDateFormat(format);
136161
df.setTimeZone(TimeZone.getTimeZone("GMT"));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
{
2+
"id": 256061594,
3+
"node_id": "MDEwOlJlcG9zaXRvcnkyNTYwNjE1OTQ=",
4+
"name": "temp-testMappingReaderWriter",
5+
"full_name": "github-api-test-org/temp-testMappingReaderWriter",
6+
"private": false,
7+
"owner": {
8+
"login": "github-api-test-org",
9+
"id": 7544739,
10+
"node_id": "MDEyOk9yZ2FuaXphdGlvbjc1NDQ3Mzk=",
11+
"avatar_url": "https://avatars3.githubusercontent.com/u/7544739?v=4",
12+
"gravatar_id": "",
13+
"url": "https://api.github.com/users/github-api-test-org",
14+
"html_url": "https://github.com/github-api-test-org",
15+
"followers_url": "https://api.github.com/users/github-api-test-org/followers",
16+
"following_url": "https://api.github.com/users/github-api-test-org/following{/other_user}",
17+
"gists_url": "https://api.github.com/users/github-api-test-org/gists{/gist_id}",
18+
"starred_url": "https://api.github.com/users/github-api-test-org/starred{/owner}{/repo}",
19+
"subscriptions_url": "https://api.github.com/users/github-api-test-org/subscriptions",
20+
"organizations_url": "https://api.github.com/users/github-api-test-org/orgs",
21+
"repos_url": "https://api.github.com/users/github-api-test-org/repos",
22+
"events_url": "https://api.github.com/users/github-api-test-org/events{/privacy}",
23+
"received_events_url": "https://api.github.com/users/github-api-test-org/received_events",
24+
"type": "Organization",
25+
"site_admin": false
26+
},
27+
"html_url": "https://github.com/github-api-test-org/temp-testMappingReaderWriter",
28+
"description": "A test repository for testing the github-api project: temp-testMappingReaderWriter",
29+
"fork": false,
30+
"url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter",
31+
"forks_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/forks",
32+
"keys_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/keys{/key_id}",
33+
"collaborators_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/collaborators{/collaborator}",
34+
"teams_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/teams",
35+
"hooks_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/hooks",
36+
"issue_events_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/issues/events{/number}",
37+
"events_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/events",
38+
"assignees_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/assignees{/user}",
39+
"branches_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/branches{/branch}",
40+
"tags_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/tags",
41+
"blobs_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/git/blobs{/sha}",
42+
"git_tags_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/git/tags{/sha}",
43+
"git_refs_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/git/refs{/sha}",
44+
"trees_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/git/trees{/sha}",
45+
"statuses_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/statuses/{sha}",
46+
"languages_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/languages",
47+
"stargazers_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/stargazers",
48+
"contributors_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/contributors",
49+
"subscribers_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/subscribers",
50+
"subscription_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/subscription",
51+
"commits_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/commits{/sha}",
52+
"git_commits_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/git/commits{/sha}",
53+
"comments_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/comments{/number}",
54+
"issue_comment_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/issues/comments{/number}",
55+
"contents_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/contents/{+path}",
56+
"compare_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/compare/{base}...{head}",
57+
"merges_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/merges",
58+
"archive_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/{archive_format}{/ref}",
59+
"downloads_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/downloads",
60+
"issues_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/issues{/number}",
61+
"pulls_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/pulls{/number}",
62+
"milestones_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/milestones{/number}",
63+
"notifications_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/notifications{?since,all,participating}",
64+
"labels_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/labels{/name}",
65+
"releases_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/releases{/id}",
66+
"deployments_url": "https://api.github.com/repos/github-api-test-org/temp-testMappingReaderWriter/deployments",
67+
"created_at": "2020-04-15T23:38:53Z",
68+
"updated_at": "2020-04-15T23:38:58Z",
69+
"pushed_at": "2020-04-15T23:38:55Z",
70+
"git_url": "git://github.com/github-api-test-org/temp-testMappingReaderWriter.git",
71+
"ssh_url": "[email protected]:github-api-test-org/temp-testMappingReaderWriter.git",
72+
"clone_url": "https://github.com/github-api-test-org/temp-testMappingReaderWriter.git",
73+
"svn_url": "https://github.com/github-api-test-org/temp-testMappingReaderWriter",
74+
"homepage": "http://github-api.kohsuke.org/",
75+
"size": 0,
76+
"stargazers_count": 0,
77+
"watchers_count": 0,
78+
"language": null,
79+
"has_issues": true,
80+
"has_projects": true,
81+
"has_downloads": true,
82+
"has_wiki": true,
83+
"has_pages": false,
84+
"forks_count": 0,
85+
"mirror_url": null,
86+
"archived": false,
87+
"disabled": false,
88+
"open_issues_count": 0,
89+
"license": null,
90+
"forks": 0,
91+
"open_issues": 0,
92+
"watchers": 0,
93+
"default_branch": "master",
94+
"permissions": {
95+
"admin": true,
96+
"push": true,
97+
"pull": true
98+
},
99+
"temp_clone_token": "",
100+
"allow_squash_merge": true,
101+
"allow_merge_commit": true,
102+
"allow_rebase_merge": true,
103+
"delete_branch_on_merge": false,
104+
"organization": {
105+
"login": "github-api-test-org",
106+
"id": 7544739,
107+
"node_id": "MDEyOk9yZ2FuaXphdGlvbjc1NDQ3Mzk=",
108+
"avatar_url": "https://avatars3.githubusercontent.com/u/7544739?v=4",
109+
"gravatar_id": "",
110+
"url": "https://api.github.com/users/github-api-test-org",
111+
"html_url": "https://github.com/github-api-test-org",
112+
"followers_url": "https://api.github.com/users/github-api-test-org/followers",
113+
"following_url": "https://api.github.com/users/github-api-test-org/following{/other_user}",
114+
"gists_url": "https://api.github.com/users/github-api-test-org/gists{/gist_id}",
115+
"starred_url": "https://api.github.com/users/github-api-test-org/starred{/owner}{/repo}",
116+
"subscriptions_url": "https://api.github.com/users/github-api-test-org/subscriptions",
117+
"organizations_url": "https://api.github.com/users/github-api-test-org/orgs",
118+
"repos_url": "https://api.github.com/users/github-api-test-org/repos",
119+
"events_url": "https://api.github.com/users/github-api-test-org/events{/privacy}",
120+
"received_events_url": "https://api.github.com/users/github-api-test-org/received_events",
121+
"type": "Organization",
122+
"site_admin": false
123+
},
124+
"network_count": 0,
125+
"subscribers_count": 7
126+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"login": "bitwiseman",
3+
"id": 1958953,
4+
"node_id": "MDQ6VXNlcjE5NTg5NTM=",
5+
"avatar_url": "https://avatars3.githubusercontent.com/u/1958953?v=4",
6+
"gravatar_id": "",
7+
"url": "https://api.github.com/users/bitwiseman",
8+
"html_url": "https://github.com/bitwiseman",
9+
"followers_url": "https://api.github.com/users/bitwiseman/followers",
10+
"following_url": "https://api.github.com/users/bitwiseman/following{/other_user}",
11+
"gists_url": "https://api.github.com/users/bitwiseman/gists{/gist_id}",
12+
"starred_url": "https://api.github.com/users/bitwiseman/starred{/owner}{/repo}",
13+
"subscriptions_url": "https://api.github.com/users/bitwiseman/subscriptions",
14+
"organizations_url": "https://api.github.com/users/bitwiseman/orgs",
15+
"repos_url": "https://api.github.com/users/bitwiseman/repos",
16+
"events_url": "https://api.github.com/users/bitwiseman/events{/privacy}",
17+
"received_events_url": "https://api.github.com/users/bitwiseman/received_events",
18+
"type": "User",
19+
"site_admin": false,
20+
"name": "Liam Newman",
21+
"company": "Cloudbees, Inc.",
22+
"blog": "",
23+
"location": "Seattle, WA, USA",
24+
"email": "[email protected]",
25+
"hireable": null,
26+
"bio": "https://twitter.com/bitwiseman",
27+
"public_repos": 181,
28+
"public_gists": 7,
29+
"followers": 156,
30+
"following": 9,
31+
"created_at": "2012-07-11T20:38:33Z",
32+
"updated_at": "2020-04-14T20:00:03Z",
33+
"private_gists": 8,
34+
"total_private_repos": 10,
35+
"owned_private_repos": 0,
36+
"disk_usage": 33697,
37+
"collaborators": 0,
38+
"two_factor_authentication": true,
39+
"plan": {
40+
"name": "free",
41+
"space": 976562499,
42+
"collaborators": 0,
43+
"private_repos": 10000
44+
}
45+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"id": "de382884-bcbf-4c2e-9ae0-176f926dcb0d",
3+
"name": "repos_github-api-test-org_temp-testmappingreaderwriter",
4+
"request": {
5+
"url": "/repos/github-api-test-org/temp-testMappingReaderWriter",
6+
"method": "GET",
7+
"headers": {
8+
"Accept": {
9+
"equalTo": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"
10+
}
11+
}
12+
},
13+
"response": {
14+
"status": 200,
15+
"bodyFileName": "repos_github-api-test-org_temp-testmappingreaderwriter-2.json",
16+
"headers": {
17+
"Date": "Wed, 15 Apr 2020 23:38:59 GMT",
18+
"Content-Type": "application/json; charset=utf-8",
19+
"Server": "GitHub.com",
20+
"Status": "200 OK",
21+
"X-RateLimit-Limit": "5000",
22+
"X-RateLimit-Remaining": "4948",
23+
"X-RateLimit-Reset": "1586996337",
24+
"Cache-Control": "private, max-age=60, s-maxage=60",
25+
"Vary": [
26+
"Accept, Authorization, Cookie, X-GitHub-OTP",
27+
"Accept-Encoding, Accept, X-Requested-With"
28+
],
29+
"ETag": "W/\"f24612ba790ffe0f1925c048a05e0f76\"",
30+
"Last-Modified": "Wed, 15 Apr 2020 23:38:58 GMT",
31+
"X-OAuth-Scopes": "admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user, write:discussion",
32+
"X-Accepted-OAuth-Scopes": "repo",
33+
"X-GitHub-Media-Type": "unknown, github.v3",
34+
"Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
35+
"X-Frame-Options": "deny",
36+
"X-Content-Type-Options": "nosniff",
37+
"X-XSS-Protection": "1; mode=block",
38+
"Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
39+
"Content-Security-Policy": "default-src 'none'",
40+
"X-GitHub-Request-Id": "F3AE:08E3:30A82:3B66F:5E979B0C"
41+
}
42+
},
43+
"uuid": "de382884-bcbf-4c2e-9ae0-176f926dcb0d",
44+
"persistent": true,
45+
"insertionIndex": 2
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"id": "ab96e191-face-4ca5-a7a2-73b552c74559",
3+
"name": "repos_github-api-test-org_temp-testmappingreaderwriter_hooks",
4+
"request": {
5+
"url": "/repos/github-api-test-org/temp-testMappingReaderWriter/hooks",
6+
"method": "GET",
7+
"headers": {
8+
"Accept": {
9+
"equalTo": "text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2"
10+
}
11+
}
12+
},
13+
"response": {
14+
"status": 200,
15+
"body": "[]",
16+
"headers": {
17+
"Date": "Wed, 15 Apr 2020 23:38:59 GMT",
18+
"Content-Type": "application/json; charset=utf-8",
19+
"Server": "GitHub.com",
20+
"Status": "200 OK",
21+
"X-RateLimit-Limit": "5000",
22+
"X-RateLimit-Remaining": "4947",
23+
"X-RateLimit-Reset": "1586996337",
24+
"Cache-Control": "private, max-age=60, s-maxage=60",
25+
"Vary": [
26+
"Accept, Authorization, Cookie, X-GitHub-OTP",
27+
"Accept-Encoding, Accept, X-Requested-With"
28+
],
29+
"ETag": "\"c2ec59aeeea67fff8edf681155a22565\"",
30+
"X-OAuth-Scopes": "admin:org, admin:org_hook, admin:public_key, admin:repo_hook, delete_repo, gist, notifications, repo, user, write:discussion",
31+
"X-Accepted-OAuth-Scopes": "admin:repo_hook, public_repo, read:repo_hook, repo, write:repo_hook",
32+
"X-GitHub-Media-Type": "unknown, github.v3",
33+
"Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
34+
"X-Frame-Options": "deny",
35+
"X-Content-Type-Options": "nosniff",
36+
"X-XSS-Protection": "1; mode=block",
37+
"Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
38+
"Content-Security-Policy": "default-src 'none'",
39+
"X-GitHub-Request-Id": "F3AE:08E3:30A85:3B709:5E979B13"
40+
}
41+
},
42+
"uuid": "ab96e191-face-4ca5-a7a2-73b552c74559",
43+
"persistent": true,
44+
"scenarioName": "scenario-1-repos-github-api-test-org-temp-testMappingReaderWriter-hooks",
45+
"requiredScenarioState": "Started",
46+
"newScenarioState": "scenario-1-repos-github-api-test-org-temp-testMappingReaderWriter-hooks-2",
47+
"insertionIndex": 3
48+
}

0 commit comments

Comments
 (0)