Skip to content

Commit

Permalink
Merge branch 'release/1.6.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
rjdavis3 committed Oct 7, 2019
2 parents 711028b + aed2d55 commit 08ecece
Show file tree
Hide file tree
Showing 11 changed files with 1,966 additions and 1,300 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
language: java
sudo: false
install: true
dist: trusty
addons:
sonarcloud:
organization: channelape-github
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.channelape</groupId>
<artifactId>shopify-sdk</artifactId>
<version>1.5.0</version>
<version>1.6.0</version>

<name>Shopify SDK</name>
<description>Java SDK for Shopify REST API.</description>
Expand Down
107 changes: 85 additions & 22 deletions src/main/java/com/shopify/ShopifySdk.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@
import com.shopify.model.ShopifyCustomerRoot;
import com.shopify.model.ShopifyCustomerUpdateRequest;
import com.shopify.model.ShopifyCustomerUpdateRoot;
import com.shopify.model.ShopifyCustomersRoot;
import com.shopify.model.ShopifyFulfillment;
import com.shopify.model.ShopifyFulfillmentCreationRequest;
import com.shopify.model.ShopifyFulfillmentRoot;
import com.shopify.model.ShopifyFulfillmentUpdateRequest;
import com.shopify.model.ShopifyGetCustomersRequest;
import com.shopify.model.ShopifyGiftCard;
import com.shopify.model.ShopifyGiftCardCreationRequest;
import com.shopify.model.ShopifyGiftCardRoot;
Expand Down Expand Up @@ -92,6 +94,10 @@

public class ShopifySdk {

private static final String EMPTY_STRING = "";

static final String RETRY_AFTER_HEADER = "Retry-After";

private static final String MINIMUM_REQUEST_RETRY_DELAY_CANNOT_BE_LARGER_THAN_MAXIMUM_REQUEST_RETRY_DELAY_MESSAGE = "Maximum request retry delay must be larger than minimum request retry delay.";

private static final String INVALID_MAXIMUM_REQUEST_RETRY_TIMEOUT_MESSAGE = "Maximum request retry timeout cannot be set lower than 2 seconds.";
Expand Down Expand Up @@ -131,7 +137,12 @@ public class ShopifySdk {
static final String ANY_STATUSES = "any";
static final String CREATED_AT_MIN_QUERY_PARAMETER = "created_at_min";
static final String CREATED_AT_MAX_QUERY_PARAMETER = "created_at_max";
static final String UPDATED_AT_MIN_QUERY_PARAMETER = "updated_at_min";
static final String UPDATED_AT_MAX_QUERY_PARAMETER = "updated_at_max";
static final String ATTRIBUTION_APP_ID_QUERY_PARAMETER = "attribution_app_id";
static final String IDS_QUERY_PARAMETER = "ids";
static final String SINCE_ID_QUERY_PARAMETER = "since_id";
static final String QUERY_QUERY_PARAMETER = "query";
static final String CALCULATE = "calculate";
static final String REFUNDS = "refunds";
static final String TRANSACTIONS = "transactions";
Expand Down Expand Up @@ -171,19 +182,20 @@ public class ShopifySdk {
private long minimumRequestRetryRandomDelayMilliseconds;
private long maximumRequestRetryRandomDelayMilliseconds;
private long maximumRequestRetryTimeoutMilliseconds;
private final ShopifySdkRetryListener shopifySdkRetryListener = new ShopifySdkRetryListener();

private static final Client CLIENT = buildClient();

private static final String CUSTOMERS = "customers";
private static final String SEARCH = "search";

public static interface OptionalsStep {

/**
* The Shopify SDK uses random waits in between retry attempts. Minimum duration
* time to wait before retrying a failed request. Value must also be less than
* {@link #withMaximumRequestRetryRandomDelay(int, TimeUnit) Maximum Request
* Retry Random Delay}.<br>
* The Shopify SDK uses random waits in between retry attempts. Minimum
* duration time to wait before retrying a failed request. Value must
* also be less than
* {@link #withMaximumRequestRetryRandomDelay(int, TimeUnit) Maximum
* Request Retry Random Delay}.<br>
* Default value is: 1 second.
*
* @param duration
Expand All @@ -193,10 +205,11 @@ public static interface OptionalsStep {
OptionalsStep withMinimumRequestRetryRandomDelay(int duration, TimeUnit timeUnit);

/**
* The Shopify SDK uses random waits in between retry attempts. Maximum duration
* time to wait before retrying a failed request. Value must also be more than
* {@link #withMinimumRequestRetryRandomDelay(int, TimeUnit) Minimum Request
* Retry Random Delay}.<br>
* The Shopify SDK uses random waits in between retry attempts. Maximum
* duration time to wait before retrying a failed request. Value must
* also be more than
* {@link #withMinimumRequestRetryRandomDelay(int, TimeUnit) Minimum
* Request Retry Random Delay}.<br>
* Default value is: 5 seconds.
*
* @param duration
Expand Down Expand Up @@ -569,6 +582,18 @@ public List<ShopifyOrder> getOrders(final DateTime mininumCreationDate, final Da
return getOrders(mininumCreationDate, maximumCreationDate, page, DEFAULT_REQUEST_LIMIT);
}

public List<ShopifyOrder> getUpdatedOrdersCreatedBefore(final DateTime minimumUpdatedAtDate,
final DateTime maximumUpdatedAtDate, final DateTime maximumCreatedAtDate, final int page,
final int pageSize) {
final Response response = get(getWebTarget().path(ORDERS).queryParam(STATUS_QUERY_PARAMETER, ANY_STATUSES)
.queryParam(LIMIT_QUERY_PARAMETER, pageSize)
.queryParam(UPDATED_AT_MIN_QUERY_PARAMETER, minimumUpdatedAtDate.toString())
.queryParam(UPDATED_AT_MAX_QUERY_PARAMETER, maximumUpdatedAtDate.toString())
.queryParam(CREATED_AT_MAX_QUERY_PARAMETER, maximumCreatedAtDate.toString())
.queryParam(PAGE_QUERY_PARAMETER, page));
return getOrders(response);
}

public List<ShopifyOrder> getOrders(final DateTime mininumCreationDate, final DateTime maximumCreationDate,
final int page, final int pageSize) {
final Response response = get(getWebTarget().path(ORDERS).queryParam(STATUS_QUERY_PARAMETER, ANY_STATUSES)
Expand Down Expand Up @@ -645,6 +670,44 @@ public ShopifyCustomer updateCustomer(final ShopifyCustomerUpdateRequest shopify
return shopifyCustomerRootResponse.getCustomer();
}

public ShopifyCustomer getCustomer(final String customerId) {
final Response response = get(getWebTarget().path(CUSTOMERS).path(customerId));
final ShopifyCustomerRoot shopifyCustomerRootResponse = response.readEntity(ShopifyCustomerRoot.class);
return shopifyCustomerRootResponse.getCustomer();
}

public List<ShopifyCustomer> getCustomers(final ShopifyGetCustomersRequest shopifyGetCustomersRequest) {
WebTarget target = getWebTarget().path(CUSTOMERS);
if (shopifyGetCustomersRequest.getPage() != 0) {
target = target.queryParam(PAGE_QUERY_PARAMETER, shopifyGetCustomersRequest.getPage());
}
if (shopifyGetCustomersRequest.getLimit() != 0) {
target = target.queryParam(LIMIT_QUERY_PARAMETER, shopifyGetCustomersRequest.getLimit());
} else {
target = target.queryParam(LIMIT_QUERY_PARAMETER, DEFAULT_REQUEST_LIMIT);
}
if (shopifyGetCustomersRequest.getIds() != null) {
target = target.queryParam(IDS_QUERY_PARAMETER, String.join(",", shopifyGetCustomersRequest.getIds()));
}
if (shopifyGetCustomersRequest.getSinceId() != null) {
target = target.queryParam(SINCE_ID_QUERY_PARAMETER, shopifyGetCustomersRequest.getSinceId());
}
if (shopifyGetCustomersRequest.getCreatedAtMin() != null) {
target = target.queryParam(CREATED_AT_MIN_QUERY_PARAMETER, shopifyGetCustomersRequest.getCreatedAtMin());
}
if (shopifyGetCustomersRequest.getCreatedAtMax() != null) {
target = target.queryParam(CREATED_AT_MAX_QUERY_PARAMETER, shopifyGetCustomersRequest.getCreatedAtMax());
}
final Response response = get(target);
return getCustomers(response);
}

public List<ShopifyCustomer> searchCustomers(final String query) {
final Response response = get(getWebTarget().path(CUSTOMERS).path(SEARCH)
.queryParam(QUERY_QUERY_PARAMETER, query).queryParam(LIMIT_QUERY_PARAMETER, DEFAULT_REQUEST_LIMIT));
return getCustomers(response);
}

public ShopifyFulfillment cancelFulfillment(final String orderId, final String fulfillmentId) {
final Response response = post(
getWebTarget().path(ORDERS).path(orderId).path(FULFILLMENTS).path(fulfillmentId).path(CANCEL),
Expand Down Expand Up @@ -759,6 +822,11 @@ public String getAccessToken() {
return accessToken;
}

private List<ShopifyCustomer> getCustomers(final Response response) {
final ShopifyCustomersRoot shopifyCustomersRootResponse = response.readEntity(ShopifyCustomersRoot.class);
return shopifyCustomersRootResponse.getCustomers();
}

private ShopifyRefund calculateRefund(final ShopifyRefundCreationRequest shopifyRefundCreationRequest) {
final ShopifyRefundRoot shopifyRefundRoot = new ShopifyRefundRoot();

Expand Down Expand Up @@ -844,7 +912,7 @@ private Response handleResponse(final Response response, final Status... expecte
return response;
}

throw new ShopifyErrorResponseException(response, shopifySdkRetryListener.getResponseBody());
throw new ShopifyErrorResponseException(response);
}

private List<Integer> getExpectedStatusCodes(final Status... expectedStatus) {
Expand All @@ -861,20 +929,21 @@ private Response invokeResponseCallable(final Callable<Response> responseCallabl
}

private Retryer<Response> buildResponseRetyer() {
return RetryerBuilder.<Response>newBuilder().retryIfResult(ShopifySdk::shouldRetryResponse).retryIfException()
return RetryerBuilder.<Response> newBuilder().retryIfResult(ShopifySdk::shouldRetryResponse).retryIfException()
.withWaitStrategy(WaitStrategies.randomWait(minimumRequestRetryRandomDelayMilliseconds,
TimeUnit.MILLISECONDS, maximumRequestRetryRandomDelayMilliseconds, TimeUnit.MILLISECONDS))
.withStopStrategy(
StopStrategies.stopAfterDelay(maximumRequestRetryTimeoutMilliseconds, TimeUnit.MILLISECONDS))
.withRetryListener(shopifySdkRetryListener).build();
.withRetryListener(new ShopifySdkRetryListener()).build();
}

private static boolean shouldRetryResponse(final Response response) {
return isServerError(response) || hasExceededRateLimit(response) || hasNotBeenSaved(response);
}

private static boolean hasExceededRateLimit(final Response response) {
return TOO_MANY_REQUESTS_STATUS_CODE == response.getStatus();
return TOO_MANY_REQUESTS_STATUS_CODE == response.getStatus()
&& response.getHeaders().containsKey(RETRY_AFTER_HEADER);
}

private static boolean isServerError(final Response response) {
Expand All @@ -895,7 +964,7 @@ private static boolean hasNotBeenSaved(final Response response) {
private String generateToken() {
try {

final Entity<String> entity = Entity.entity("", MediaType.APPLICATION_JSON);
final Entity<String> entity = Entity.entity(EMPTY_STRING, MediaType.APPLICATION_JSON);
final Response response = this.webTarget.path(OAUTH).path(ACCESS_TOKEN).queryParam(CLIENT_ID, this.clientId)
.queryParam(CLIENT_SECRET, this.clientSecret)
.queryParam(AUTHORIZATION_CODE, this.authorizationToken).request(MediaType.APPLICATION_JSON)
Expand Down Expand Up @@ -946,23 +1015,21 @@ public class ShopifySdkRetryListener implements RetryListener {
private static final String RETRY_EXCEPTION_ATTEMPT_MESSAGE = "An exception occurred while making an API call to shopify: {} on attempt number {} and {} seconds since first attempt";
private static final String RETRY_INVALID_RESPONSE_ATTEMPT_MESSAGE = "Waited {} seconds since first retry attempt. This is attempt {}. Please review the following failed request information.\nRequest Location of {}\nResponse Status Code of {}\nResponse Headers of:\n{}\nResponse Body of:\n{}";

private String responseBody;

@Override
public <V> void onRetry(final Attempt<V> attempt) {
if (attempt.hasResult()) {
final Response response = (Response) attempt.getResult();

response.bufferEntity();
this.responseBody = response.readEntity(String.class);
final String responseBody = response.readEntity(String.class);

if (LOGGER.isWarnEnabled() && !hasExceededRateLimit(response) && shouldRetryResponse(response)) {

final long delaySinceFirstAttemptInSeconds = convertMillisecondsToSeconds(
attempt.getDelaySinceFirstAttempt());
LOGGER.warn(RETRY_INVALID_RESPONSE_ATTEMPT_MESSAGE, delaySinceFirstAttemptInSeconds,
attempt.getAttemptNumber(), response.getLocation(), response.getStatus(),
response.getStringHeaders(), this.responseBody);
response.getStringHeaders(), responseBody);

}

Expand All @@ -975,10 +1042,6 @@ public <V> void onRetry(final Attempt<V> attempt) {
}
}

public String getResponseBody() {
return responseBody;
}

private long convertMillisecondsToSeconds(final long milliiseconds) {
return TimeUnit.SECONDS.convert(milliiseconds, TimeUnit.MILLISECONDS);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,10 @@ public ShopifyErrorResponseException(final Response response) {
this.statusCode = response.getStatus();
}

public ShopifyErrorResponseException(final Response response, final String responseBody) {
super(buildMessage(response, responseBody));
response.bufferEntity();

this.responseBody = responseBody;
this.shopifyErrorCodes = ShopifyErrorCodeFactory.create(responseBody);
this.statusCode = response.getStatus();
}

private static String buildMessage(final Response response, final String responseBody) {
response.bufferEntity();
return String.format(MESSAGE, response.getStatus(), response.getStringHeaders(), responseBody);
}

private static String buildMessage(final Response response) {
response.bufferEntity();
return String.format(MESSAGE, response.getStatus(), response.getStringHeaders(),
response.readEntity(String.class));
final String readEntity = response.readEntity(String.class);
return String.format(MESSAGE, response.getStatus(), response.getStringHeaders(), readEntity);
}

public int getStatusCode() {
Expand Down
19 changes: 19 additions & 0 deletions src/main/java/com/shopify/model/ShopifyCustomersRoot.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.shopify.model;

import javax.xml.bind.annotation.XmlRootElement;
import java.util.LinkedList;
import java.util.List;

@XmlRootElement
public class ShopifyCustomersRoot {
public List<ShopifyCustomer> getCustomers() {
return customers;
}

public void setCustomers(List<ShopifyCustomer> customers) {
this.customers = customers;
}

private List<ShopifyCustomer> customers = new LinkedList<>();

}
Loading

0 comments on commit 08ecece

Please sign in to comment.