initial commit

This commit is contained in:
Brian Long 2021-01-11 13:26:31 -05:00
commit 87a057a3b8
16 changed files with 697 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@ -0,0 +1,9 @@
# Maven
target
pom.xml.versionsBackup
# Eclipse
.project
.classpath
.settings

93
pom.xml Normal file
View File

@ -0,0 +1,93 @@
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.inteligr8</groupId>
<artifactId>bitbucket-api</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>BitBucket API &amp; Utilities</name>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.12</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludes>
<exclude>**/*IT.class</exclude>
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<id>integration-tests</id>
<phase>integration-test</phase>
<goals>
<goal>integration-test</goal>
</goals>
<configuration>
<includes>
<include>**/*IT.class</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<distributionManagement>
<repository>
<id>inteligr8-releases</id>
<name>Inteligr8 Releases</name>
<url>http://repos.yateslong.us/nexus/repository/inteligr8-public</url>
<layout>default</layout>
</repository>
</distributionManagement>
</project>

View File

@ -0,0 +1,104 @@
package com.inteligr8.bitbucket;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.http.HttpResponse;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.inteligr8.bitbucket.http.BaseResponse;
import com.inteligr8.bitbucket.http.PreemptiveAuthInterceptor;
public class ApiGateway {
private final Logger logger = LoggerFactory.getLogger(ApiGateway.class);
private final String baseUrl = "https://api.bitbucket.org/2.0";
private ObjectMapper omapper = new ObjectMapper();
private CredentialsProvider credProvider;
public ApiGateway(CredentialsProvider credProvider) {
this.credProvider = credProvider;
}
public <Response extends BaseResponse> Response get(String uriPath, Map<String, Object> paramMap, Class<Response> responseType) throws IOException {
return this.execute(HttpGet.METHOD_NAME, uriPath, paramMap, null, responseType);
}
public <Request, Response extends BaseResponse> Response post(String uriPath, Request requestObject, Class<Response> responseType) throws IOException {
return this.execute(HttpPost.METHOD_NAME, uriPath, null, requestObject, responseType);
}
public <Request, Response extends BaseResponse> Response put(String uriPath, Request requestObject, Class<Response> responseType) throws IOException {
return this.execute(HttpPost.METHOD_NAME, uriPath, null, requestObject, responseType);
}
public <Response extends BaseResponse> Response delete(String uriPath, Map<String, Object> paramMap, Class<Response> responseType) throws IOException {
return this.execute(HttpDelete.METHOD_NAME, uriPath, paramMap, null, responseType);
}
private <Request, Response extends BaseResponse> Response execute(String method, String uriPath, Map<String, Object> paramMap, Request requestObject, Class<Response> responseType) throws IOException {
if (this.logger.isTraceEnabled())
this.logger.trace("execute('" + method + "', '" + uriPath + "')");
RequestBuilder builder = RequestBuilder
.create(method)
.setUri(this.baseUrl + uriPath);
if (paramMap != null) {
for (Entry<String, Object> param : paramMap.entrySet())
if (param.getValue() != null)
builder.addParameter(param.getKey(), param.getValue().toString());
}
if (requestObject != null) {
String requestJson = this.omapper.writeValueAsString(requestObject);
if (this.logger.isTraceEnabled())
this.logger.trace("execute('" + method + "', '" + uriPath + "'): " + requestJson);
builder.setEntity(new StringEntity(requestJson, ContentType.APPLICATION_JSON));
}
HttpUriRequest request = builder.build();
if (this.logger.isDebugEnabled())
this.logger.debug("Prepared request for " + method + " to: " + uriPath);
HttpResponse response = HttpClientBuilder
.create()
.addInterceptorFirst(new PreemptiveAuthInterceptor())
.setDefaultCredentialsProvider(this.credProvider)
.build()
.execute(request);
if (this.logger.isDebugEnabled())
this.logger.debug("Received response from " + method + ": " + response.getStatusLine().getStatusCode());
Response responseObject = null;
if (response.getEntity() != null) {
InputStream istream = response.getEntity().getContent();
try {
responseObject = this.omapper.readerFor(responseType).readValue(istream);
} finally {
istream.close();
}
} else {
responseObject = this.omapper.readerFor(responseType).readValue("{}");
}
responseObject.setHttpStatusCode(response.getStatusLine().getStatusCode());
responseObject.setHttpStatusReason(response.getStatusLine().getReasonPhrase());
return responseObject;
}
}

View File

@ -0,0 +1,49 @@
package com.inteligr8.bitbucket.http;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.inteligr8.bitbucket.model.Error;
@JsonIgnoreProperties(ignoreUnknown = true)
public class BaseResponse {
private int httpStatusCode;
private String httpStatusReason;
private String type;
private Error error;
public int getHttpStatusCode() {
return this.httpStatusCode;
}
public void setHttpStatusCode(int httpStatusCode) {
this.httpStatusCode = httpStatusCode;
}
public String getHttpStatusReason() {
return this.httpStatusReason;
}
public void setHttpStatusReason(String httpStatusReason) {
this.httpStatusReason = httpStatusReason;
}
public String getType() {
return this.type;
}
public void setType(String type) {
this.type = type;
}
public Error getError() {
if (this.error == null)
this.error = new Error();
return this.error;
}
@JsonIgnoreProperties(ignoreUnknown = true)
public class Data {
}
}

View File

@ -0,0 +1,37 @@
package com.inteligr8.bitbucket.http;
import java.io.IOException;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.AuthState;
import org.apache.http.auth.Credentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpCoreContext;
public class PreemptiveAuthInterceptor implements HttpRequestInterceptor {
public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState) context.getAttribute(HttpClientContext.TARGET_AUTH_STATE);
// If no auth scheme available yet, try to initialize it
// preemptively
if (authState.getAuthScheme() == null) {
CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(HttpClientContext.CREDS_PROVIDER);
HttpHost targetHost = (HttpHost) context.getAttribute(HttpCoreContext.HTTP_TARGET_HOST);
Credentials creds = credsProvider.getCredentials(new AuthScope(targetHost.getHostName(), targetHost.getPort()));
if (creds == null) {
throw new HttpException("No credentials for preemptive authentication");
}
authState.update(new BasicScheme(), creds);
}
}
}

View File

@ -0,0 +1,23 @@
package com.inteligr8.bitbucket.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@JsonInclude(value = Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true)
public class Branch {
@JsonProperty(required = true)
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,63 @@
package com.inteligr8.bitbucket.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.bitbucket.http.BaseResponse;
public class CreateBranch {
private CreateBranch() {
}
public static String constructRequestPath(String repoName) {
return "/repositories/" + repoName + "/" + httpPath;
}
public static String httpPath = "refs/branches";
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Request {
@JsonProperty(required = true)
private String name;
@JsonProperty(required = true)
private RequestTarget target;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public RequestTarget getTarget() {
if (this.target == null)
this.target = new RequestTarget();
return this.target;
}
@JsonIgnoreProperties(ignoreUnknown = true)
public class RequestTarget {
@JsonProperty(required = true)
private String hash;
public String getHash() {
return this.hash;
}
public void setHash(String hash) {
this.hash = hash;
}
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Response extends BaseResponse {
}
}

View File

@ -0,0 +1,103 @@
package com.inteligr8.bitbucket.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.inteligr8.bitbucket.http.BaseResponse;
public class CreatePullRequest {
private CreatePullRequest() {
}
public static String constructRequestPath(String repoName) {
return "/repositories/" + repoName + "/" + httpPath;
}
public static String httpPath = "pullrequests";
@JsonInclude(value = Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Request {
@JsonProperty(required = true)
private String title;
private String description;
@JsonProperty(required = true)
private Repository source;
private Repository destination;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public Repository getSource() {
if (this.source == null)
this.source = new Repository();
return this.source;
}
public Repository getDestination() {
if (this.destination == null)
this.destination = new Repository();
return this.destination;
}
}
@JsonIgnoreProperties(ignoreUnknown = true)
public static class Response extends BaseResponse {
private int id;
private String title;
private State state;
private Links links;
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
public State getState() {
return this.state;
}
public void setState(State state) {
this.state = state;
}
public Links getLinks() {
if (this.links == null)
this.links = new Links();
return this.links;
}
public enum State {MERGED, SUPERSEDED, OPEN, DECLINED};
}
}

View File

@ -0,0 +1,14 @@
package com.inteligr8.bitbucket.model;
public class DeleteBranch {
private DeleteBranch() {
}
public static String constructRequestPath(String repoName, String branchName) {
return "/repositories/" + repoName + "/" + httpPath + "/" + branchName;
}
public static String httpPath = "refs/branches";
}

View File

@ -0,0 +1,29 @@
package com.inteligr8.bitbucket.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true)
public class Error {
@JsonProperty(required = true)
private String message;
private String detail;
public String getMessage() {
return this.message;
}
public void setMessage(String message) {
this.message = message;
}
public String getDetail() {
return this.detail;
}
public void setDetail(String detail) {
this.detail = detail;
}
}

View File

@ -0,0 +1,33 @@
package com.inteligr8.bitbucket.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@JsonInclude(value = Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true)
public class Link {
@JsonProperty(required = true)
private String name;
@JsonProperty(required = true)
private String href;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getHref() {
return this.href;
}
public void setHref(String href) {
this.href = href;
}
}

View File

@ -0,0 +1,26 @@
package com.inteligr8.bitbucket.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@JsonInclude(value = Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true)
public class Links {
private Link self;
private Link html;
public Link getSelf() {
if (this.self == null)
this.self = new Link();
return this.self;
}
public Link getHtml() {
if (this.html == null)
this.html = new Link();
return this.html;
}
}

View File

@ -0,0 +1,21 @@
package com.inteligr8.bitbucket.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@JsonInclude(value = Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true)
public class Repository {
@JsonProperty(required = true)
private Branch branch;
public Branch getBranch() {
if (this.branch == null)
this.branch = new Branch();
return this.branch;
}
}

View File

@ -0,0 +1,41 @@
package com.inteligr8.bitbucket.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@JsonInclude(value = Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true)
public class TextBox {
private String raw;
private Markup markup;
private String html;
public String getRaw() {
return this.raw;
}
public void setRaw(String raw) {
this.raw = raw;
}
public Markup getMarkup() {
return this.markup;
}
public void setMarkup(Markup markup) {
this.markup = markup;
}
public String getHtml() {
return this.html;
}
public void setHtml(String html) {
this.html = html;
}
public enum Markup {markdown, creole, plaintext};
}

View File

@ -0,0 +1,33 @@
package com.inteligr8.bitbucket.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@JsonInclude(value = Include.NON_EMPTY)
@JsonIgnoreProperties(ignoreUnknown = true)
public class TextBoxes {
private TextBox title;
private TextBox description;
private TextBox reason;
public TextBox getTitle() {
if (this.title == null)
this.title = new TextBox();
return this.title;
}
public TextBox getDescription() {
if (this.description == null)
this.description = new TextBox();
return this.description;
}
public TextBox getReason() {
if (this.reason == null)
this.reason = new TextBox();
return this.reason;
}
}

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="[%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="me.brianlong" level="trace" additivity="false">
<AppenderRef ref="STDOUT" />
</Logger>
<Logger name="org.apache.http" level="trace" additivity="false">
<AppenderRef ref="STDOUT" />
</Logger>
<Root level="info">
<AppenderRef ref="STDOUT"/>
</Root>
</Loggers>
</Configuration>