Skip to content

Commit 5910988

Browse files
authored
Merge pull request #625 from bitwiseman/bug/content-encode
Fixed GHContent to allow spaces in path
2 parents 7d4f194 + 8c4b1ef commit 5910988

File tree

59 files changed

+1375
-1074
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1375
-1074
lines changed

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ public GHContentUpdateResponse update(byte[] newContentBytes, String commitMessa
316316
requester.with("branch", branch);
317317
}
318318

319-
GHContentUpdateResponse response = requester.to(getApiRoute(), GHContentUpdateResponse.class);
319+
GHContentUpdateResponse response = requester.to(getApiRoute(repository, path), GHContentUpdateResponse.class);
320320

321321
response.getContent().wrap(repository);
322322
response.getCommit().wrapUp(repository);
@@ -359,14 +359,14 @@ public GHContentUpdateResponse delete(String commitMessage, String branch) throw
359359
requester.with("branch", branch);
360360
}
361361

362-
GHContentUpdateResponse response = requester.to(getApiRoute(), GHContentUpdateResponse.class);
362+
GHContentUpdateResponse response = requester.to(getApiRoute(repository, path), GHContentUpdateResponse.class);
363363

364364
response.getCommit().wrapUp(repository);
365365
return response;
366366
}
367367

368-
private String getApiRoute() {
369-
return "/repos/" + repository.getOwnerName() + "/" + repository.getName() + "/contents/" + path;
368+
static String getApiRoute(GHRepository repository, String path) {
369+
return repository.getApiTailUrl("contents/" + path);
370370
}
371371

372372
GHContent wrap(GHRepository owner) {

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,7 @@ public GHContentBuilder message(String commitMessage) {
108108
* the io exception
109109
*/
110110
public GHContentUpdateResponse commit() throws IOException {
111-
GHContentUpdateResponse response = req.to(repo.getApiTailUrl("contents/" + path),
112-
GHContentUpdateResponse.class);
111+
GHContentUpdateResponse response = req.to(GHContent.getApiRoute(repo, path), GHContentUpdateResponse.class);
113112

114113
response.getContent().wrap(repo);
115114
response.getCommit().wrapUp(repo);

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

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@
3434
import java.io.InputStreamReader;
3535
import java.io.InterruptedIOException;
3636
import java.io.Reader;
37-
import java.io.UnsupportedEncodingException;
3837
import java.net.URL;
39-
import java.net.URLEncoder;
4038
import java.util.AbstractSet;
4139
import java.util.ArrayList;
4240
import java.util.Arrays;
@@ -51,8 +49,6 @@
5149
import java.util.Set;
5250
import java.util.TreeMap;
5351
import java.util.WeakHashMap;
54-
import java.util.logging.Level;
55-
import java.util.logging.Logger;
5652

5753
import static java.util.Arrays.*;
5854
import static org.kohsuke.github.Previews.*;
@@ -1460,14 +1456,7 @@ public PagedIterable<GHRef> listRefs(String refType) throws IOException {
14601456
* on failure communicating with GitHub, potentially due to an invalid ref type being requested
14611457
*/
14621458
public GHRef getRef(String refName) throws IOException {
1463-
// hashes in branch names must be replaced with the url encoded equivalent or this call will fail
1464-
// FIXME: how about other URL unsafe characters, like space, @, : etc? do we need to be using
1465-
// URLEncoder.encode()?
1466-
// OTOH, '/' need no escaping
1467-
refName = refName.replaceAll("#", "%23");
1468-
return root.retrieve()
1469-
.to(String.format("/repos/%s/%s/git/refs/%s", getOwnerName(), name, refName), GHRef.class)
1470-
.wrap(root);
1459+
return root.retrieve().to(getApiTailUrl(String.format("git/refs/%s", refName)), GHRef.class).wrap(root);
14711460
}
14721461

14731462
/**
@@ -2031,25 +2020,6 @@ public Map<String, GHBranch> getBranches() throws IOException {
20312020
return r;
20322021
}
20332022

2034-
/**
2035-
* Replace special characters (e.g. #) with standard values (e.g. %23) so GitHub understands what is being
2036-
* requested.
2037-
*
2038-
* @param value
2039-
* string to be encoded.
2040-
* @return The encoded string.
2041-
*/
2042-
private String UrlEncode(String value) {
2043-
try {
2044-
return URLEncoder.encode(value, org.apache.commons.codec.CharEncoding.UTF_8);
2045-
} catch (UnsupportedEncodingException ex) {
2046-
Logger.getLogger(GHRepository.class.getName()).log(Level.SEVERE, null, ex);
2047-
}
2048-
2049-
// Something went wrong - just return original value as is.
2050-
return value;
2051-
}
2052-
20532023
/**
20542024
* Gets branch.
20552025
*
@@ -2060,7 +2030,7 @@ private String UrlEncode(String value) {
20602030
* the io exception
20612031
*/
20622032
public GHBranch getBranch(String name) throws IOException {
2063-
return root.retrieve().to(getApiTailUrl("branches/" + UrlEncode(name)), GHBranch.class).wrap(this);
2033+
return root.retrieve().to(getApiTailUrl("branches/" + name), GHBranch.class).wrap(this);
20642034
}
20652035

20662036
/**
@@ -2576,7 +2546,7 @@ public boolean equals(Object obj) {
25762546
String getApiTailUrl(String tail) {
25772547
if (tail.length() > 0 && !tail.startsWith("/"))
25782548
tail = '/' + tail;
2579-
return "/repos/" + getOwnerName() + "/" + name + tail;
2549+
return Requester.urlPathEncode("/repos/" + getOwnerName() + "/" + name + tail);
25802550
}
25812551

25822552
/**

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
import java.net.MalformedURLException;
4141
import java.net.ProtocolException;
4242
import java.net.SocketTimeoutException;
43+
import java.net.URI;
44+
import java.net.URISyntaxException;
4345
import java.net.URL;
4446
import java.net.URLEncoder;
4547
import java.util.ArrayList;
@@ -942,6 +944,21 @@ static String transformEnum(Enum en) {
942944
return en.toString().toLowerCase(Locale.ENGLISH).replace('_', '-');
943945
}
944946

947+
/**
948+
* Encode the path to url safe string.
949+
*
950+
* @param value
951+
* string to be path encoded.
952+
* @return The encoded string.
953+
*/
954+
public static String urlPathEncode(String value) {
955+
try {
956+
return new URI(null, null, value, null, null).toString();
957+
} catch (URISyntaxException ex) {
958+
throw new AssertionError(ex);
959+
}
960+
}
961+
945962
private static final List<String> METHODS_WITHOUT_BODY = asList("GET", "DELETE");
946963
private static final Logger LOGGER = Logger.getLogger(Requester.class.getName());
947964
}

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

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,18 @@
99
import java.io.InputStreamReader;
1010
import java.util.List;
1111

12+
import static org.hamcrest.CoreMatchers.*;
13+
1214
/**
1315
* Integration test for {@link GHContent}.
1416
*/
1517
public class GHContentIntegrationTest extends AbstractGitHubWireMockTest {
1618

1719
private GHRepository repo;
18-
private String createdFilename = "test-file-to-create.txt";
20+
21+
// file name with spaces and other chars
22+
private final String createdDirectory = "test+directory #50";
23+
private final String createdFilename = createdDirectory + "/test file-to+create-#1.txt";
1924

2025
@Before
2126
@After
@@ -79,8 +84,23 @@ public void testCRUDContent() throws Exception {
7984
assertNotNull(created.getCommit());
8085
assertNotNull(created.getContent());
8186
assertNotNull(createdContent.getContent());
87+
assertThat(createdContent.getPath(), equalTo(createdFilename));
8288
assertEquals("this is an awesome file I created\n", createdContent.getContent());
8389

90+
GHContent content = repo.getFileContent(createdFilename);
91+
assertThat(content, is(notNullValue()));
92+
assertThat(content.getSha(), equalTo(createdContent.getSha()));
93+
assertThat(content.getContent(), equalTo(createdContent.getContent()));
94+
assertThat(content.getPath(), equalTo(createdContent.getPath()));
95+
96+
List<GHContent> directoryContents = repo.getDirectoryContent(createdDirectory);
97+
assertThat(directoryContents, is(notNullValue()));
98+
assertThat(directoryContents.size(), equalTo(1));
99+
content = directoryContents.get(0);
100+
assertThat(content.getSha(), is(created.getContent().getSha()));
101+
assertThat(content.getContent(), is(created.getContent().getContent()));
102+
assertThat(content.getPath(), equalTo(createdFilename));
103+
84104
GHContentUpdateResponse updatedContentResponse = createdContent.update("this is some new content\n",
85105
"Updated file for integration tests.");
86106
GHContent updatedContent = updatedContentResponse.getContent();
@@ -96,5 +116,13 @@ public void testCRUDContent() throws Exception {
96116

97117
assertNotNull(deleteResponse.getCommit());
98118
assertNull(deleteResponse.getContent());
119+
120+
try {
121+
repo.getFileContent(createdFilename);
122+
fail("Delete didn't work!");
123+
} catch (GHFileNotFoundException e) {
124+
assertThat(e.getMessage(),
125+
equalTo("{\"message\":\"Not Found\",\"documentation_url\":\"https://developer.github.com/v3/repos/contents/#get-contents\"}"));
126+
}
99127
}
100128
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public void getBranchNonExistentBut200Status() throws Exception {
6565
assertThat(e.getMessage(),
6666
equalTo("Server returned HTTP response code: 200, message: 'OK' for URL: "
6767
+ mockGitHub.apiServer().baseUrl()
68-
+ "/repos/github-api-test-org/github-api/branches/test%2FNonExistent"));
68+
+ "/repos/github-api-test-org/github-api/branches/test/NonExistent"));
6969
}
7070
}
7171

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,14 +65,14 @@
6565
"releases_url": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/releases{/id}",
6666
"deployments_url": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/deployments",
6767
"created_at": "2015-08-15T14:14:57Z",
68-
"updated_at": "2019-10-11T22:17:07Z",
69-
"pushed_at": "2019-10-11T22:17:06Z",
68+
"updated_at": "2019-11-26T01:09:15Z",
69+
"pushed_at": "2019-11-26T01:09:14Z",
7070
"git_url": "git://github.com/github-api-test-org/GHContentIntegrationTest.git",
7171
"ssh_url": "[email protected]:github-api-test-org/GHContentIntegrationTest.git",
7272
"clone_url": "https://github.com/github-api-test-org/GHContentIntegrationTest.git",
7373
"svn_url": "https://github.com/github-api-test-org/GHContentIntegrationTest",
7474
"homepage": null,
75-
"size": 38,
75+
"size": 45,
7676
"stargazers_count": 1,
7777
"watchers_count": 1,
7878
"language": null,
@@ -81,13 +81,13 @@
8181
"has_downloads": true,
8282
"has_wiki": true,
8383
"has_pages": false,
84-
"forks_count": 40,
84+
"forks_count": 41,
8585
"mirror_url": null,
8686
"archived": false,
8787
"disabled": false,
8888
"open_issues_count": 0,
8989
"license": null,
90-
"forks": 40,
90+
"forks": 41,
9191
"open_issues": 0,
9292
"watchers": 1,
9393
"default_branch": "master",
@@ -296,17 +296,17 @@
296296
"has_downloads": true,
297297
"has_wiki": true,
298298
"has_pages": false,
299-
"forks_count": 59,
299+
"forks_count": 60,
300300
"mirror_url": null,
301301
"archived": false,
302302
"disabled": false,
303303
"open_issues_count": 0,
304304
"license": null,
305-
"forks": 59,
305+
"forks": 60,
306306
"open_issues": 0,
307307
"watchers": 0,
308308
"default_branch": "master"
309309
},
310-
"network_count": 59,
310+
"network_count": 60,
311311
"subscribers_count": 1
312312
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[
2+
{
3+
"name": "test file-to+create-#1.txt",
4+
"path": "test+directory #50/test file-to+create-#1.txt",
5+
"sha": "8db9c31d79dfb9d0411c7af11b7ec7fabc72c5b1",
6+
"size": 34,
7+
"url": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/contents/test%2Bdirectory%20%2350/test%20file-to%2Bcreate-%231.txt?ref=master",
8+
"html_url": "https://github.com/github-api-test-org/GHContentIntegrationTest/blob/master/test%2Bdirectory%20%2350/test%20file-to%2Bcreate-%231.txt",
9+
"git_url": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/git/blobs/8db9c31d79dfb9d0411c7af11b7ec7fabc72c5b1",
10+
"download_url": "https://raw.githubusercontent.com/github-api-test-org/GHContentIntegrationTest/master/test%2Bdirectory%20%2350/test%20file-to%2Bcreate-%231.txt",
11+
"type": "file",
12+
"_links": {
13+
"self": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/contents/test%2Bdirectory%20%2350/test%20file-to%2Bcreate-%231.txt?ref=master",
14+
"git": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/git/blobs/8db9c31d79dfb9d0411c7af11b7ec7fabc72c5b1",
15+
"html": "https://github.com/github-api-test-org/GHContentIntegrationTest/blob/master/test%2Bdirectory%20%2350/test%20file-to%2Bcreate-%231.txt"
16+
}
17+
}
18+
]
Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,45 @@
11
{
22
"content": {
3-
"name": "test-file-to-create.txt",
4-
"path": "test-file-to-create.txt",
3+
"name": "test file-to+create-#1.txt",
4+
"path": "test+directory #50/test file-to+create-#1.txt",
55
"sha": "da2d3cc78776aec68881668775c46a53f0ee2288",
66
"size": 25,
7-
"url": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/contents/test-file-to-create.txt?ref=master",
8-
"html_url": "https://github.com/github-api-test-org/GHContentIntegrationTest/blob/master/test-file-to-create.txt",
7+
"url": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/contents/test%2Bdirectory%20%2350/test%20file-to%2Bcreate-%231.txt?ref=master",
8+
"html_url": "https://github.com/github-api-test-org/GHContentIntegrationTest/blob/master/test%2Bdirectory%20%2350/test%20file-to%2Bcreate-%231.txt",
99
"git_url": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/git/blobs/da2d3cc78776aec68881668775c46a53f0ee2288",
10-
"download_url": "https://raw.githubusercontent.com/github-api-test-org/GHContentIntegrationTest/master/test-file-to-create.txt",
10+
"download_url": "https://raw.githubusercontent.com/github-api-test-org/GHContentIntegrationTest/master/test%2Bdirectory%20%2350/test%20file-to%2Bcreate-%231.txt",
1111
"type": "file",
1212
"_links": {
13-
"self": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/contents/test-file-to-create.txt?ref=master",
13+
"self": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/contents/test%2Bdirectory%20%2350/test%20file-to%2Bcreate-%231.txt?ref=master",
1414
"git": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/git/blobs/da2d3cc78776aec68881668775c46a53f0ee2288",
15-
"html": "https://github.com/github-api-test-org/GHContentIntegrationTest/blob/master/test-file-to-create.txt"
15+
"html": "https://github.com/github-api-test-org/GHContentIntegrationTest/blob/master/test%2Bdirectory%20%2350/test%20file-to%2Bcreate-%231.txt"
1616
}
1717
},
1818
"commit": {
19-
"sha": "bc9cda986e02ae2677358a28f9cb6097865b3536",
20-
"node_id": "MDY6Q29tbWl0NDA3NjM1Nzc6YmM5Y2RhOTg2ZTAyYWUyNjc3MzU4YTI4ZjljYjYwOTc4NjViMzUzNg==",
21-
"url": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/git/commits/bc9cda986e02ae2677358a28f9cb6097865b3536",
22-
"html_url": "https://github.com/github-api-test-org/GHContentIntegrationTest/commit/bc9cda986e02ae2677358a28f9cb6097865b3536",
19+
"sha": "00c7107a9d00cfd9116747ce61c91f2fd9a7acbc",
20+
"node_id": "MDY6Q29tbWl0NDA3NjM1Nzc6MDBjNzEwN2E5ZDAwY2ZkOTExNjc0N2NlNjFjOTFmMmZkOWE3YWNiYw==",
21+
"url": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/git/commits/00c7107a9d00cfd9116747ce61c91f2fd9a7acbc",
22+
"html_url": "https://github.com/github-api-test-org/GHContentIntegrationTest/commit/00c7107a9d00cfd9116747ce61c91f2fd9a7acbc",
2323
"author": {
2424
"name": "Liam Newman",
2525
"email": "[email protected]",
26-
"date": "2019-10-11T22:17:43Z"
26+
"date": "2019-11-26T01:09:46Z"
2727
},
2828
"committer": {
2929
"name": "Liam Newman",
3030
"email": "[email protected]",
31-
"date": "2019-10-11T22:17:43Z"
31+
"date": "2019-11-26T01:09:46Z"
3232
},
3333
"tree": {
34-
"sha": "7aaee6a6cddcf48f94a40093de05be83c580ce10",
35-
"url": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/git/trees/7aaee6a6cddcf48f94a40093de05be83c580ce10"
34+
"sha": "50e945108f168faeb2eaf15855904e65e70b9336",
35+
"url": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/git/trees/50e945108f168faeb2eaf15855904e65e70b9336"
3636
},
3737
"message": "Updated file for integration tests.",
3838
"parents": [
3939
{
40-
"sha": "88bd1f52fc1e8242758364cee7dce2c82790d272",
41-
"url": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/git/commits/88bd1f52fc1e8242758364cee7dce2c82790d272",
42-
"html_url": "https://github.com/github-api-test-org/GHContentIntegrationTest/commit/88bd1f52fc1e8242758364cee7dce2c82790d272"
40+
"sha": "ce407cde9c6e4c970e33a955197512a2c1922ecf",
41+
"url": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/git/commits/ce407cde9c6e4c970e33a955197512a2c1922ecf",
42+
"html_url": "https://github.com/github-api-test-org/GHContentIntegrationTest/commit/ce407cde9c6e4c970e33a955197512a2c1922ecf"
4343
}
4444
],
4545
"verification": {
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
{
2-
"name": "test-file-to-create.txt",
3-
"path": "test-file-to-create.txt",
2+
"name": "test file-to+create-#1.txt",
3+
"path": "test+directory #50/test file-to+create-#1.txt",
44
"sha": "da2d3cc78776aec68881668775c46a53f0ee2288",
55
"size": 25,
6-
"url": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/contents/test-file-to-create.txt?ref=master",
7-
"html_url": "https://github.com/github-api-test-org/GHContentIntegrationTest/blob/master/test-file-to-create.txt",
6+
"url": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/contents/test%2Bdirectory%20%2350/test%20file-to%2Bcreate-%231.txt?ref=master",
7+
"html_url": "https://github.com/github-api-test-org/GHContentIntegrationTest/blob/master/test%2Bdirectory%20%2350/test%20file-to%2Bcreate-%231.txt",
88
"git_url": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/git/blobs/da2d3cc78776aec68881668775c46a53f0ee2288",
9-
"download_url": "https://raw.githubusercontent.com/github-api-test-org/GHContentIntegrationTest/master/test-file-to-create.txt",
9+
"download_url": "https://raw.githubusercontent.com/github-api-test-org/GHContentIntegrationTest/master/test%2Bdirectory%20%2350/test%20file-to%2Bcreate-%231.txt",
1010
"type": "file",
1111
"content": "dGhpcyBpcyBzb21lIG5ldyBjb250ZW50Cg==\n",
1212
"encoding": "base64",
1313
"_links": {
14-
"self": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/contents/test-file-to-create.txt?ref=master",
14+
"self": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/contents/test%2Bdirectory%20%2350/test%20file-to%2Bcreate-%231.txt?ref=master",
1515
"git": "https://api.github.com/repos/github-api-test-org/GHContentIntegrationTest/git/blobs/da2d3cc78776aec68881668775c46a53f0ee2288",
16-
"html": "https://github.com/github-api-test-org/GHContentIntegrationTest/blob/master/test-file-to-create.txt"
16+
"html": "https://github.com/github-api-test-org/GHContentIntegrationTest/blob/master/test%2Bdirectory%20%2350/test%20file-to%2Bcreate-%231.txt"
1717
}
1818
}

0 commit comments

Comments
 (0)