Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
Matt Thomson committed Jul 1, 2015
2 parents 4b12662 + 24e4ab6 commit e63c805
Show file tree
Hide file tree
Showing 52 changed files with 1,410 additions and 1,029 deletions.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ With Maven:
<dependency>
<groupId>com.gocardless</groupId>
<artifactId>gocardless-pro</artifactId>
<version>0.2.4</version>
<version>0.3.0</version>
</dependency>
```

With Gradle:

```
compile 'com.gocardless:gocardless-pro:0.2.4'
compile 'com.gocardless:gocardless-pro:0.3.0'
```

## Initializing the client
Expand Down Expand Up @@ -107,6 +107,10 @@ See the [documentation](http://gocardless.github.io/gocardless-pro-java/com/goca

This library requires JDK version 7 or above.

## Logging

All requests are logged at `INFO` level using [SLF4J](http://www.slf4j.org/). Logs will only be sent if you have an SLF4J binding on your classpath - we recommend using [Logback](http://logback.qos.ch/).

## Documentation

Full Javadoc can be found [here](http://gocardless.github.io/gocardless-pro-java/com/gocardless/package-summary.html).
Expand Down
7 changes: 4 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ apply plugin: 'signing'

sourceCompatibility = 1.7
group = ' com.gocardless'
version = '0.2.4'
version = '0.3.0'

repositories {
mavenCentral()
Expand All @@ -15,13 +15,14 @@ configurations {
}

dependencies {
compile 'com.squareup.okhttp:okhttp:2.3.0'
compile 'com.squareup.okhttp:okhttp:2.4.0'
compile 'com.google.code.gson:gson:2.3.1'
compile 'com.google.guava:guava:18.0'
compile 'org.slf4j:slf4j-api:1.7.12'

testCompile 'junit:junit:4.12'
testCompile 'org.assertj:assertj-core:2.0.0'
testCompile 'com.xebialabs.restito:restito:0.5'
testCompile 'com.squareup.okhttp:mockwebserver:2.4.0'

testCompile 'co.freeside:betamax:1.1.2'
testCompile 'org.codehaus.groovy:groovy-all:2.2.0'
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/com/gocardless/GoCardlessClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public class GoCardlessClient {
private final EventService events;
private final HelperService helpers;
private final MandateService mandates;
private final MandatePdfService mandatePdfs;
private final ModulusCheckService modulusChecks;
private final PaymentService payments;
private final PayoutService payouts;
private final RedirectFlowService redirectFlows;
Expand All @@ -32,6 +34,8 @@ private GoCardlessClient(HttpClient httpClient) {
this.events = new EventService(httpClient);
this.helpers = new HelperService(httpClient);
this.mandates = new MandateService(httpClient);
this.mandatePdfs = new MandatePdfService(httpClient);
this.modulusChecks = new ModulusCheckService(httpClient);
this.payments = new PaymentService(httpClient);
this.payouts = new PayoutService(httpClient);
this.redirectFlows = new RedirectFlowService(httpClient);
Expand Down Expand Up @@ -88,6 +92,20 @@ public MandateService mandates() {
return mandates;
}

/**
* A service class for working with mandate pdf resources.
*/
public MandatePdfService mandatePdfs() {
return mandatePdfs;
}

/**
* A service class for working with modulus check resources.
*/
public ModulusCheckService modulusChecks() {
return modulusChecks;
}

/**
* A service class for working with payment resources.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
package com.gocardless.http;

import java.io.InputStream;
import java.io.Reader;
import java.net.URL;
import java.util.Map;

import com.google.common.collect.ImmutableMap;

/**
* Base class for HTTP requests.
* Base class for API requests.
*
* @param <T> the type of the item returned by this request.
*/
abstract class HttpRequest<T> {
abstract class ApiRequest<T> {
private transient final HttpClient httpClient;

HttpRequest(HttpClient httpClient) {
ApiRequest(HttpClient httpClient) {
this.httpClient = httpClient;
}

Expand Down Expand Up @@ -46,5 +46,5 @@ protected String getRequestEnvelope() {

protected abstract boolean hasBody();

protected abstract T parseResponse(InputStream stream, ResponseParser responseParser);
protected abstract T parseResponse(Reader stream, ResponseParser responseParser);
}
44 changes: 44 additions & 0 deletions src/main/java/com/gocardless/http/ApiResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.gocardless.http;

import java.util.List;
import java.util.Map;

import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Multimap;

/**
* Base class for API responses.
*
* @param <T> the type of the resource within this response.
*/
public class ApiResponse<T> {
private final T resource;
private final int statusCode;
private final Multimap<String, String> headers;

public ApiResponse(T resource, int statusCode, Map<String, List<String>> headers) {
this.resource = resource;
this.statusCode = statusCode;
this.headers = buildHeaderMap(headers);
}

public T getResource() {
return resource;
}

public int getStatusCode() {
return statusCode;
}

public Multimap<String, String> getHeaders() {
return headers;
}

private static Multimap<String, String> buildHeaderMap(Map<String, List<String>> headers) {
ImmutableListMultimap.Builder<String, String> builder = ImmutableListMultimap.builder();
for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
builder.putAll(entry.getKey(), entry.getValue());
}
return builder.build();
}
}
51 changes: 19 additions & 32 deletions src/main/java/com/gocardless/http/GetRequest.java
Original file line number Diff line number Diff line change
@@ -1,33 +1,42 @@
package com.gocardless.http;

import java.io.InputStream;
import java.io.Reader;

/**
* Base class for GET requests that return a single item.
*
* @param <T> the type of the item returned by this request.
*/
public abstract class GetRequest<S, T> extends HttpRequest<T> {
private final GetRequestExecutor<S, T> executor;

protected GetRequest(HttpClient httpClient, GetRequestExecutor<S, T> executor) {
public abstract class GetRequest<T> extends ApiRequest<T> {
protected GetRequest(HttpClient httpClient) {
super(httpClient);
this.executor = executor;
}

/**
* Executes this request.
*
* Returns the API response.
* Returns the response entity.
*
* @throws com.gocardless.GoCardlessException
*/
public S execute() {
return executor.execute(this, getHttpClient());
public T execute() {
return getHttpClient().execute(this);
}

/**
* Executes this request.
*
* Returns a {@link com.gocardless.http.ApiResponse} that wraps the
* response entity.
*
* @throws com.gocardless.GoCardlessException
*/
public ApiResponse<T> executeWrapped() {
return getHttpClient().executeWrapped(this);
}

@Override
protected T parseResponse(InputStream stream, ResponseParser responseParser) {
protected T parseResponse(Reader stream, ResponseParser responseParser) {
return responseParser.parseSingle(stream, getEnvelope(), getResponseClass());
}

Expand All @@ -42,26 +51,4 @@ protected final boolean hasBody() {
}

protected abstract Class<T> getResponseClass();

public interface GetRequestExecutor<S, T> {
S execute(GetRequest<S, T> request, HttpClient client);
}

public static <T> GetRequestExecutor<T, T> jsonExecutor() {
return new GetRequestExecutor<T, T>() {
@Override
public T execute(GetRequest<T, T> request, HttpClient client) {
return client.execute(request);
}
};
}

public static <T> GetRequestExecutor<InputStream, T> downloadExecutor(final String contentType) {
return new GetRequestExecutor<InputStream, T>() {
@Override
public InputStream execute(GetRequest<InputStream, T> request, HttpClient client) {
return client.rawExecute(request, contentType);
}
};
}
}
76 changes: 46 additions & 30 deletions src/main/java/com/gocardless/http/HttpClient.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.gocardless.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.net.URL;
import java.util.Map;

Expand All @@ -14,16 +14,17 @@
import com.squareup.okhttp.*;

/**
* An HTTP client that can execute {@link HttpRequest}s.
* An HTTP client that can execute {@link ApiRequest}s.
*
* Users of this library should not need to access this class directly.
*/
public class HttpClient {
private static final String USER_AGENT = String.format("gocardless-pro/0.2.4 %s/%s %s/%s",
private static final String USER_AGENT = String.format("gocardless-pro/0.3.0 %s/%s %s/%s",
replaceSpaces(System.getProperty("os.name")),
replaceSpaces(System.getProperty("os.version")),
replaceSpaces(System.getProperty("java.vm.name")),
replaceSpaces(System.getProperty("java.version")));
private static final RequestBody EMPTY_BODY = RequestBody.create(null, new byte[0]);
private static final MediaType MEDIA_TYPE = MediaType.parse("application/json");
private static final Map<String, String> HEADERS;
static {
Expand All @@ -45,59 +46,74 @@ public class HttpClient {
*/
public HttpClient(String accessToken, String baseUrl) {
this.rawClient = new OkHttpClient();
rawClient.interceptors().add(new LoggingInterceptor());
this.urlFormatter = new UrlFormatter(baseUrl);
Gson gson = GsonFactory.build();
this.responseParser = new ResponseParser(gson);
this.requestWriter = new RequestWriter(gson);
this.credentials = String.format("Bearer %s", accessToken);
}

InputStream rawExecute(HttpRequest<?> request, String accept) {
URL url = request.getUrl(urlFormatter);
Request.Builder httpRequest =
new Request.Builder().url(url).header("Accept", accept)
.header("Authorization", credentials).header("User-Agent", USER_AGENT)
.method(request.getMethod(), getBody(request));
for (Map.Entry<String, String> entry : HEADERS.entrySet()) {
httpRequest = httpRequest.header(entry.getKey(), entry.getValue());
}
Response response = execute(httpRequest.build());
if (!response.isSuccessful()) {
throw handleErrorResponse(response);
}
try {
return response.body().byteStream();
} catch (IOException e) {
throw new GoCardlessNetworkException("Failed to read response body", e);
}
<T> T execute(ApiRequest<T> apiRequest) {
Request request = buildRequest(apiRequest);
Response response = execute(request);
return parseResponseBody(apiRequest, response);
}

<T> T execute(HttpRequest<T> request) {
try (InputStream stream = rawExecute(request, MEDIA_TYPE.toString())) {
return request.parseResponse(stream, responseParser);
} catch (IOException e) {
throw new GoCardlessNetworkException("Failed to read response body", e);
<T> ApiResponse<T> executeWrapped(ApiRequest<T> apiRequest) {
Request request = buildRequest(apiRequest);
Response response = execute(request);
T resource = parseResponseBody(apiRequest, response);
return new ApiResponse<T>(resource, response.code(), response.headers().toMultimap());
}

<T> Request buildRequest(ApiRequest<T> apiRequest) {
URL url = apiRequest.getUrl(urlFormatter);
Request.Builder request =
new Request.Builder().url(url).header("Authorization", credentials)
.header("User-Agent", USER_AGENT)
.method(apiRequest.getMethod(), getBody(apiRequest));
for (Map.Entry<String, String> entry : HEADERS.entrySet()) {
request = request.header(entry.getKey(), entry.getValue());
}
return request.build();
}

private <T> RequestBody getBody(HttpRequest<T> request) {
private <T> RequestBody getBody(ApiRequest<T> request) {
if (!request.hasBody()) {
return null;
if (request.getMethod().equals("GET")) {
return null;
} else {
return EMPTY_BODY;
}
}
String json = requestWriter.write(request, request.getRequestEnvelope());
return RequestBody.create(MEDIA_TYPE, json);
}

private Response execute(Request request) {
Response response;
try {
return rawClient.newCall(request).execute();
response = rawClient.newCall(request).execute();
} catch (IOException e) {
throw new GoCardlessNetworkException("Failed to execute request", e);
}
if (!response.isSuccessful()) {
throw handleErrorResponse(response);
}
return response;
}

private <T> T parseResponseBody(ApiRequest<T> request, Response response) {
try (Reader stream = response.body().charStream()) {
return request.parseResponse(stream, responseParser);
} catch (IOException e) {
throw new GoCardlessNetworkException("Failed to read response body", e);
}
}

private GoCardlessException handleErrorResponse(Response response) {
try (InputStream stream = response.body().byteStream()) {
try (Reader stream = response.body().charStream()) {
return responseParser.parseError(stream);
} catch (IOException e) {
throw new GoCardlessNetworkException("Failed to read response body", e);
Expand Down
Loading

0 comments on commit e63c805

Please sign in to comment.