Skip to content

Commit

Permalink
Prepare for 3.0.0 release
Browse files Browse the repository at this point in the history
Use Apache Http Client for http calls, as PATCH is supported.
Add new instruments endpoints support.
  • Loading branch information
harry-peirse committed Sep 27, 2020
1 parent 67a891a commit deb16b6
Show file tree
Hide file tree
Showing 16 changed files with 325 additions and 137 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ targetCompatibility = 1.8
dependencies {
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'org.slf4j:slf4j-api:1.7.26'
implementation 'org.apache.httpcomponents:httpclient:4.5.12'

testCompile 'junit:junit:4.12'
testCompile 'org.slf4j:slf4j-simple:1.7.26'
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
group=com.checkout
version=2.4.0-SNAPSHOT
version=3.0.0-SNAPSHOT

maven2_url=https://oss.sonatype.org/service/local/staging/deploy/maven2/
snapshot_url=https://oss.sonatype.org/content/repositories/snapshots/
Expand Down
122 changes: 122 additions & 0 deletions src/main/java/com/checkout/ApacheHttpClientTransport.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package com.checkout;

import com.checkout.common.CheckoutUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.Header;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.*;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

@Slf4j
public class ApacheHttpClientTransport implements Transport {

private final URI baseUri;
private final CloseableHttpClient httpClient;

public ApacheHttpClientTransport(String baseUri, HttpClientBuilder httpClientBuilder) {
this.baseUri = URI.create(baseUri);
if (httpClientBuilder != null) {
httpClient = httpClientBuilder.build();
} else {
httpClient = HttpClients.createDefault();
}
}

@Override
public CompletableFuture<Response> invoke(String httpMethod, String path, ApiCredentials apiCredentials, String jsonRequest, String idempotencyKey) {
return CompletableFuture.supplyAsync(() -> {
CloseableHttpResponse response = null;
HttpUriRequest request = null;
switch (httpMethod) {
case "GET":
request = new HttpGet(getRequestUrl(path));
break;
case "PUT":
request = new HttpPut(getRequestUrl(path));
break;
case "POST":
request = new HttpPost(getRequestUrl(path));
break;
case "DELETE":
request = new HttpDelete(getRequestUrl(path));
break;
case "PATCH":
request = new HttpPatch(getRequestUrl(path));
break;
default:
throw new UnsupportedOperationException("Unsupported HTTP Method: " + httpMethod);
}
request.setHeader("user-agent", "checkout-sdk-java/" + CheckoutUtils.getVersionFromManifest());
request.setHeader("Accept", "application/json;charset=UTF-8");
request.setHeader("Content-Type", "application/json;charset=UTF-8");
request.setHeader("Authorization", apiCredentials.getAuthorizationHeader());
if (idempotencyKey != null) {
request.setHeader("Cko-Idempotency-Key", idempotencyKey);
}

log.info("Request: " + Arrays.toString(request.getAllHeaders()));

try {
if (jsonRequest != null && request instanceof HttpEntityEnclosingRequest) {
((HttpEntityEnclosingRequestBase) request).setEntity(new StringEntity(jsonRequest, ContentType.APPLICATION_JSON));
}

log.info(httpMethod + " " + request.getURI());

response = httpClient.execute(request);

log.info("Response: " + Arrays.toString(response.getAllHeaders()));

final int statusCode = response.getStatusLine().getStatusCode();
final String requestId = Optional.ofNullable(response.getFirstHeader("Cko-Request-Id"))
.map(Header::getValue).orElse("NO_REQUEST_ID_SUPPLIED");

if (statusCode != 404) {
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()))) {
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
stringBuilder.append(line).append("\n");
}
String jsonBody = stringBuilder.toString();
return new Response(statusCode, jsonBody, requestId);
}
} else {
return new Response(statusCode, null, requestId);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (response != null) {
try {
response.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
});
}

private String getRequestUrl(String path) {
try {
return baseUri.resolve(path).toURL().toString();
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
}
2 changes: 2 additions & 0 deletions src/main/java/com/checkout/ApiClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,7 @@ public interface ApiClient {

<T> CompletableFuture<T> postAsync(String path, ApiCredentials credentials, Class<T> responseType, Object request, String idempotencyKey);

<T> CompletableFuture<T> patchAsync(String path, ApiCredentials credentials, Class<T> responseType, Object request, String idempotencyKey);

CompletableFuture<? extends Resource> postAsync(String path, ApiCredentials credentials, Map<Integer, Class<? extends Resource>> resultTypeMappings, Object request, String idempotencyKey);
}
14 changes: 13 additions & 1 deletion src/main/java/com/checkout/ApiClientImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class ApiClientImpl implements ApiClient {
private final Transport transport;

public ApiClientImpl(CheckoutConfiguration configuration) {
this(new GsonSerializer(), new HttpUrlConnectionTransport(configuration.getUri(), configuration.getConnectionTimeout()));
this(new GsonSerializer(), new ApacheHttpClientTransport(configuration.getUri(), configuration.getApacheHttpClientBuilder()));
}

public ApiClientImpl(Serializer serializer, Transport transport) {
Expand Down Expand Up @@ -78,6 +78,18 @@ public <T> CompletableFuture<T> postAsync(String path, ApiCredentials credential
return sendRequestAsync("POST", path, credentials, request, idempotencyKey, responseType);
}

@Override
public <T> CompletableFuture<T> patchAsync(String path, ApiCredentials credentials, Class<T> responseType, Object request, String idempotencyKey) {
if (CheckoutUtils.isNullOrEmpty(path)) {
throw new IllegalArgumentException("path must not be null or blank");
}
if (credentials == null) {
throw new IllegalArgumentException("credentials must not be null");
}

return sendRequestAsync("PATCH", path, credentials, request, idempotencyKey, responseType);
}

@Override
public CompletableFuture<? extends Resource> postAsync(String path, ApiCredentials credentials, Map<Integer, Class<? extends Resource>> resultTypeMappings, Object request, String idempotencyKey) {
if (CheckoutUtils.isNullOrEmpty(path)) {
Expand Down
24 changes: 6 additions & 18 deletions src/main/java/com/checkout/CheckoutConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.checkout.common.CheckoutUtils;
import lombok.Data;
import org.apache.http.impl.client.HttpClientBuilder;

@Data
public class CheckoutConfiguration {
Expand All @@ -11,8 +12,11 @@ public class CheckoutConfiguration {
private final String secretKey;
private final String uri;

private int connectionTimeout = 60000; // default to 60 seconds
private String publicKey = null; // no public key required for production
// no public key required for production
private String publicKey = null;

// optionally exposing to allow client configuration of timeouts etc
private HttpClientBuilder apacheHttpClientBuilder = null;

public CheckoutConfiguration(String secretKey, boolean useSandbox) {
this(secretKey, useSandbox ? SANDBOX_URI : PRODUCTION_URI);
Expand All @@ -38,20 +42,4 @@ public CheckoutConfiguration(String secretKey, String uri, String publicKey) {
this(secretKey, uri);
this.publicKey = publicKey;
}

public int getConnectionTimeout() {
return connectionTimeout;
}

public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}

public String getPublicKey() {
return publicKey;
}

public void setPublicKey(String publicKey) {
this.publicKey = publicKey;
}
}
115 changes: 0 additions & 115 deletions src/main/java/com/checkout/HttpUrlConnectionTransport.java

This file was deleted.

17 changes: 17 additions & 0 deletions src/main/java/com/checkout/instruments/AccountHolder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.checkout.instruments;

import com.checkout.common.Address;
import com.checkout.common.Phone;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AccountHolder {
private Address billingAddress;
private Phone phone;
}
17 changes: 17 additions & 0 deletions src/main/java/com/checkout/instruments/CustomerRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.checkout.instruments;

import com.google.gson.annotations.SerializedName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CustomerRequest {
private String id;
@SerializedName("default")
private boolean isDefault;
}
13 changes: 13 additions & 0 deletions src/main/java/com/checkout/instruments/CustomerResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.checkout.instruments;

import com.google.gson.annotations.SerializedName;
import lombok.Data;

@Data
public class CustomerResponse {
private String id;
private String email;
private String name;
@SerializedName("default")
private boolean isDefault;
}
Loading

0 comments on commit deb16b6

Please sign in to comment.