Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 38 additions & 1 deletion src/main/java/org/kohsuke/github/GitHubRequest.java
Original file line number Diff line number Diff line change
Expand Up @@ -589,7 +589,15 @@ static URL getApiURL(String apiUrl, String tailApiUrl) {
// backward compatibility
apiUrl = GitHubClient.GITHUB_URL;
}
return new URI(apiUrl + tailApiUrl).toURL();

String fullApiUrl = apiUrl + tailApiUrl;
try {
return new URI(fullApiUrl).toURL();
} catch (URISyntaxException e) {
// Some API URL fields include unescaped square brackets in path segments.
// Keep existing escaping as-is while making the URL URI-safe for connectors.
return new URI(encodeSquareBrackets(fullApiUrl)).toURL();
}
} catch (Exception e) {
// The data going into constructing this URL should be controlled by the GitHub API framework,
// so a malformed URL here is a framework runtime error.
Expand All @@ -598,6 +606,35 @@ static URL getApiURL(String apiUrl, String tailApiUrl) {
throw new GHException("Unable to build GitHub API URL", e);
}
}

@Nonnull
private static String encodeSquareBrackets(@Nonnull String url) {
URL parsedUrl;
try {
parsedUrl = new URL(url);
} catch (MalformedURLException e) {
// Preserve the original input when URL parsing fails so existing error behavior is unchanged.
return url;
}

String path = parsedUrl.getPath();
String query = parsedUrl.getQuery();
String ref = parsedUrl.getRef();

StringBuilder encodedUrl = new StringBuilder();
encodedUrl.append(parsedUrl.getProtocol()).append("://").append(parsedUrl.getAuthority());
if (path != null) {
encodedUrl.append(path.replace("[", "%5B").replace("]", "%5D"));
}
if (query != null) {
encodedUrl.append('?').append(query.replace("[", "%5B").replace("]", "%5D"));
}
if (ref != null) {
encodedUrl.append('#').append(ref.replace("[", "%5B").replace("]", "%5D"));
}

return encodedUrl.toString();
}
/**
* Create a new {@link Builder}.
*
Expand Down
7 changes: 7 additions & 0 deletions src/test/java/org/kohsuke/github/GitHubStaticTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,13 @@ public void testGitHubRequest_getApiURL() {
equalTo("ftp://whoa.github.com/endpoint"));
assertThat(GitHubRequest.getApiURL(null, "ftp://api.test.github.com/endpoint").toString(),
equalTo("ftp://api.test.github.com/endpoint"));
assertThat(
GitHubRequest
.getApiURL(null,
"https://api.github.com/repositories/694641495/contents/Alfred.alfredpreferences/workflows/menubar-search%20[3rd-Party]/menu/Package.swift?ref=073e7b4493d088fdf1995a74cf5da201a5795181")
.toString(),
equalTo(
"https://api.github.com/repositories/694641495/contents/Alfred.alfredpreferences/workflows/menubar-search%20%5B3rd-Party%5D/menu/Package.swift?ref=073e7b4493d088fdf1995a74cf5da201a5795181"));

GHException e;
e = Assert.assertThrows(GHException.class,
Expand Down
26 changes: 26 additions & 0 deletions src/test/java/org/kohsuke/github/GitHubTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.kohsuke.github.example.dataobject.ReadOnlyObjects;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;

import static org.hamcrest.Matchers.*;
Expand Down Expand Up @@ -305,6 +306,31 @@ public void searchContent() throws Exception {
assertThat(e.getMessage(), equalTo("qualifier cannot be null or empty"));
}

/**
* Search content where the API item URL contains special characters in path segments.
*
* @throws Exception
* the exception
*/
@Test
public void searchContentSpecialCharactersInUrl() throws Exception {
GHContent content = gitHub.searchContent()
.q("filename:Package.swift repo:chrisgrieser/.config")
.list()
.iterator()
.next();

assertThat(content.getPath(),
equalTo("Alfred.alfredpreferences/workflows/menubar-search [3rd-Party]/menu/Package.swift"));

byte[] data;
try (java.io.InputStream inputStream = content.read()) {
data = inputStream.readAllBytes();
}

assertThat(new String(data, StandardCharsets.UTF_8), equalTo("// content with special path chars\n"));
}

/**
* Search content with forks.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"login": "bitwiseman",
"id": 1958953,
"node_id": "MDQ6VXNlcjE5NTg5NTM=",
"avatar_url": "https://avatars.githubusercontent.com/u/1958953?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/bitwiseman",
"html_url": "https://github.com/bitwiseman",
"followers_url": "https://api.github.com/users/bitwiseman/followers",
"following_url": "https://api.github.com/users/bitwiseman/following{/other_user}",
"gists_url": "https://api.github.com/users/bitwiseman/gists{/gist_id}",
"starred_url": "https://api.github.com/users/bitwiseman/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/bitwiseman/subscriptions",
"organizations_url": "https://api.github.com/users/bitwiseman/orgs",
"repos_url": "https://api.github.com/users/bitwiseman/repos",
"events_url": "https://api.github.com/users/bitwiseman/events{/privacy}",
"received_events_url": "https://api.github.com/users/bitwiseman/received_events",
"type": "User",
"site_admin": false,
"name": "Liam Newman"
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"total_count": 1,
"incomplete_results": false,
"items": [
{
"name": "Package.swift",
"path": "Alfred.alfredpreferences/workflows/menubar-search [3rd-Party]/menu/Package.swift",
"sha": "f13c7ef5735f02330063eb233273ccf0bb37f332",
"url": "https://api.github.com/repositories/694641495/contents/Alfred.alfredpreferences/workflows/menubar-search%20[3rd-Party]/menu/Package.swift?ref=073e7b4493d088fdf1995a74cf5da201a5795181",
"git_url": "https://api.github.com/repositories/694641495/git/blobs/f13c7ef5735f02330063eb233273ccf0bb37f332",
"html_url": "https://github.com/chrisgrieser/.config/blob/073e7b4493d088fdf1995a74cf5da201a5795181/Alfred.alfredpreferences/workflows/menubar-search%20%5B3rd-Party%5D/menu/Package.swift",
"repository": {
"id": 694641495,
"name": ".config",
"full_name": "chrisgrieser/.config",
"private": false,
"owner": {
"login": "chrisgrieser",
"id": 1106439,
"type": "User",
"site_admin": false
}
},
"score": 1
}
]
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "Package.swift",
"path": "Alfred.alfredpreferences/workflows/menubar-search [3rd-Party]/menu/Package.swift",
"sha": "f13c7ef5735f02330063eb233273ccf0bb37f332",
"size": 35,
"url": "https://api.github.com/repositories/694641495/contents/Alfred.alfredpreferences/workflows/menubar-search%20[3rd-Party]/menu/Package.swift?ref=073e7b4493d088fdf1995a74cf5da201a5795181",
"html_url": "https://github.com/chrisgrieser/.config/blob/073e7b4493d088fdf1995a74cf5da201a5795181/Alfred.alfredpreferences/workflows/menubar-search%20%5B3rd-Party%5D/menu/Package.swift",
"git_url": "https://api.github.com/repositories/694641495/git/blobs/f13c7ef5735f02330063eb233273ccf0bb37f332",
"download_url": "https://raw.githubusercontent.com/chrisgrieser/.config/073e7b4493d088fdf1995a74cf5da201a5795181/Alfred.alfredpreferences/workflows/menubar-search%20%5B3rd-Party%5D/menu/Package.swift",
"type": "file",
"content": "Ly8gY29udGVudCB3aXRoIHNwZWNpYWwgcGF0aCBjaGFycwo=",
"encoding": "base64"
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"id": "e173c8a1-fa95-4d0c-8b5c-b18f3ecc8bab",
"name": "user",
"request": {
"url": "/user",
"method": "GET",
"headers": {
"Accept": {
"equalTo": "application/vnd.github+json"
}
}
},
"response": {
"status": 200,
"bodyFileName": "1-user.json",
"headers": {
"Content-Type": "application/json; charset=utf-8"
}
},
"uuid": "e173c8a1-fa95-4d0c-8b5c-b18f3ecc8bab",
"persistent": true,
"insertionIndex": 1
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"id": "4ecf8d7f-5a76-4f31-9e2c-2f770fa64eb3",
"name": "search_code",
"request": {
"url": "/search/code?q=filename%3APackage.swift+repo%3Achrisgrieser%2F.config",
"method": "GET",
"headers": {
"Accept": {
"equalTo": "application/vnd.github+json"
}
}
},
"response": {
"status": 200,
"bodyFileName": "2-search_code.json",
"headers": {
"Content-Type": "application/json; charset=utf-8"
}
},
"uuid": "4ecf8d7f-5a76-4f31-9e2c-2f770fa64eb3",
"persistent": true,
"insertionIndex": 2
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"id": "be779a3f-c503-4d48-9f35-0f8d39bb70a8",
"name": "repositories_694641495_contents_package_swift",
"request": {
"url": "/repositories/694641495/contents/Alfred.alfredpreferences/workflows/menubar-search%20%5B3rd-Party%5D/menu/Package.swift?ref=073e7b4493d088fdf1995a74cf5da201a5795181",
"method": "GET",
"headers": {
"Accept": {
"equalTo": "application/vnd.github+json"
}
}
},
"response": {
"status": 200,
"bodyFileName": "3-repositories_694641495_contents_package.swift.json",
"headers": {
"Content-Type": "application/json; charset=utf-8"
}
},
"uuid": "be779a3f-c503-4d48-9f35-0f8d39bb70a8",
"persistent": true,
"insertionIndex": 3
}

Loading