/*
* Copyright 2015 Ritesh Kapoor
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.ritesh.idea.plugin.reviewboard;
import com.google.common.io.CharStreams;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.util.net.ssl.CertificateManager;
import com.ritesh.idea.plugin.exception.InvalidCredentialException;
import com.ritesh.idea.plugin.exception.ReviewBoardServerException;
import com.ritesh.idea.plugin.reviewboard.model.*;
import com.ritesh.idea.plugin.util.HttpRequestBuilder;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
/**
* @author Ritesh
*/
public class ReviewBoardClient {
private static final String API = "api";
private static final String REVIEW_REQUESTS = "review-requests";
private static final String DIFFS = "diffs";
private static final String FILES = "files";
private static final String DIFF_COMMENTS = "diff-comments";
private static final String DRAFT = "draft";
private static final String REVIEWS = "reviews";
private static final String AUTHORIZATION = "Authorization";
private static final String REPOSITORIES = "repositories";
private static final String GROUPS = "groups";
private static final String USERS = "users";
private static final String ERRORCODE_LOGINFAILED = "104";
private final String url;
private final String userName;
private final String password;
public ReviewBoardClient(String url, String userName, String password) {
this.url = url;
this.userName = userName;
this.password = password;
}
private String getAuthorizationHeader() {
return getAuthorizationHeader(userName, password);
}
private String getAuthorizationHeader(String userName, String password) {
return "Basic " + Base64.encodeBase64String((userName + ":" + password).getBytes());
}
private <T extends RBModel> T checkSuccess(T model) {
if (!model.stat.equalsIgnoreCase("ok")) {
//TODO : more error information in message
String error = String.format("Server : %s, reason : %s, fields : %s,file : %s , revision : %s",
model.err.msg, model.reason, model.fields, model.file, model.revision);
if (model.err.code.equals(ERRORCODE_LOGINFAILED)) {
throw new InvalidCredentialException(error);
}
throw new ReviewBoardServerException(error);
}
return model;
}
public RBReviewRequestList reviewRequestListApi(String fromUser, String toUser, String status,
String repositoryId, long start, long count) throws URISyntaxException, IOException {
HttpRequestBuilder requestBuilder = HttpRequestBuilder.get(url).route(API).route(REVIEW_REQUESTS).slash();
if (toUser != null) requestBuilder.queryString("to-users", toUser);
if (fromUser != null) requestBuilder.queryString("from-user", fromUser);
if (repositoryId != null) requestBuilder.queryString("repository", repositoryId);
RBReviewRequestList result = requestBuilder.queryString("start", String.valueOf(start))
.queryString("max-results", String.valueOf(count))
.queryString("status", status)
.header(AUTHORIZATION, getAuthorizationHeader())
.asJson(RBReviewRequestList.class);
return checkSuccess(result);
}
public RBDiffList diffListApi(String reviewRequestId) throws URISyntaxException, IOException {
RBDiffList result = HttpRequestBuilder.get(url).route(API).route(REVIEW_REQUESTS).route(reviewRequestId)
.route(DIFFS).slash().header(AUTHORIZATION, getAuthorizationHeader()).asJson(RBDiffList.class);
return checkSuccess(result);
}
public RBFileDiff fileDiffApi(String reviewRequestId, String revision) throws URISyntaxException, IOException {
RBFileDiff result = HttpRequestBuilder.get(url).route(API).route(REVIEW_REQUESTS)
.route(reviewRequestId).route(DIFFS).route(revision).route(FILES).slash()
.header(AUTHORIZATION, getAuthorizationHeader())
.asJson(RBFileDiff.class);
return checkSuccess(result);
}
public RBComments diffCommentListApi(String reviewRequestId, String revision, String fileId) throws URISyntaxException, IOException {
RBComments result = HttpRequestBuilder.get(url).route(API).route(REVIEW_REQUESTS)
.route(reviewRequestId).route(DIFFS).route(revision).route(FILES).route(fileId)
.route(DIFF_COMMENTS).slash()
.header(AUTHORIZATION, getAuthorizationHeader())
.asJson(RBComments.class);
return checkSuccess(result);
}
public RBReview createReviewApi(String reviewRequestId, Boolean shipIt) throws URISyntaxException, IOException {
HttpRequestBuilder requestBuilder = HttpRequestBuilder.post(url).route(API).route(REVIEW_REQUESTS)
.route(reviewRequestId).route(REVIEWS).slash()
.header(AUTHORIZATION, getAuthorizationHeader());
if (shipIt != null) requestBuilder.field("ship_it", shipIt);
RBReview result = requestBuilder.asJson(RBReview.class);
return checkSuccess(result);
}
public RBRepository repositories(int count) throws URISyntaxException, IOException {
RBRepository result = HttpRequestBuilder.get(url).route(API).route(REPOSITORIES).slash()
.header(AUTHORIZATION, getAuthorizationHeader())
.queryString("max-results", count)
.asJson(RBRepository.class);
return checkSuccess(result);
}
public void createDiffComment(String reviewRequestId, String reviewId, String filediff_id,
int first_line, int num_lines, String text, boolean issue_opened) throws URISyntaxException, IOException {
RBModel result = HttpRequestBuilder.post(url).route(API).route(REVIEW_REQUESTS)
.route(reviewRequestId).route(REVIEWS).route(reviewId).route(DIFF_COMMENTS).slash()
.field("filediff_id", filediff_id)
.field("first_line", first_line)
.field("num_lines", num_lines)
.field("issue_opened", issue_opened)
.field("text", text)
.header(AUTHORIZATION, getAuthorizationHeader())
.asJson(RBModel.class);
checkSuccess(result);
}
public void updateReviewApi(String reviewRequestId, String reviewId, boolean isPublic,
String body_top, String body_bottom) throws URISyntaxException, IOException {
HttpRequestBuilder put = HttpRequestBuilder.put(url);
put.route(API).route(REVIEW_REQUESTS)
.route(reviewRequestId).route(REVIEWS).route(reviewId).slash()
.header(AUTHORIZATION, getAuthorizationHeader())
.field("public", isPublic);
if (!StringUtils.isEmpty(body_bottom)) put.field("body_bottom", body_bottom);
if (!StringUtils.isEmpty(body_top)) put.field("body_top", body_top);
RBModel result = put.asJson(RBModel.class);
checkSuccess(result);
}
public String contents(String href) {
try {
HttpRequestBase request = HttpRequestBuilder.get(href)
.header(AUTHORIZATION, getAuthorizationHeader())
.request();
try (CloseableHttpClient client = HttpClientBuilder.create().setSslcontext(CertificateManager.getInstance().getSslContext()).build()) {
CloseableHttpResponse response = client.execute(request);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_FOUND) {
return null;
} else {
return CharStreams.toString(new InputStreamReader(response.getEntity().getContent()));
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public RBCreateReview createReviewRequestApi(String repositoryId) throws URISyntaxException, IOException {
RBCreateReview result = HttpRequestBuilder.post(url).route(API).route(REVIEW_REQUESTS).slash()
.header(AUTHORIZATION, getAuthorizationHeader())
.field("repository", repositoryId)
.asJson(RBCreateReview.class);
return checkSuccess(result);
}
public RBModel updateReviewRequestStatus(String reviewRequestId, String status) throws URISyntaxException, IOException {
RBModel result = HttpRequestBuilder.put(url).route(API).route(REVIEW_REQUESTS)
.route(reviewRequestId).slash()
.header(AUTHORIZATION, getAuthorizationHeader())
.field("status", status)
.asJson(RBCreateReview.class);
return checkSuccess(result);
}
public RBModel draftDiffUploadApi(String reviewRequestId, String content, String basedir) throws URISyntaxException, IOException {
RBModel model = HttpRequestBuilder.post(url).route(API).route(REVIEW_REQUESTS)
.route(reviewRequestId).route(DIFFS).slash()
.header(AUTHORIZATION, getAuthorizationHeader())
.field("basedir", basedir)
.file("path", "git.diff", content.getBytes(CharsetToolkit.UTF8_CHARSET))
.asJson(RBModel.class);
return checkSuccess(model);
}
public RBGroupList groupsApi(String q, int maxResults) throws URISyntaxException, IOException {
RBGroupList result = HttpRequestBuilder.get(url).route(API).route(GROUPS).slash()
.header(AUTHORIZATION, getAuthorizationHeader())
.queryString("q", q)
.queryString("max-results", maxResults)
.asJson(RBGroupList.class);
return checkSuccess(result);
}
public RBUserList usersApi(String q) throws URISyntaxException, IOException {
RBUserList result = HttpRequestBuilder.get(url).route(API).route(USERS).slash()
.header(AUTHORIZATION, getAuthorizationHeader())
.queryString("q", q)
.asJson(RBUserList.class);
return checkSuccess(result);
}
public RBModel updateReviewApi(String reviewRequestId, String description, String summary, String targetGroups,
String targetPeople, boolean isPublic) throws URISyntaxException, IOException {
RBModel model = HttpRequestBuilder.post(url).route(API).route(REVIEW_REQUESTS)
.route(reviewRequestId).route(DRAFT).slash()
.header(AUTHORIZATION, getAuthorizationHeader())
.field("summary", summary)
.field("description", description)
.field("target_groups", targetGroups)
.field("target_people", targetPeople)
.field("public", isPublic)
.asJson(RBModel.class);
return checkSuccess(model);
}
public RBModel testConnection(String url, String username, String password) throws URISyntaxException, IOException {
RBModel model = HttpRequestBuilder.get(url).route(API).slash()
.header(AUTHORIZATION, getAuthorizationHeader(username, password)).asJson(RBModel.class);
return checkSuccess(model);
}
}