diff options
Diffstat (limited to 'src/main/java/org/jenkinsci/plugins/bbprb/bitbucket')
-rw-r--r-- | src/main/java/org/jenkinsci/plugins/bbprb/bitbucket/ApiClient.java | 246 | ||||
-rw-r--r-- | src/main/java/org/jenkinsci/plugins/bbprb/bitbucket/BuildState.java | 9 |
2 files changed, 255 insertions, 0 deletions
diff --git a/src/main/java/org/jenkinsci/plugins/bbprb/bitbucket/ApiClient.java b/src/main/java/org/jenkinsci/plugins/bbprb/bitbucket/ApiClient.java new file mode 100644 index 0000000..80ae3aa --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/bbprb/bitbucket/ApiClient.java @@ -0,0 +1,246 @@ +package org.jenkinsci.plugins.bbprb.bitbucket; + +import org.apache.commons.httpclient.*; +import org.apache.commons.httpclient.auth.AuthScope; +import org.apache.commons.httpclient.methods.GetMethod; +import org.apache.commons.httpclient.methods.PostMethod; +import org.apache.commons.httpclient.methods.DeleteMethod; +import org.apache.commons.httpclient.params.HttpClientParams; +import org.codehaus.jackson.map.ObjectMapper; +import org.codehaus.jackson.map.type.TypeFactory; +import org.codehaus.jackson.type.JavaType; +import org.codehaus.jackson.type.TypeReference; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import jenkins.model.Jenkins; +import hudson.ProxyConfiguration; + +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.httpclient.methods.PutMethod; +import org.apache.commons.httpclient.util.EncodingUtil; + +/** + * Created by nishio + */ +public class ApiClient { + private static final Logger logger = + Logger.getLogger(ApiClient.class.getName()); + private static final String V1_API_BASE_URL = + "https://bitbucket.org/api/1.0/repositories/"; + private static final String V2_API_BASE_URL = + "https://bitbucket.org/api/2.0/repositories/"; + private static final String COMPUTED_KEY_FORMAT = "%s-%s"; + private String repository; + private Credentials credentials; + private String key; + private String name; + private HttpClientFactory factory; + + public static final byte MAX_KEY_SIZE_BB_API = 40; + + public static class HttpClientFactory { + public static final HttpClientFactory INSTANCE = new HttpClientFactory(); + private static final int DEFAULT_TIMEOUT = 60000; + + public HttpClient getInstanceHttpClient() { + HttpClient client = new HttpClient(); + + HttpClientParams params = client.getParams(); + params.setConnectionManagerTimeout(DEFAULT_TIMEOUT); + params.setSoTimeout(DEFAULT_TIMEOUT); + + if (Jenkins.getInstance() == null) + return client; + + ProxyConfiguration proxy = getInstance().proxy; + if (proxy == null) + return client; + + logger.log(Level.FINE, "Jenkins proxy: {0}:{1}", + new Object[] {proxy.name, proxy.port}); + client.getHostConfiguration().setProxy(proxy.name, proxy.port); + String username = proxy.getUserName(); + String password = proxy.getPassword(); + + // Consider it to be passed if username specified. Sufficient? + if (username != null && !"".equals(username.trim())) { + logger.log(Level.FINE, "Using proxy authentication (user={0})", + username); + client.getState().setProxyCredentials( + AuthScope.ANY, new UsernamePasswordCredentials(username, password)); + } + + return client; + } + + private Jenkins getInstance() { + final Jenkins instance = Jenkins.getInstance(); + if (instance == null) { + throw new IllegalStateException("Jenkins instance is NULL!"); + } + return instance; + } + } + + public <T extends HttpClientFactory> ApiClient(String username, + String password, + String repository, String key, + String name) { + this.credentials = new UsernamePasswordCredentials(username, password); + this.repository = repository; + this.key = key; + this.name = name; + this.factory = HttpClientFactory.INSTANCE; + } + + public String getName() { + return this.name; + } + + private static MessageDigest SHA1 = null; + + /** + * Retrun + * @param keyExPart + * @return key parameter for call BitBucket API + */ + private String computeAPIKey(String keyExPart) { + String computedKey = + String.format(COMPUTED_KEY_FORMAT, this.key, keyExPart); + + if (computedKey.length() > MAX_KEY_SIZE_BB_API) { + try { + if (SHA1 == null) + SHA1 = MessageDigest.getInstance("SHA1"); + return new String( + Hex.encodeHex(SHA1.digest(computedKey.getBytes("UTF-8")))); + } catch (NoSuchAlgorithmException e) { + logger.log(Level.WARNING, "Failed to create hash provider", e); + } catch (UnsupportedEncodingException e) { + logger.log(Level.WARNING, "Failed to create hash provider", e); + } + } + return (computedKey.length() <= MAX_KEY_SIZE_BB_API) + ? computedKey + : computedKey.substring(0, MAX_KEY_SIZE_BB_API); + } + + public String buildStatusKey(String bsKey) { + return this.computeAPIKey(bsKey); + } + + public boolean hasBuildStatus(String revision, String keyEx) { + String url = v2("/commit/" + revision + "/statuses/build/" + + this.computeAPIKey(keyEx)); + String reqBody = get(url); + return reqBody != null && reqBody.contains("\"state\""); + } + + public void setBuildStatus(String revision, BuildState state, String buildUrl, + String comment, String keyEx) { + String url = v2("/commit/" + revision + "/statuses/build"); + String computedKey = this.computeAPIKey(keyEx); + NameValuePair[] data = new NameValuePair[] { + new NameValuePair("description", comment), + new NameValuePair("key", computedKey), + new NameValuePair("name", this.name), + new NameValuePair("state", state.toString()), + new NameValuePair("url", buildUrl), + }; + logger.log(Level.FINE, + "POST state {0} to {1} with key {2} with response {3}", + new Object[] {state, url, computedKey, post(url, data)}); + } + + public void deletePullRequestApproval(String pullRequestId) { + delete(v2("/pullrequests/" + pullRequestId + "/approve")); + } + + public void deletePullRequestComment(String pullRequestId, String commentId) { + delete(v1("/pullrequests/" + pullRequestId + "/comments/" + commentId)); + } + + public void updatePullRequestComment(String pullRequestId, String content, + String commentId) { + NameValuePair[] data = new NameValuePair[] { + new NameValuePair("content", content), + }; + put(v1("/pullrequests/" + pullRequestId + "/comments/" + commentId), data); + } + + private HttpClient getHttpClient() { + return this.factory.getInstanceHttpClient(); + } + + private String v1(String path) { + return V1_API_BASE_URL + this.repository + path; + } + + private String v2(String path) { + return V2_API_BASE_URL + this.repository + path; + } + + private String get(String path) { + return send(new GetMethod(path)); + } + + private String post(String path, NameValuePair[] data) { + PostMethod req = new PostMethod(path); + req.setRequestBody(data); + req.getParams().setContentCharset("utf-8"); + return send(req); + } + + private void delete(String path) { + send(new DeleteMethod(path)); + } + + private void put(String path, NameValuePair[] data) { + PutMethod req = new PutMethod(path); + req.setRequestBody(EncodingUtil.formUrlEncode(data, "utf-8")); + req.getParams().setContentCharset("utf-8"); + send(req); + } + + private String send(HttpMethodBase req) { + HttpClient client = getHttpClient(); + client.getState().setCredentials(AuthScope.ANY, credentials); + client.getParams().setAuthenticationPreemptive(true); + try { + int statusCode = client.executeMethod(req); + if (statusCode != HttpStatus.SC_OK) { + logger.log(Level.WARNING, "Response status: " + req.getStatusLine() + + " URI: " + req.getURI()); + } else { + return req.getResponseBodyAsString(); + } + } catch (HttpException e) { + logger.log(Level.WARNING, "Failed to send request.", e); + } catch (IOException e) { + logger.log(Level.WARNING, "Failed to send request.", e); + } finally { + req.releaseConnection(); + } + return null; + } + + private <R> R parse(String response, Class<R> cls) throws IOException { + return new ObjectMapper().readValue(response, cls); + } + private <R> R parse(String response, JavaType type) throws IOException { + return new ObjectMapper().readValue(response, type); + } + private <R> R parse(String response, TypeReference<R> ref) + throws IOException { + return new ObjectMapper().readValue(response, ref); + } +} diff --git a/src/main/java/org/jenkinsci/plugins/bbprb/bitbucket/BuildState.java b/src/main/java/org/jenkinsci/plugins/bbprb/bitbucket/BuildState.java new file mode 100644 index 0000000..8fe1cdd --- /dev/null +++ b/src/main/java/org/jenkinsci/plugins/bbprb/bitbucket/BuildState.java @@ -0,0 +1,9 @@ +package org.jenkinsci.plugins.bbprb.bitbucket; + +/** + * Valid build states for a pull request + * + * @see + * "https://confluence.atlassian.com/bitbucket/buildstatus-resource-779295267.html" + */ +public enum BuildState { FAILED, INPROGRESS, SUCCESSFUL } |