Skip to content

Commit

Permalink
#461: Correctly encode spaces in Azure DevOps project and repository …
Browse files Browse the repository at this point in the history
…names

Azure DevOps does not correctly resolve URLs that contains spaces encoded as a `+`. To allow projects containg spaces to be decorated, the `+` from an the encoded names is being replaced with a `%20` since Azure Devops resolves spaces encoded this way properly.
  • Loading branch information
mc1arke committed Oct 2, 2021
1 parent b18e039 commit 27699d0
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -65,51 +65,51 @@ public AzureDevopsRestClient(String apiUrl, String authToken, ObjectMapper objec

@Override
public void submitPullRequestStatus(String projectId, String repositoryName, int pullRequestId, GitPullRequestStatus status) throws IOException {
String url = String.format("%s/%s/_apis/git/repositories/%s/pullRequests/%s/statuses?api-version=%s", apiUrl, URLEncoder.encode(projectId, StandardCharsets.UTF_8), URLEncoder.encode(repositoryName, StandardCharsets.UTF_8), pullRequestId, API_VERSION);
String url = String.format("%s/%s/_apis/git/repositories/%s/pullRequests/%s/statuses?api-version=%s", apiUrl, encode(projectId), encode(repositoryName), pullRequestId, API_VERSION);
execute(url, "post", objectMapper.writeValueAsString(status), null);
}

@Override
public Repository getRepository(String projectId, String repositoryName) throws IOException {
String url = String.format("%s/%s/_apis/git/repositories/%s?api-version=%s", apiUrl, URLEncoder.encode(projectId, StandardCharsets.UTF_8), URLEncoder.encode(repositoryName, StandardCharsets.UTF_8), API_VERSION);
String url = String.format("%s/%s/_apis/git/repositories/%s?api-version=%s", apiUrl, encode(projectId), encode(repositoryName), API_VERSION);
return execute(url, "get", null, Repository.class);
}

@Override
public List<CommentThread> retrieveThreads(String projectId, String repositoryName, int pullRequestId) throws IOException {
String url = String.format("%s/%s/_apis/git/repositories/%s/pullRequests/%s/threads?api-version=%s", apiUrl, URLEncoder.encode(projectId, StandardCharsets.UTF_8), URLEncoder.encode(repositoryName, StandardCharsets.UTF_8), pullRequestId, API_VERSION);
String url = String.format("%s/%s/_apis/git/repositories/%s/pullRequests/%s/threads?api-version=%s", apiUrl, encode(projectId), encode(repositoryName), pullRequestId, API_VERSION);
return Objects.requireNonNull(execute(url, "get", null, CommentThreadResponse.class)).getValue();
}

@Override
public CommentThread createThread(String projectId, String repositoryName, int pullRequestId, CreateCommentThreadRequest thread) throws IOException {
String url = String.format("%s/%s/_apis/git/repositories/%s/pullRequests/%s/threads?api-version=%s", apiUrl, URLEncoder.encode(projectId, StandardCharsets.UTF_8), URLEncoder.encode(repositoryName, StandardCharsets.UTF_8), pullRequestId, API_VERSION);
String url = String.format("%s/%s/_apis/git/repositories/%s/pullRequests/%s/threads?api-version=%s", apiUrl, encode(projectId), encode(repositoryName), pullRequestId, API_VERSION);
return execute(url, "post", objectMapper.writeValueAsString(thread), CommentThread.class);
}

@Override
public void addCommentToThread(String projectId, String repositoryName, int pullRequestId, int threadId, CreateCommentRequest comment) throws IOException {
String url = String.format("%s/%s/_apis/git/repositories/%s/pullRequests/%s/threads/%s/comments?api-version=%s", apiUrl, URLEncoder.encode(projectId, StandardCharsets.UTF_8), URLEncoder.encode(repositoryName, StandardCharsets.UTF_8), pullRequestId, threadId, API_VERSION);
String url = String.format("%s/%s/_apis/git/repositories/%s/pullRequests/%s/threads/%s/comments?api-version=%s", apiUrl, encode(projectId), encode(repositoryName), pullRequestId, threadId, API_VERSION);
execute(url, "post", objectMapper.writeValueAsString(comment), null);
}

@Override
public void resolvePullRequestThread(String projectId, String repositoryName, int pullRequestId, int threadId) throws IOException {
String url = String.format("%s/%s/_apis/git/repositories/%s/pullRequests/%s/threads/%s?api-version=%s", apiUrl, URLEncoder.encode(projectId, StandardCharsets.UTF_8), URLEncoder.encode(repositoryName, StandardCharsets.UTF_8), pullRequestId, threadId, API_VERSION);
String url = String.format("%s/%s/_apis/git/repositories/%s/pullRequests/%s/threads/%s?api-version=%s", apiUrl, encode(projectId), encode(repositoryName), pullRequestId, threadId, API_VERSION);

UpdateCommentThreadStatusRequest commentThread = new UpdateCommentThreadStatusRequest(CommentThreadStatus.CLOSED);
execute(url, "patch", objectMapper.writeValueAsString(commentThread), null);
}

@Override
public PullRequest retrievePullRequest(String projectId, String repositoryName, int pullRequestId) throws IOException {
String url = String.format("%s/%s/_apis/git/repositories/%s/pullRequests/%s?api-version=%s", apiUrl, URLEncoder.encode(projectId, StandardCharsets.UTF_8), URLEncoder.encode(repositoryName, StandardCharsets.UTF_8), pullRequestId, API_VERSION);
String url = String.format("%s/%s/_apis/git/repositories/%s/pullRequests/%s?api-version=%s", apiUrl, encode(projectId), encode(repositoryName), pullRequestId, API_VERSION);
return execute(url, "get", null, PullRequest.class);
}

@Override
public List<Commit> getPullRequestCommits(String projectId, String repositoryName, int pullRequestId) throws IOException {
String url = String.format("%s/%s/_apis/git/repositories/%s/pullRequests/%s/commits?api-version=%s", apiUrl, URLEncoder.encode(projectId, StandardCharsets.UTF_8), URLEncoder.encode(repositoryName, StandardCharsets.UTF_8), pullRequestId, API_VERSION);
String url = String.format("%s/%s/_apis/git/repositories/%s/pullRequests/%s/commits?api-version=%s", apiUrl, encode(projectId), encode(repositoryName), pullRequestId, API_VERSION);
return Objects.requireNonNull(execute(url, "get", null, Commits.class)).getValue();
}

Expand Down Expand Up @@ -156,4 +156,8 @@ private static void validateResponse(HttpResponse httpResponse) {

throw new IllegalStateException("An unexpected response code was returned from the Azure Devops API - Expected: 200, Got: " + httpResponse.getStatusLine().getStatusCode());
}

private static String encode(String input) {
return URLEncoder.encode(input, StandardCharsets.UTF_8).replace("+", "%20");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ private void configureTestDefaults() {
}

private void setupStubs() {
wireMockRule.stubFor(get(urlEqualTo("/azure+Project/_apis/git/repositories/my+Repository/pullRequests/"+ pullRequestId +"/threads?api-version=4.1"))
wireMockRule.stubFor(get(urlEqualTo("/azure%20Project/_apis/git/repositories/my%20Repository/pullRequests/"+ pullRequestId +"/threads?api-version=4.1"))
.withHeader("Accept", equalTo("application/json"))
.withHeader("Authorization", equalTo(authHeader))
.willReturn(aResponse()
Expand Down Expand Up @@ -208,7 +208,7 @@ private void setupStubs() {
" \"count\": 2" + System.lineSeparator() +
"}")));

wireMockRule.stubFor(get(urlEqualTo("/azure+Project/_apis/git/repositories/my+Repository/pullRequests/" + pullRequestId +"?api-version=4.1"))
wireMockRule.stubFor(get(urlEqualTo("/azure%20Project/_apis/git/repositories/my%20Repository/pullRequests/" + pullRequestId +"?api-version=4.1"))
.withHeader("Accept", equalTo("application/json"))
.withHeader("Authorization", equalTo(authHeader))
.willReturn(aResponse()
Expand Down Expand Up @@ -313,15 +313,15 @@ private void setupStubs() {
" \"artifactId\": \"vstfs:///Git/PullRequestId/a7573007-bbb3-4341-b726-0c4148a07853%2f3411ebc1-d5aa-464f-9615-0b527bc66719%2f22\"" + System.lineSeparator() +
"}")));

wireMockRule.stubFor(post(urlEqualTo("/azure+Project/_apis/git/repositories/my+Repository/pullRequests/" + pullRequestId + "/threads/" + threadId + "/comments?api-version=4.1"))
wireMockRule.stubFor(post(urlEqualTo("/azure%20Project/_apis/git/repositories/my%20Repository/pullRequests/" + pullRequestId + "/threads/" + threadId + "/comments?api-version=4.1"))
.withHeader("Accept", equalTo("application/json"))
.withHeader("Content-Type", equalTo("application/json; charset=UTF-8"))
.withHeader("Authorization", equalTo(authHeader))
.withRequestBody(equalTo("{\"content\":\"Issue has been closed in SonarQube\"}")
)
.willReturn(ok()));

wireMockRule.stubFor(get(urlEqualTo("/azure+Project/_apis/git/repositories/my+Repository/pullRequests/" + pullRequestId + "/commits?api-version=4.1"))
wireMockRule.stubFor(get(urlEqualTo("/azure%20Project/_apis/git/repositories/my%20Repository/pullRequests/" + pullRequestId + "/commits?api-version=4.1"))
.withHeader("Accept", equalTo("application/json"))
.withHeader("Authorization", equalTo(authHeader))
.willReturn(aResponse().withStatus(200).withBody("{\"value\": [{" + System.lineSeparator() +
Expand Down Expand Up @@ -372,7 +372,7 @@ private void setupStubs() {
"}]}")));


wireMockRule.stubFor(post(urlEqualTo("/azure+Project/_apis/git/repositories/my+Repository/pullRequests/"+ pullRequestId +"/statuses?api-version=4.1"))
wireMockRule.stubFor(post(urlEqualTo("/azure%20Project/_apis/git/repositories/my%20Repository/pullRequests/"+ pullRequestId +"/statuses?api-version=4.1"))
.withHeader("Content-Type", equalTo("application/json; charset=UTF-8"))
.withHeader("Authorization", equalTo(authHeader))
.withRequestBody(equalTo("{" +
Expand All @@ -384,7 +384,7 @@ private void setupStubs() {
)
.willReturn(ok()));

wireMockRule.stubFor(post(urlEqualTo("/azure+Project/_apis/git/repositories/my+Repository/pullRequests/"+ pullRequestId +"/threads?api-version=4.1"))
wireMockRule.stubFor(post(urlEqualTo("/azure%20Project/_apis/git/repositories/my%20Repository/pullRequests/"+ pullRequestId +"/threads?api-version=4.1"))
.withHeader("Accept", equalTo("application/json"))
.withHeader("Content-Type", equalTo("application/json; charset=UTF-8"))
.withHeader("Authorization", equalTo(authHeader))
Expand Down Expand Up @@ -441,7 +441,7 @@ private void setupStubs() {
" }" + System.lineSeparator() +
"}")));

wireMockRule.stubFor(post(urlEqualTo("/azure+Project/_apis/git/repositories/my+Repository/pullRequests/"+ pullRequestId +"/threads?api-version=4.1"))
wireMockRule.stubFor(post(urlEqualTo("/azure%20Project/_apis/git/repositories/my%20Repository/pullRequests/"+ pullRequestId +"/threads?api-version=4.1"))
.withHeader("Accept", equalTo("application/json"))
.withHeader("Content-Type", equalTo("application/json; charset=UTF-8"))
.withHeader("Authorization", equalTo(authHeader))
Expand Down Expand Up @@ -498,7 +498,7 @@ private void setupStubs() {
" }" + System.lineSeparator() +
"}")));

wireMockRule.stubFor(patch(urlEqualTo("/azure+Project/_apis/git/repositories/my+Repository/pullRequests/" + pullRequestId + "/threads/" + threadId + "?api-version=4.1"))
wireMockRule.stubFor(patch(urlEqualTo("/azure%20Project/_apis/git/repositories/my%20Repository/pullRequests/" + pullRequestId + "/threads/" + threadId + "?api-version=4.1"))
.withHeader("Content-Type", equalTo("application/json; charset=UTF-8"))
.withHeader("Authorization", equalTo(authHeader))
.withRequestBody(equalTo("{" +
Expand Down

0 comments on commit 27699d0

Please sign in to comment.