From dd7cd5c9f9ca30bfd8eec24e3dbcb156b4189921 Mon Sep 17 00:00:00 2001 From: Ryan Kazokas Date: Mon, 6 Mar 2023 09:18:01 -0500 Subject: [PATCH] Release 2.6.0 (#101) * migration to fulfillmet order api (2023-04) * javaDocs problem solved * feedback implemented * fulfillment order move response root added * feedback implemented * throwing exception if we cannot create a fulfillment * throwing exception for non supported actions * feedback implemented * Updates release version * Removes duplicate runs of ci during PRs --------- Co-authored-by: Pedro Clericuzzi Co-authored-by: Pedro Clericuzzi --- .github/workflows/maven-feature-hotfix.yml | 6 +- pom.xml | 2 +- src/main/java/com/shopify/ShopifySdk.java | 180 ++++++-- .../ShopifyEmptyLineItemsException.java | 10 + .../ShopifyIncompatibleApiException.java | 16 + .../LegacyToFulfillmentOrderMapping.java | 175 +++++++ .../mappers/ResponseEntityToStringMapper.java | 10 +- .../model/ShopifyAssignedLocation.java | 95 ++++ .../shopify/model/ShopifyDeliveryMethod.java | 125 +++++ .../com/shopify/model/ShopifyDestination.java | 122 +++++ .../ShopifyFulfillmentCreationRequest.java | 2 +- .../shopify/model/ShopifyFulfillmentHold.java | 32 ++ .../model/ShopifyFulfillmentOrder.java | 427 ++++++++++++++++++ .../ShopifyFulfillmentOrderLineItem.java | 90 ++++ ...fyFulfillmentOrderMoveLocationPayload.java | 36 ++ ...hopifyFulfillmentOrderMoveRequestRoot.java | 23 + ...opifyFulfillmentOrderMoveResponseRoot.java | 23 + ...hopifyFulfillmentOrderPayloadLineItem.java | 33 ++ .../model/ShopifyFulfillmentOrderRoot.java | 22 + .../model/ShopifyFulfillmentOrdersRoot.java | 25 + .../model/ShopifyFulfillmentPayload.java | 54 +++ .../model/ShopifyFulfillmentPayloadRoot.java | 20 + .../ShopifyFulfillmentUpdateRequest.java | 2 +- .../model/ShopifyInternationalDuty.java | 60 +++ .../ShopifyLineItemsByFulfillmentOrder.java | 35 ++ .../shopify/model/ShopifyMerchandRequest.java | 71 +++ .../shopify/model/ShopifyRequestOptions.java | 47 ++ .../shopify/model/ShopifyTrackingInfo.java | 38 ++ .../ShopifyUpdateFulfillmentPayload.java | 41 ++ .../ShopifyUpdateFulfillmentPayloadRoot.java | 20 + src/test/java/com/shopify/ShopifySdkTest.java | 253 ++++++++++- 31 files changed, 2051 insertions(+), 44 deletions(-) create mode 100644 src/main/java/com/shopify/exceptions/ShopifyEmptyLineItemsException.java create mode 100644 src/main/java/com/shopify/exceptions/ShopifyIncompatibleApiException.java create mode 100644 src/main/java/com/shopify/mappers/LegacyToFulfillmentOrderMapping.java create mode 100644 src/main/java/com/shopify/model/ShopifyAssignedLocation.java create mode 100644 src/main/java/com/shopify/model/ShopifyDeliveryMethod.java create mode 100644 src/main/java/com/shopify/model/ShopifyDestination.java create mode 100644 src/main/java/com/shopify/model/ShopifyFulfillmentHold.java create mode 100644 src/main/java/com/shopify/model/ShopifyFulfillmentOrder.java create mode 100644 src/main/java/com/shopify/model/ShopifyFulfillmentOrderLineItem.java create mode 100644 src/main/java/com/shopify/model/ShopifyFulfillmentOrderMoveLocationPayload.java create mode 100644 src/main/java/com/shopify/model/ShopifyFulfillmentOrderMoveRequestRoot.java create mode 100644 src/main/java/com/shopify/model/ShopifyFulfillmentOrderMoveResponseRoot.java create mode 100644 src/main/java/com/shopify/model/ShopifyFulfillmentOrderPayloadLineItem.java create mode 100644 src/main/java/com/shopify/model/ShopifyFulfillmentOrderRoot.java create mode 100644 src/main/java/com/shopify/model/ShopifyFulfillmentOrdersRoot.java create mode 100644 src/main/java/com/shopify/model/ShopifyFulfillmentPayload.java create mode 100644 src/main/java/com/shopify/model/ShopifyFulfillmentPayloadRoot.java create mode 100644 src/main/java/com/shopify/model/ShopifyInternationalDuty.java create mode 100644 src/main/java/com/shopify/model/ShopifyLineItemsByFulfillmentOrder.java create mode 100644 src/main/java/com/shopify/model/ShopifyMerchandRequest.java create mode 100644 src/main/java/com/shopify/model/ShopifyRequestOptions.java create mode 100644 src/main/java/com/shopify/model/ShopifyTrackingInfo.java create mode 100644 src/main/java/com/shopify/model/ShopifyUpdateFulfillmentPayload.java create mode 100644 src/main/java/com/shopify/model/ShopifyUpdateFulfillmentPayloadRoot.java diff --git a/.github/workflows/maven-feature-hotfix.yml b/.github/workflows/maven-feature-hotfix.yml index 7020df68..da52fd26 100644 --- a/.github/workflows/maven-feature-hotfix.yml +++ b/.github/workflows/maven-feature-hotfix.yml @@ -5,11 +5,7 @@ name: Shopify SDK Build Feature/Hotfix Branch on: pull_request: - push: - branches: - - '**' - - '!master' - - '!develop' + jobs: build: diff --git a/pom.xml b/pom.xml index 42304a57..cbe6a77d 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 com.channelape shopify-sdk - 2.5.0 + 2.6.0 Shopify SDK Java SDK for Shopify REST API. diff --git a/src/main/java/com/shopify/ShopifySdk.java b/src/main/java/com/shopify/ShopifySdk.java index 86819f30..b8ad0bd8 100644 --- a/src/main/java/com/shopify/ShopifySdk.java +++ b/src/main/java/com/shopify/ShopifySdk.java @@ -37,6 +37,9 @@ import com.github.rholder.retry.WaitStrategies; import com.shopify.exceptions.ShopifyClientException; import com.shopify.exceptions.ShopifyErrorResponseException; +import com.shopify.exceptions.ShopifyIncompatibleApiException; +import com.shopify.exceptions.ShopifyEmptyLineItemsException; +import com.shopify.mappers.LegacyToFulfillmentOrderMapping; import com.shopify.mappers.ResponseEntityToStringMapper; import com.shopify.mappers.ShopifySdkObjectMapper; import com.shopify.model.Count; @@ -59,6 +62,11 @@ import com.shopify.model.ShopifyCustomersRoot; import com.shopify.model.ShopifyFulfillment; import com.shopify.model.ShopifyFulfillmentCreationRequest; +import com.shopify.model.ShopifyFulfillmentOrder; +import com.shopify.model.ShopifyFulfillmentOrderMoveRequestRoot; +import com.shopify.model.ShopifyFulfillmentOrderMoveResponseRoot; +import com.shopify.model.ShopifyFulfillmentOrdersRoot; +import com.shopify.model.ShopifyFulfillmentPayloadRoot; import com.shopify.model.ShopifyFulfillmentRoot; import com.shopify.model.ShopifyFulfillmentUpdateRequest; import com.shopify.model.ShopifyGetCustomersRequest; @@ -96,6 +104,7 @@ import com.shopify.model.ShopifyShop; import com.shopify.model.ShopifyTransaction; import com.shopify.model.ShopifyTransactionsRoot; +import com.shopify.model.ShopifyUpdateFulfillmentPayloadRoot; import com.shopify.model.ShopifyVariant; import com.shopify.model.ShopifyVariantMetafieldCreationRequest; import com.shopify.model.ShopifyVariantRoot; @@ -129,12 +138,15 @@ public class ShopifySdk { static final String RECURRING_APPLICATION_CHARGES = "recurring_application_charges"; static final String ORDERS = "orders"; static final String FULFILLMENTS = "fulfillments"; + static final String FULFILLMENT_ORDERS = "fulfillment_orders"; static final String ACTIVATE = "activate"; static final String IMAGES = "images"; static final String SHOP = "shop"; static final String COUNT = "count"; static final String CLOSE = "close"; static final String CANCEL = "cancel"; + static final String MOVE = "move"; + static final String UPDATE_TRACKING = "update_tracking"; static final String METAFIELDS = "metafields"; static final String RISKS = "risks"; static final String LOCATIONS = "locations"; @@ -199,28 +211,34 @@ public class ShopifySdk { 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}.
+ * 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}.
* Default value is: 1 second. * * @param duration + * of the minimum retry delay * @param timeUnit - * @return {@link OptionalsStep} + * the time unit to be used (miliseconds, seconds, etc...) + * @return {@link OptionalsStep} the OptionalsStep interface */ 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}.
+ * 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}.
* Default value is: 5 seconds. * * @param duration + * of the maximum retry delay * @param timeUnit - * @return {@link OptionalsStep} + * the time unit to be used (miliseconds, seconds, etc...) + * @return {@link OptionalsStep} the OptionalsStep interface */ OptionalsStep withMaximumRequestRetryRandomDelay(int duration, TimeUnit timeUnit); @@ -229,8 +247,10 @@ public static interface OptionalsStep { * Default value is: 3 minutes. * * @param duration + * of the request timeout * @param timeUnit - * @return {@link OptionalsStep} + * the time unit to be used (miliseconds, seconds, etc...) + * @return {@link OptionalsStep} the OptionalsStep interface */ OptionalsStep withMaximumRequestRetryTimeout(int duration, TimeUnit timeUnit); @@ -239,8 +259,10 @@ public static interface OptionalsStep { * Default value is: 1 minute. * * @param duration + * of the connection timeout * @param timeUnit - * @return {@link OptionalsStep} + * the time unit to be used (miliseconds, seconds, etc...) + * @return {@link OptionalsStep} the OptionalsStep interface */ OptionalsStep withConnectionTimeout(int duration, TimeUnit timeUnit); @@ -249,20 +271,23 @@ public static interface OptionalsStep { * Default value is: 15 seconds. * * @param duration + * of the duration to be considered * @param timeUnit - * @return {@link OptionalsStep} + * the time unit to be used (miliseconds, seconds, etc...) + * @return {@link OptionalsStep} the OptionalsStep interface */ OptionalsStep withReadTimeout(int duration, TimeUnit timeUnit); /** - * String representation of the version you want to use. If not populated, this - * will use shopify oldest stable version. Although this is not recommended so - * you can target a set of shopify features. Ex: '2020-10' '2020-07' '2020-04'. - * If you are specifying the API URL ensure you leave off the version if you are - * using this. + * String representation of the version you want to use. If not + * populated, this will use shopify oldest stable version. Although this + * is not recommended so you can target a set of shopify features. Ex: + * '2020-10' '2020-07' '2020-04'. If you are specifying the API URL + * ensure you leave off the version if you are using this. * * @param apiVersion - * @return + * current apiVersion value + * @return {@link OptionalsStep} the OptionalsStep interface */ OptionalsStep withApiVersion(final String apiVersion); @@ -688,24 +713,75 @@ public ShopifyPage getOrders(final String pageInfo, final int page return getOrders(response); } + /** + * Creates a fulfillment in shopify + * + * @deprecated starting in March 01, 2023 this method will be obsolete + * @param shopifyFulfillmentCreationRequest + * the fulfillment creaton request + * @return the newly created fulfillment + */ + @Deprecated public ShopifyFulfillment createFulfillment( final ShopifyFulfillmentCreationRequest shopifyFulfillmentCreationRequest) { + return this.createFulfillmentWithLegacyApi(shopifyFulfillmentCreationRequest); + } + + /** + * Creates a fulfillment in shopify starting with api 2023-04 + * + * @param shopifyFulfillmentCreationRequest + * the fulfillment creaton request + * + * @param fulfillmentOrders + * the fulfillment orders list to create the new fulfillment + * @return the newly created fulfillment + * @throws ShopifyEmptyLineItemsException + * in case we have a fulfillment associated with a + * fulfillmentOrder without supported action + */ + public ShopifyFulfillment createFulfillment( + final ShopifyFulfillmentCreationRequest shopifyFulfillmentCreationRequest, + List fulfillmentOrders) throws ShopifyEmptyLineItemsException { + return this.createFulfillmentWithFulfillmentOrderApi(shopifyFulfillmentCreationRequest, fulfillmentOrders); + } + + /** + * Updates a fulfillment in shopify + * + * @deprecated starting in March 01, 2023 this method will be obsolete + * @param shopifyFulfillmentUpdateRequest + * the fulfillment update request + * @return the newly updated fulfillment + */ + @Deprecated + public ShopifyFulfillment updateFulfillment(final ShopifyFulfillmentUpdateRequest shopifyFulfillmentUpdateRequest) { final ShopifyFulfillmentRoot shopifyFulfillmentRoot = new ShopifyFulfillmentRoot(); - final ShopifyFulfillment shopifyFulfillment = shopifyFulfillmentCreationRequest.getRequest(); - + final ShopifyFulfillment shopifyFulfillment = shopifyFulfillmentUpdateRequest.getRequest(); shopifyFulfillmentRoot.setFulfillment(shopifyFulfillment); - final Response response = post(buildOrdersEndpoint().path(shopifyFulfillment.getOrderId()).path(FULFILLMENTS), - shopifyFulfillmentRoot); + final Response response = put(buildOrdersEndpoint().path(shopifyFulfillment.getOrderId()).path(FULFILLMENTS) + .path(shopifyFulfillment.getId()), shopifyFulfillmentRoot); final ShopifyFulfillmentRoot shopifyFulfillmentRootResponse = response.readEntity(ShopifyFulfillmentRoot.class); return shopifyFulfillmentRootResponse.getFulfillment(); } - public ShopifyFulfillment updateFulfillment(final ShopifyFulfillmentUpdateRequest shopifyFulfillmentUpdateRequest) { - final ShopifyFulfillmentRoot shopifyFulfillmentRoot = new ShopifyFulfillmentRoot(); + /** + * Based on the api starting from 2023-04 it only updates tracking + * information + * + * @param shopifyFulfillmentUpdateRequest + * the information needed to update the fulfillment's tracking + * info + * @return the updated version of the shopify fulfillment + */ + public ShopifyFulfillment updateFulfillmentTrackingInfo( + final ShopifyFulfillmentUpdateRequest shopifyFulfillmentUpdateRequest) { final ShopifyFulfillment shopifyFulfillment = shopifyFulfillmentUpdateRequest.getRequest(); - shopifyFulfillmentRoot.setFulfillment(shopifyFulfillment); - final Response response = put(buildOrdersEndpoint().path(shopifyFulfillment.getOrderId()).path(FULFILLMENTS) - .path(shopifyFulfillment.getId()), shopifyFulfillmentRoot); + final ShopifyUpdateFulfillmentPayloadRoot payload = LegacyToFulfillmentOrderMapping + .toUpdateShopifyFulfillmentPayloadRoot(shopifyFulfillment); + + final Response response = post( + getWebTarget().path(FULFILLMENTS).path(shopifyFulfillment.getId()).path(UPDATE_TRACKING), payload); final ShopifyFulfillmentRoot shopifyFulfillmentRootResponse = response.readEntity(ShopifyFulfillmentRoot.class); return shopifyFulfillmentRootResponse.getFulfillment(); } @@ -996,7 +1072,7 @@ private Response invokeResponseCallable(final Callable responseCallabl } private Retryer buildResponseRetyer() { - return RetryerBuilder.newBuilder().retryIfResult(ShopifySdk::shouldRetryResponse).retryIfException() + return RetryerBuilder. newBuilder().retryIfResult(ShopifySdk::shouldRetryResponse).retryIfException() .withWaitStrategy(WaitStrategies.randomWait(minimumRequestRetryRandomDelayMilliseconds, TimeUnit.MILLISECONDS, maximumRequestRetryRandomDelayMilliseconds, TimeUnit.MILLISECONDS)) .withStopStrategy( @@ -1156,4 +1232,50 @@ private String getQueryParam(final URI uri, final String key) { private WebTarget buildOrdersEndpoint() { return getWebTarget().path(ORDERS); } + + public List getFulfillmentOrdersFromOrder(final String shopifyOrderId) { + final List fulfillmentOrders = new LinkedList<>(); + final Response response = get(buildOrdersEndpoint().path(shopifyOrderId).path(FULFILLMENT_ORDERS)); + final ShopifyFulfillmentOrdersRoot shopifyFulfillmentOrdersRoot = response + .readEntity(ShopifyFulfillmentOrdersRoot.class); + + fulfillmentOrders.addAll(shopifyFulfillmentOrdersRoot.getFulfillmentOrders()); + + return fulfillmentOrders; + } + + public ShopifyFulfillmentOrder moveFulfillmentOrder(final String newLocationId, + final ShopifyFulfillmentOrder shopifyFulfillmentOrder) throws ShopifyIncompatibleApiException { + final ShopifyFulfillmentOrderMoveRequestRoot payload = LegacyToFulfillmentOrderMapping + .toShopifyMoveFulfillmentOrder(newLocationId, shopifyFulfillmentOrder); + + final Response response = post( + getWebTarget().path(FULFILLMENT_ORDERS).path(shopifyFulfillmentOrder.getId()).path(MOVE), payload); + final ShopifyFulfillmentOrderMoveResponseRoot shopifyFulfillmentRootResponse = response + .readEntity(ShopifyFulfillmentOrderMoveResponseRoot.class); + return shopifyFulfillmentRootResponse.getOriginalFulfillmentOrder(); + } + + private ShopifyFulfillment createFulfillmentWithLegacyApi( + final ShopifyFulfillmentCreationRequest shopifyFulfillmentCreationRequest) { + final ShopifyFulfillmentRoot shopifyFulfillmentRoot = new ShopifyFulfillmentRoot(); + final ShopifyFulfillment shopifyFulfillment = shopifyFulfillmentCreationRequest.getRequest(); + shopifyFulfillmentRoot.setFulfillment(shopifyFulfillment); + final Response response = post(buildOrdersEndpoint().path(shopifyFulfillment.getOrderId()).path(FULFILLMENTS), + shopifyFulfillmentRoot); + final ShopifyFulfillmentRoot shopifyFulfillmentRootResponse = response.readEntity(ShopifyFulfillmentRoot.class); + return shopifyFulfillmentRootResponse.getFulfillment(); + } + + private ShopifyFulfillment createFulfillmentWithFulfillmentOrderApi( + final ShopifyFulfillmentCreationRequest shopifyFulfillmentCreationRequest, + final List fulfillmentOrders) throws ShopifyEmptyLineItemsException { + final ShopifyFulfillmentPayloadRoot payload = LegacyToFulfillmentOrderMapping + .toShopifyFulfillmentPayloadRoot(shopifyFulfillmentCreationRequest.getRequest(), fulfillmentOrders); + + final Response response = post(getWebTarget().path(FULFILLMENTS), payload); + final ShopifyFulfillmentRoot shopifyFulfillmentRootResponse = response.readEntity(ShopifyFulfillmentRoot.class); + + return shopifyFulfillmentRootResponse.getFulfillment(); + } } diff --git a/src/main/java/com/shopify/exceptions/ShopifyEmptyLineItemsException.java b/src/main/java/com/shopify/exceptions/ShopifyEmptyLineItemsException.java new file mode 100644 index 00000000..e0a91821 --- /dev/null +++ b/src/main/java/com/shopify/exceptions/ShopifyEmptyLineItemsException.java @@ -0,0 +1,10 @@ +package com.shopify.exceptions; + +public class ShopifyEmptyLineItemsException extends Exception { + private static final long serialVersionUID = -6803895255030397292L; + + public ShopifyEmptyLineItemsException() { + super("LineItems are required when creating a new fulfillment from a fulfillmentOrder"); + } + +} diff --git a/src/main/java/com/shopify/exceptions/ShopifyIncompatibleApiException.java b/src/main/java/com/shopify/exceptions/ShopifyIncompatibleApiException.java new file mode 100644 index 00000000..b88cc08a --- /dev/null +++ b/src/main/java/com/shopify/exceptions/ShopifyIncompatibleApiException.java @@ -0,0 +1,16 @@ +package com.shopify.exceptions; + +public class ShopifyIncompatibleApiException extends RuntimeException { + + private static final String defaultMessage = "This action cannot be done in the current ShopifyApi version"; + private static final long serialVersionUID = 147838161361971621L; + + public ShopifyIncompatibleApiException() { + super(defaultMessage); + } + + public ShopifyIncompatibleApiException(final Throwable throwable) { + super(defaultMessage, throwable); + } + +} diff --git a/src/main/java/com/shopify/mappers/LegacyToFulfillmentOrderMapping.java b/src/main/java/com/shopify/mappers/LegacyToFulfillmentOrderMapping.java new file mode 100644 index 00000000..4756ae9d --- /dev/null +++ b/src/main/java/com/shopify/mappers/LegacyToFulfillmentOrderMapping.java @@ -0,0 +1,175 @@ +package com.shopify.mappers; + +import java.util.LinkedList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.shopify.ShopifySdk; +import com.shopify.exceptions.ShopifyEmptyLineItemsException; +import com.shopify.model.ShopifyFulfillment; +import com.shopify.model.ShopifyFulfillmentOrder; +import com.shopify.model.ShopifyFulfillmentOrder.SupportedActions; +import com.shopify.model.ShopifyFulfillmentOrderLineItem; +import com.shopify.model.ShopifyFulfillmentOrderMoveLocationPayload; +import com.shopify.model.ShopifyFulfillmentOrderMoveRequestRoot; +import com.shopify.model.ShopifyFulfillmentOrderPayloadLineItem; +import com.shopify.model.ShopifyFulfillmentPayload; +import com.shopify.model.ShopifyFulfillmentPayloadRoot; +import com.shopify.model.ShopifyLineItem; +import com.shopify.model.ShopifyLineItemsByFulfillmentOrder; +import com.shopify.model.ShopifyTrackingInfo; +import com.shopify.model.ShopifyUpdateFulfillmentPayload; +import com.shopify.model.ShopifyUpdateFulfillmentPayloadRoot; + +public class LegacyToFulfillmentOrderMapping { + + private static final Logger LOGGER = LoggerFactory.getLogger(LegacyToFulfillmentOrderMapping.class); + + /** + * the idea here is to create a payload similar to what we + * have here, the resulting payload will be sent to shopify via the + * active ShopifySdk instance + * + * @see ShopifySdk + * @see ShopifyFulfillmentOrderMoveRequestRoot + * + * @param newLocation + * the new location that the fulfillment order will be moved to + * @param fulfillmentOrder + * the fulfillment order to be moved + * @return an ShopifyFulfillmentOrderMoveRequestRoot instance to be sent to + * shopify + */ + public static ShopifyFulfillmentOrderMoveRequestRoot toShopifyMoveFulfillmentOrder(final String newLocation, + ShopifyFulfillmentOrder fulfillmentOrder) { + try { + final ShopifyFulfillmentOrderMoveLocationPayload payload = new ShopifyFulfillmentOrderMoveLocationPayload(); + payload.setNewLocationId(newLocation); + for (final ShopifyFulfillmentOrderLineItem fulfillmentOrderLineItem : fulfillmentOrder.getLineItems()) { + payload.getFulfillmentOrderLineItems().add(new ShopifyFulfillmentOrderPayloadLineItem( + fulfillmentOrderLineItem.getId(), fulfillmentOrderLineItem.getQuantity())); + } + + ShopifyFulfillmentOrderMoveRequestRoot root = new ShopifyFulfillmentOrderMoveRequestRoot(); + root.setFulfillmentOrder(payload); + return root; + } catch (final Exception e) { + LOGGER.error( + "There was an error parsing fulfillmentOrder into a ShopifyFulfillmentOrder moveRequest payload", + e); + throw e; + } + } + + /** + * the idea here is to create a payload similar to what we + * have here, the resulting payload will be sent to shopify via the + * active ShopifySdk instance + * + * @see ShopifySdk + * @see ShopifyFulfillmentPayloadRoot + * + * @param fulfillment + * the active fulfillment to be created/updated + * @param fulfillmentOrders + * a list of fulfillment orders that are contained within the + * order + * @return an ShopifyFulfillmentPayloadRoot instance to be sent to shopify + * @throws ShopifyEmptyLineItemsException + */ + public static ShopifyFulfillmentPayloadRoot toShopifyFulfillmentPayloadRoot(final ShopifyFulfillment fulfillment, + final List fulfillmentOrders) throws ShopifyEmptyLineItemsException { + try { + final ShopifyTrackingInfo trackingInfo = new ShopifyTrackingInfo(); + final ShopifyFulfillmentPayload payload = new ShopifyFulfillmentPayload(); + final List lineItemsByFulfillmentOrder = new LinkedList<>(); + + trackingInfo.setUrl(fulfillment.getTrackingUrl()); + trackingInfo.setNumber(fulfillment.getTrackingNumber()); + trackingInfo.setCompany(fulfillment.getTrackingCompany()); + + // here we need to iterate through all fulfillmentOrder line items + // to determine which fulfillment order line items are going to be + // referenced inside the payload's line_items_by_fulfillment_order + // section + for (final ShopifyFulfillmentOrder fulfillmentOrder : fulfillmentOrders) { + // if a shopify fulfillment is cancelled a new fulfillment order + // is added the old one gets it's supported actions emptied, so + // we need to make sure the current fulfillmentOrder supportes + // fulfillment creation under it + if (fulfillmentOrder.hasSupportedAction(SupportedActions.CREATE_FULFILLMENT)) { + ShopifyLineItemsByFulfillmentOrder lineItemsByFulfillment = new ShopifyLineItemsByFulfillmentOrder(); + lineItemsByFulfillment.setFulfillmentOrderId(fulfillmentOrder.getId()); + for (final ShopifyFulfillmentOrderLineItem fulfillmentOrderLineItem : fulfillmentOrder + .getLineItems()) { + for (final ShopifyLineItem fulfillmentLineItem : fulfillment.getLineItems()) { + if (fulfillmentOrderLineItem.getLineItemId().equals(fulfillmentLineItem.getId())) { + ShopifyFulfillmentOrderPayloadLineItem payloadLineItem = new ShopifyFulfillmentOrderPayloadLineItem( + fulfillmentOrderLineItem.getId(), fulfillmentLineItem.getQuantity()); + lineItemsByFulfillment.getFulfillmentOrderLineItems().add(payloadLineItem); + } + } + } + + if (lineItemsByFulfillment.getFulfillmentOrderLineItems().size() > 0) { + lineItemsByFulfillmentOrder.add(lineItemsByFulfillment); + } + } + } + if (lineItemsByFulfillmentOrder.size() < 1) + throw new ShopifyEmptyLineItemsException(); + + payload.setTrackingInfo(trackingInfo); + payload.setNotifyCustomer(fulfillment.isNotifyCustomer()); + payload.setLineItemsByFulfillmentOrder(lineItemsByFulfillmentOrder); + + ShopifyFulfillmentPayloadRoot root = new ShopifyFulfillmentPayloadRoot(); + root.setFulfillment(payload); + return root; + } catch (final Exception e) { + LOGGER.error("There was an error parsing fulfillmentOrder into a ShopifyFulfillment create payload", e); + throw e; + } + } + + /** + * the idea here is to create a payload similar to what we + * have here, the resulting payload will be sent to shopify via the + * active ShopifySdk instance + * + * @see ShopifySdk + * @see ShopifyUpdateFulfillmentPayloadRoot + * + * @param fulfillment + * the active fulfillment to have it's tracking information + * updated + * @return an ShopifyFulfillmentPayloadRoot instance to be sent to shopify + */ + public static ShopifyUpdateFulfillmentPayloadRoot toUpdateShopifyFulfillmentPayloadRoot( + final ShopifyFulfillment fulfillment) { + try { + final ShopifyTrackingInfo trackingInfo = new ShopifyTrackingInfo(); + final ShopifyUpdateFulfillmentPayload payload = new ShopifyUpdateFulfillmentPayload(); + + trackingInfo.setUrl(fulfillment.getTrackingUrl()); + trackingInfo.setNumber(fulfillment.getTrackingNumber()); + trackingInfo.setCompany(fulfillment.getTrackingCompany()); + + payload.setTrackingInfo(trackingInfo); + payload.setNotifyCustomer(fulfillment.isNotifyCustomer()); + + ShopifyUpdateFulfillmentPayloadRoot root = new ShopifyUpdateFulfillmentPayloadRoot(); + root.setFulfillment(payload); + return root; + } catch (final Exception e) { + LOGGER.error("There was an error parsing fulfillmentOrder into a ShopifyFulfillment update payload", e); + throw e; + } + } + +} diff --git a/src/main/java/com/shopify/mappers/ResponseEntityToStringMapper.java b/src/main/java/com/shopify/mappers/ResponseEntityToStringMapper.java index 02fedf4a..57216e8d 100644 --- a/src/main/java/com/shopify/mappers/ResponseEntityToStringMapper.java +++ b/src/main/java/com/shopify/mappers/ResponseEntityToStringMapper.java @@ -17,12 +17,14 @@ private ResponseEntityToStringMapper() { } /** - * JAXRS Jersey Client implementation closes stream buffers when reading entity - * by string. To combat this and be able to read entities via a string more than - * once, this deals with the input streams involved and resets where necessary. + * JAXRS Jersey Client implementation closes stream buffers when reading + * entity by string. To combat this and be able to read entities via a + * string more than once, this deals with the input streams involved and + * resets where necessary. * * @param response - * @return + * the response gotten from a request + * @return the response as a string value */ public static String map(final Response response) { diff --git a/src/main/java/com/shopify/model/ShopifyAssignedLocation.java b/src/main/java/com/shopify/model/ShopifyAssignedLocation.java new file mode 100644 index 00000000..98ada937 --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyAssignedLocation.java @@ -0,0 +1,95 @@ +package com.shopify.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyAssignedLocation { + + private String address1; + private String address2; + private String city; + @XmlElement(name = "country_code") + private String countryCode; + @XmlElement(name = "location_id") + private String locationId; + private String name; + private String phone; + private String province; + private String zip; + + public String getAddress1() { + return address1; + } + + public void setAddress1(String address1) { + this.address1 = address1; + } + + public String getAddress2() { + return address2; + } + + public void setAddress2(String address2) { + this.address2 = address2; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getCountryCode() { + return countryCode; + } + + public void setCountryCode(String countryCode) { + this.countryCode = countryCode; + } + + public String getLocationId() { + return locationId; + } + + public void setLocationId(String locationId) { + this.locationId = locationId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getProvince() { + return province; + } + + public void setProvince(String province) { + this.province = province; + } + + public String getZip() { + return zip; + } + + public void setZip(String zip) { + this.zip = zip; + } +} diff --git a/src/main/java/com/shopify/model/ShopifyDeliveryMethod.java b/src/main/java/com/shopify/model/ShopifyDeliveryMethod.java new file mode 100644 index 00000000..f8974dd0 --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyDeliveryMethod.java @@ -0,0 +1,125 @@ +package com.shopify.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import org.joda.time.DateTime; + +import com.shopify.model.adapters.DateTimeAdapter; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyDeliveryMethod { + + /** + * The type of method used to transfer a product or service to a customer + * + * @see #NONE + * @see #LOCAL + * @see #CUSTOM + * @see #RETAIL + * @see #PICK_UP + * @see #SHIPPING + */ + public enum MethodType { + /** + * No delivery method + */ + NONE("none"), + /** + * A delivery to a customer's doorstep. + */ + LOCAL("local"), + /** + * A default value for a non matching delivery method + */ + CUSTOM("custom"), + /** + * Items delivered immediately in a retail store. + */ + RETAIL("retail"), + /** + * A delivery that a customer picks up at your retail store, curbside, + * or any location that you choose + */ + PICK_UP("pick_up"), + /** + * A delivery to a customer using a shipping carrier + */ + SHIPPING("shipping"); + + static final String NO_MATCHING_ENUMS_ERROR_MESSAGE = "No matching enum found for status: %s"; + private final String value; + + private MethodType(final String value) { + this.value = value; + } + + public static MethodType toEnum(final String value) { + if (NONE.toString().equals(value)) { + return MethodType.NONE; + } else if (LOCAL.toString().equals(value)) { + return MethodType.LOCAL; + } else if (RETAIL.toString().equals(value)) { + return MethodType.RETAIL; + } else if (PICK_UP.toString().equals(value)) { + return MethodType.PICK_UP; + } else if (SHIPPING.toString().equals(value)) { + return MethodType.SHIPPING; + } else { + return MethodType.CUSTOM; + } + } + + @Override + public String toString() { + return value; + } + } + + private String id; + @XmlElement(name = "method_type") + private String methodType; + @XmlElement(name = "min_delivery_date_time") + @XmlJavaTypeAdapter(DateTimeAdapter.class) + private DateTime minDeliveryDateTime; + @XmlElement(name = "max_delivery_date_time") + @XmlJavaTypeAdapter(DateTimeAdapter.class) + private DateTime maxDeliveryDateTime; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getMethodType() { + return methodType; + } + + public void setMethodType(String methodType) { + this.methodType = methodType; + } + + public DateTime getMinDeliveryDateTime() { + return minDeliveryDateTime; + } + + public void setMinDeliveryDateTime(DateTime minDeliveryDateTime) { + this.minDeliveryDateTime = minDeliveryDateTime; + } + + public DateTime getMaxDeliveryDateTime() { + return maxDeliveryDateTime; + } + + public void setMaxDeliveryDateTime(DateTime maxDeliveryDateTime) { + this.maxDeliveryDateTime = maxDeliveryDateTime; + } + +} diff --git a/src/main/java/com/shopify/model/ShopifyDestination.java b/src/main/java/com/shopify/model/ShopifyDestination.java new file mode 100644 index 00000000..0fb7c153 --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyDestination.java @@ -0,0 +1,122 @@ +package com.shopify.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyDestination { + + private String id; + private String address1; + private String address2; + private String city; + private String company; + private String country; + private String email; + @XmlElement(name = "first_name") + private String firstName; + @XmlElement(name = "last_name") + private String lastName; + private String phone; + private String province; + private String zip; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getAddress1() { + return address1; + } + + public void setAddress1(String address1) { + this.address1 = address1; + } + + public String getAddress2() { + return address2; + } + + public void setAddress2(String address2) { + this.address2 = address2; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getCompany() { + return company; + } + + public void setCompany(String company) { + this.company = company; + } + + public String getCountry() { + return country; + } + + public void setCountry(String country) { + this.country = country; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } + + public String getProvince() { + return province; + } + + public void setProvince(String province) { + this.province = province; + } + + public String getZip() { + return zip; + } + + public void setZip(String zip) { + this.zip = zip; + } +} diff --git a/src/main/java/com/shopify/model/ShopifyFulfillmentCreationRequest.java b/src/main/java/com/shopify/model/ShopifyFulfillmentCreationRequest.java index 37d028c9..57b65ab9 100644 --- a/src/main/java/com/shopify/model/ShopifyFulfillmentCreationRequest.java +++ b/src/main/java/com/shopify/model/ShopifyFulfillmentCreationRequest.java @@ -104,4 +104,4 @@ public BuildStep withTrackingUrls(final List trackingUrls) { } -} +} \ No newline at end of file diff --git a/src/main/java/com/shopify/model/ShopifyFulfillmentHold.java b/src/main/java/com/shopify/model/ShopifyFulfillmentHold.java new file mode 100644 index 00000000..4f76d8ab --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyFulfillmentHold.java @@ -0,0 +1,32 @@ +package com.shopify.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyFulfillmentHold { + + private String reason; + @XmlElement(name = "reason_notes") + private String reasonNotes; + + public String getReason() { + return reason; + } + + public void setReason(String reason) { + this.reason = reason; + } + + public String getReasonNotes() { + return reasonNotes; + } + + public void setReasonNotes(String reasonNotes) { + this.reasonNotes = reasonNotes; + } + +} diff --git a/src/main/java/com/shopify/model/ShopifyFulfillmentOrder.java b/src/main/java/com/shopify/model/ShopifyFulfillmentOrder.java new file mode 100644 index 00000000..7bdf1c19 --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyFulfillmentOrder.java @@ -0,0 +1,427 @@ +package com.shopify.model; + +import java.util.LinkedList; +import java.util.List; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import org.joda.time.DateTime; + +import com.shopify.model.adapters.DateTimeAdapter; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyFulfillmentOrder { + + /** + * Lists all acceptable status for a given FulfillmentOrder + * + * @see #OPEN + * @see #CLOSED + * @see #ON_HOLD + * @see #CANCELLED + * @see #SCHEDULED + * @see #INCOMPLETE + * @see #IN_PROGRESS + */ + public enum Status { + /** + * The fulfillment order is ready for fulfillment + */ + OPEN("open"), + /** + * The fulfillment order has been completed and closed + */ + CLOSED("closed"), + /** + * The fulfillment order is on hold. The fulfillment process can't be + * initiated until the hold on the fulfillment order is released + */ + ON_HOLD("on_hold"), + /** + * The fulfillment order has been cancelled by the merchant + */ + CANCELLED("cancelled"), + /** + * The fulfillment order is deferred and will be ready for fulfillment + * after the datetime specified in fulfill_at + */ + SCHEDULED("scheduled"), + /** + * The fulfillment order cannot be completed as requested + */ + INCOMPLETE("incomplete"), + /** + * The fulfillment order is being processed + */ + IN_PROGRESS("in_progress"); + + static final String NO_MATCHING_ENUMS_ERROR_MESSAGE = "No matching enum found for status: %s"; + private final String value; + + private Status(final String value) { + this.value = value; + } + + public static Status toEnum(final String value) { + if (OPEN.toString().equals(value)) { + return Status.OPEN; + } else if (CLOSED.toString().equals(value)) { + return Status.CLOSED; + } else if (ON_HOLD.toString().equals(value)) { + return Status.ON_HOLD; + } else if (CANCELLED.toString().equals(value)) { + return Status.CANCELLED; + } else if (SCHEDULED.toString().equals(value)) { + return Status.SCHEDULED; + } else if (INCOMPLETE.toString().equals(value)) { + return Status.INCOMPLETE; + } else if (IN_PROGRESS.toString().equals(value)) { + return Status.IN_PROGRESS; + } + + throw new IllegalArgumentException(String.format(NO_MATCHING_ENUMS_ERROR_MESSAGE, value)); + } + + @Override + public String toString() { + return value; + } + } + + /** + * The actions that can be performed on this fulfillment order. + */ + public enum SupportedActions { + HOLD("hold"), + MOVE("move"), + EXTERNAL("external"), + MARK_AS_OPEN("mark_as_open"), + RELEASE_HOLD("release_hold"), + CREATE_FULFILLMENT("create_fulfillment"), + REQUEST_FULFILLMENT("request_fulfillment"), + REQUEST_CANCELLATION("request_cancellation"), + CANCEL_FULFILLMENT_ORDER("cancel_fulfillment_order"); + + static final String NO_MATCHING_ENUMS_ERROR_MESSAGE = "No matching enum found for supported action: %s"; + private final String value; + + private SupportedActions(final String value) { + this.value = value; + } + + public static SupportedActions toEnum(final String value) { + if (MOVE.toString().equals(value)) { + return SupportedActions.MOVE; + } else if (HOLD.toString().equals(value)) { + return SupportedActions.HOLD; + } else if (EXTERNAL.toString().equals(value)) { + return SupportedActions.EXTERNAL; + } else if (MARK_AS_OPEN.toString().equals(value)) { + return SupportedActions.MARK_AS_OPEN; + } else if (RELEASE_HOLD.toString().equals(value)) { + return SupportedActions.RELEASE_HOLD; + } else if (CREATE_FULFILLMENT.toString().equals(value)) { + return SupportedActions.CREATE_FULFILLMENT; + } else if (REQUEST_FULFILLMENT.toString().equals(value)) { + return SupportedActions.REQUEST_FULFILLMENT; + } else if (REQUEST_CANCELLATION.toString().equals(value)) { + return SupportedActions.REQUEST_CANCELLATION; + } else if (CANCEL_FULFILLMENT_ORDER.toString().equals(value)) { + return SupportedActions.CANCEL_FULFILLMENT_ORDER; + } + + throw new IllegalArgumentException(String.format(NO_MATCHING_ENUMS_ERROR_MESSAGE, value)); + } + + @Override + public String toString() { + return value; + } + } + + /** + * List of all request status of the fulfillment order. Valid values + * + * @see #CLOSED + * @see #ACCEPTED + * @see #REJECTED + * @see #SUBMITTED + * @see #UNSUBMITTED + * @see #CANCELLATION_ACCEPTED + * @see #CANCELLATION_REJECTED + * @see #CANCELLATION_REQUESTED + */ + public enum RequestStatus { + /** + * The fulfillment service closed the fulfillment order without + * completing it. + */ + CLOSED("closed"), + /** + * The fulfillment service accepted the merchant's fulfillment request + */ + ACCEPTED("accepted"), + /** + * The fulfillment service rejected the merchant's fulfillment request + */ + REJECTED("rejected"), + /** + * The merchant requested fulfillment for this fulfillment order + */ + SUBMITTED("submitted"), + /** + * The initial request status for newly-created fulfillment orders. This + * is the only valid request status for fulfillment orders that aren't + * assigned to a fulfillment service + */ + UNSUBMITTED("unsubmitted"), + /** + * The fulfillment service accepted the merchant's fulfillment + * cancellation request + */ + CANCELLATION_ACCEPTED("cancellation_accepted"), + /** + * The fulfillment service rejected the merchant's fulfillment + * cancellation request + */ + CANCELLATION_REJECTED("cancellation_rejected"), + /** + * The merchant requested a cancellation of the fulfillment request for + * this fulfillment order + */ + CANCELLATION_REQUESTED("cancellation_requested"); + + static final String NO_MATCHING_ENUMS_ERROR_MESSAGE = "No matching enum found for supported action: %s"; + private final String value; + + private RequestStatus(final String value) { + this.value = value; + } + + public static RequestStatus toEnum(final String value) { + if (CLOSED.toString().equals(value)) { + return RequestStatus.CLOSED; + } else if (ACCEPTED.toString().equals(value)) { + return RequestStatus.ACCEPTED; + } else if (REJECTED.toString().equals(value)) { + return RequestStatus.REJECTED; + } else if (SUBMITTED.toString().equals(value)) { + return RequestStatus.SUBMITTED; + } else if (UNSUBMITTED.toString().equals(value)) { + return RequestStatus.UNSUBMITTED; + } else if (CANCELLATION_ACCEPTED.toString().equals(value)) { + return RequestStatus.CANCELLATION_ACCEPTED; + } else if (CANCELLATION_REJECTED.toString().equals(value)) { + return RequestStatus.CANCELLATION_REJECTED; + } else if (CANCELLATION_REQUESTED.toString().equals(value)) { + return RequestStatus.CANCELLATION_REQUESTED; + } + + throw new IllegalArgumentException(String.format(NO_MATCHING_ENUMS_ERROR_MESSAGE, value)); + } + + @Override + public String toString() { + return value; + } + } + + private String id; + @XmlElement(name = "shop_id") + private String shopId; + @XmlElement(name = "order_id") + private String orderId; + @XmlElement(name = "assigned_location_id") + private String assignedLocationId; + @XmlElement(name = "request_status") + private String requestStatus; + private String status; + @XmlElement(name = "supported_actions") + private List supportedActions = new LinkedList<>(); + private ShopifyDestination destination; + @XmlElement(name = "line_items") + private List lineItems = new LinkedList<>(); + @XmlElement(name = "fulfill_at") + @XmlJavaTypeAdapter(DateTimeAdapter.class) + private DateTime fulfillAt; + @XmlElement(name = "fulfill_by") + @XmlJavaTypeAdapter(DateTimeAdapter.class) + private DateTime fulfillBy; + @XmlElement(name = "international_duties") + private ShopifyInternationalDuty internationalDuties; + @XmlElement(name = "fulfillment_holds") + private List fulfillmentHolds = new LinkedList<>(); + @XmlElement(name = "delivery_method") + private ShopifyDeliveryMethod deliveryMethod; + @XmlElement(name = "created_at") + @XmlJavaTypeAdapter(DateTimeAdapter.class) + private DateTime createdAt; + @XmlElement(name = "updated_at") + @XmlJavaTypeAdapter(DateTimeAdapter.class) + private DateTime updatedAt; + @XmlElement(name = "assigned_location") + private ShopifyAssignedLocation assignedLocation; + @XmlElement(name = "merchant_requests") + private List merchantRequests = new LinkedList<>(); + + public String getAssignedLocationId() { + return assignedLocationId; + } + + public void setAssignedLocationId(String assignedLocationId) { + this.assignedLocationId = assignedLocationId; + } + + public ShopifyDestination getDestination() { + return destination; + } + + public void setDestination(ShopifyDestination destination) { + this.destination = destination; + } + + public ShopifyDeliveryMethod getDeliveryMethod() { + return deliveryMethod; + } + + public void setDeliveryMethod(ShopifyDeliveryMethod deliveryMethod) { + this.deliveryMethod = deliveryMethod; + } + + public DateTime getFulfillAt() { + return fulfillAt; + } + + public void setFulfillAt(DateTime fulfillAt) { + this.fulfillAt = fulfillAt; + } + + public DateTime getFulfillBy() { + return fulfillBy; + } + + public void setFulfillBy(DateTime fulfillBy) { + this.fulfillBy = fulfillBy; + } + + public List getFulfillmentHolds() { + return fulfillmentHolds; + } + + public void setFulfillmentHolds(List fulfillmentHolds) { + this.fulfillmentHolds = fulfillmentHolds; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public ShopifyInternationalDuty getInternationalDuties() { + return internationalDuties; + } + + public void setInternationalDuties(ShopifyInternationalDuty internationalDuties) { + this.internationalDuties = internationalDuties; + } + + public List getLineItems() { + return lineItems; + } + + public void setLineItems(List lineItems) { + this.lineItems = lineItems; + } + + public String getOrderId() { + return orderId; + } + + public void setOrderId(String orderId) { + this.orderId = orderId; + } + + public String getRequestStatus() { + return requestStatus; + } + + public void setRequestStatus(String requestStatus) { + this.requestStatus = requestStatus; + } + + public String getShopId() { + return shopId; + } + + public void setShopId(String shopId) { + this.shopId = shopId; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public List getSupportedActions() { + return supportedActions; + } + + public void setSupportedActions(List supportedActions) { + this.supportedActions = supportedActions; + } + + public List getMerchantRequests() { + return merchantRequests; + } + + public void setMerchantRequests(List merchantRequests) { + this.merchantRequests = merchantRequests; + } + + public ShopifyAssignedLocation getAssignedLocation() { + return assignedLocation; + } + + public void setAssignedLocation(ShopifyAssignedLocation assignedLocation) { + this.assignedLocation = assignedLocation; + } + + public DateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(DateTime createdAt) { + this.createdAt = createdAt; + } + + public DateTime getUpdatedAt() { + return updatedAt; + } + + public void setUpdatedAt(DateTime updatedAt) { + this.updatedAt = updatedAt; + } + + public boolean hasSupportedAction(final SupportedActions action) { + final String stringAction = action.toString().toLowerCase(); + for (final String supportedAction : this.getSupportedActions()) { + if (supportedAction.toLowerCase().equals(stringAction)) { + return true; + } + } + + return false; + } + +} diff --git a/src/main/java/com/shopify/model/ShopifyFulfillmentOrderLineItem.java b/src/main/java/com/shopify/model/ShopifyFulfillmentOrderLineItem.java new file mode 100644 index 00000000..93e88e63 --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyFulfillmentOrderLineItem.java @@ -0,0 +1,90 @@ +package com.shopify.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyFulfillmentOrderLineItem { + + private String id; + @XmlElement(name = "shop_id") + private String shopId; + @XmlElement(name = "fulfillment_order_id") + private String fulfillmentOrderId; + private long quantity; + @XmlElement(name = "line_item_id") + private String lineItemId; + @XmlElement(name = "inventory_item_id") + private String inventoryItemId; + @XmlElement(name = "fulfillable_quantity") + private long fulfillableQuantity; + @XmlElement(name = "variant_id") + private String variantId; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getShopId() { + return shopId; + } + + public void setShopId(String shopId) { + this.shopId = shopId; + } + + public String getFulfillmentOrderId() { + return fulfillmentOrderId; + } + + public void setFulfillmentOrderId(String fulfillmentOrderId) { + this.fulfillmentOrderId = fulfillmentOrderId; + } + + public String getLineItemId() { + return lineItemId; + } + + public void setLineItemId(String lineItemId) { + this.lineItemId = lineItemId; + } + + public String getInventoryItemId() { + return inventoryItemId; + } + + public void setInventoryItemId(String inventoryItemId) { + this.inventoryItemId = inventoryItemId; + } + + public long getQuantity() { + return quantity; + } + + public void setQuantity(long quantity) { + this.quantity = quantity; + } + + public long getFulfillableQuantity() { + return fulfillableQuantity; + } + + public void setFulfillableQuantity(long fulfillableQuantity) { + this.fulfillableQuantity = fulfillableQuantity; + } + + public String getVariantId() { + return variantId; + } + + public void setVariantId(String variantId) { + this.variantId = variantId; + } +} diff --git a/src/main/java/com/shopify/model/ShopifyFulfillmentOrderMoveLocationPayload.java b/src/main/java/com/shopify/model/ShopifyFulfillmentOrderMoveLocationPayload.java new file mode 100644 index 00000000..3691ef32 --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyFulfillmentOrderMoveLocationPayload.java @@ -0,0 +1,36 @@ +package com.shopify.model; + +import java.util.LinkedList; +import java.util.List; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyFulfillmentOrderMoveLocationPayload { + + @XmlElement(name = "new_location_id") + private String newLocationId; + @XmlElement(name = "fulfillment_order_line_items") + private List fulfillmentOrderLineItems = new LinkedList<>(); + + public String getNewLocationId() { + return newLocationId; + } + + public void setNewLocationId(String newLocationId) { + this.newLocationId = newLocationId; + } + + public List getFulfillmentOrderLineItems() { + return fulfillmentOrderLineItems; + } + + public void setFulfillmentOrderLineItems(List fulfillmentOrderLineItems) { + this.fulfillmentOrderLineItems = fulfillmentOrderLineItems; + } + +} \ No newline at end of file diff --git a/src/main/java/com/shopify/model/ShopifyFulfillmentOrderMoveRequestRoot.java b/src/main/java/com/shopify/model/ShopifyFulfillmentOrderMoveRequestRoot.java new file mode 100644 index 00000000..a05fc942 --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyFulfillmentOrderMoveRequestRoot.java @@ -0,0 +1,23 @@ +package com.shopify.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyFulfillmentOrderMoveRequestRoot { + + @XmlElement(name = "fulfillment_order") + private ShopifyFulfillmentOrderMoveLocationPayload fulfillmentOrder; + + public ShopifyFulfillmentOrderMoveLocationPayload getFulfillmentOrder() { + return fulfillmentOrder; + } + + public void setFulfillmentOrder(ShopifyFulfillmentOrderMoveLocationPayload fulfillmentOrder) { + this.fulfillmentOrder = fulfillmentOrder; + } + +} \ No newline at end of file diff --git a/src/main/java/com/shopify/model/ShopifyFulfillmentOrderMoveResponseRoot.java b/src/main/java/com/shopify/model/ShopifyFulfillmentOrderMoveResponseRoot.java new file mode 100644 index 00000000..3255db33 --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyFulfillmentOrderMoveResponseRoot.java @@ -0,0 +1,23 @@ +package com.shopify.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyFulfillmentOrderMoveResponseRoot { + + @XmlElement(name = "original_fulfillment_order") + private ShopifyFulfillmentOrder originalFulfillmentOrder; + + public ShopifyFulfillmentOrder getOriginalFulfillmentOrder() { + return originalFulfillmentOrder; + } + + public void setOriginalFulfillmentOrder(ShopifyFulfillmentOrder originalFulfillmentOrder) { + this.originalFulfillmentOrder = originalFulfillmentOrder; + } + +} \ No newline at end of file diff --git a/src/main/java/com/shopify/model/ShopifyFulfillmentOrderPayloadLineItem.java b/src/main/java/com/shopify/model/ShopifyFulfillmentOrderPayloadLineItem.java new file mode 100644 index 00000000..18e11a20 --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyFulfillmentOrderPayloadLineItem.java @@ -0,0 +1,33 @@ +package com.shopify.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyFulfillmentOrderPayloadLineItem { + private String id; + private long quantity; + + public ShopifyFulfillmentOrderPayloadLineItem(final String id, final long quantity) { + this.id = id; + this.quantity = quantity; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public long getQuantity() { + return quantity; + } + + public void setQuantity(long quantity) { + this.quantity = quantity; + } +} \ No newline at end of file diff --git a/src/main/java/com/shopify/model/ShopifyFulfillmentOrderRoot.java b/src/main/java/com/shopify/model/ShopifyFulfillmentOrderRoot.java new file mode 100644 index 00000000..9711e563 --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyFulfillmentOrderRoot.java @@ -0,0 +1,22 @@ +package com.shopify.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyFulfillmentOrderRoot { + + @XmlElement(name = "fulfillment_order") + private ShopifyFulfillmentOrder fulfillmentOrder; + + public ShopifyFulfillmentOrder getFulfillmentOrder() { + return fulfillmentOrder; + } + + public void setFulfillmentOrder(ShopifyFulfillmentOrder fulfillmentOrder) { + this.fulfillmentOrder = fulfillmentOrder; + } +} diff --git a/src/main/java/com/shopify/model/ShopifyFulfillmentOrdersRoot.java b/src/main/java/com/shopify/model/ShopifyFulfillmentOrdersRoot.java new file mode 100644 index 00000000..487de546 --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyFulfillmentOrdersRoot.java @@ -0,0 +1,25 @@ +package com.shopify.model; + +import java.util.LinkedList; +import java.util.List; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyFulfillmentOrdersRoot { + + @XmlElement(name = "fulfillment_orders") + private List fulfillmentOrders = new LinkedList<>(); + + public List getFulfillmentOrders() { + return fulfillmentOrders; + } + + public void setFulfillmentOrders(List fulfillmentOrders) { + this.fulfillmentOrders = fulfillmentOrders; + } +} diff --git a/src/main/java/com/shopify/model/ShopifyFulfillmentPayload.java b/src/main/java/com/shopify/model/ShopifyFulfillmentPayload.java new file mode 100644 index 00000000..c2ab278e --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyFulfillmentPayload.java @@ -0,0 +1,54 @@ +package com.shopify.model; + +import java.util.LinkedList; +import java.util.List; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyFulfillmentPayload { + + private String message; + @XmlElement(name = "notify_customer") + private boolean notifyCustomer; + @XmlElement(name = "tracking_info") + private ShopifyTrackingInfo trackingInfo; + @XmlElement(name = "line_items_by_fulfillment_order") + private List lineItemsByFulfillmentOrder = new LinkedList<>(); + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isNotifyCustomer() { + return notifyCustomer; + } + + public void setNotifyCustomer(boolean notifyCustomer) { + this.notifyCustomer = notifyCustomer; + } + + public ShopifyTrackingInfo getTrackingInfo() { + return trackingInfo; + } + + public void setTrackingInfo(ShopifyTrackingInfo trackingInfo) { + this.trackingInfo = trackingInfo; + } + + public List getLineItemsByFulfillmentOrder() { + return lineItemsByFulfillmentOrder; + } + + public void setLineItemsByFulfillmentOrder(List lineItemsByFulfillmentOrder) { + this.lineItemsByFulfillmentOrder = lineItemsByFulfillmentOrder; + } +} diff --git a/src/main/java/com/shopify/model/ShopifyFulfillmentPayloadRoot.java b/src/main/java/com/shopify/model/ShopifyFulfillmentPayloadRoot.java new file mode 100644 index 00000000..18f15a6e --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyFulfillmentPayloadRoot.java @@ -0,0 +1,20 @@ +package com.shopify.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyFulfillmentPayloadRoot { + + private ShopifyFulfillmentPayload fulfillment; + + public ShopifyFulfillmentPayload getFulfillment() { + return fulfillment; + } + + public void setFulfillment(ShopifyFulfillmentPayload fulfillment) { + this.fulfillment = fulfillment; + } +} diff --git a/src/main/java/com/shopify/model/ShopifyFulfillmentUpdateRequest.java b/src/main/java/com/shopify/model/ShopifyFulfillmentUpdateRequest.java index 94843334..707ca4fd 100644 --- a/src/main/java/com/shopify/model/ShopifyFulfillmentUpdateRequest.java +++ b/src/main/java/com/shopify/model/ShopifyFulfillmentUpdateRequest.java @@ -104,4 +104,4 @@ public BuildStep withTrackingUrls(final List trackingUrls) { } -} +} \ No newline at end of file diff --git a/src/main/java/com/shopify/model/ShopifyInternationalDuty.java b/src/main/java/com/shopify/model/ShopifyInternationalDuty.java new file mode 100644 index 00000000..4fc85ca0 --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyInternationalDuty.java @@ -0,0 +1,60 @@ +package com.shopify.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyInternationalDuty { + + /** + * The international duties relevant to the fulfillment order + * + * @see #DAP + * @see #DDP + */ + public enum Inconterm { + /** + * Delivered at place. + */ + DAP("dap"), + /** + * Delivered duty paid. + */ + DDP("ddp"); + + static final String NO_MATCHING_ENUMS_ERROR_MESSAGE = "No matching enum found for status: %s"; + private final String value; + + private Inconterm(final String value) { + this.value = value; + } + + public static Inconterm toEnum(final String value) { + if (DAP.toString().equals(value)) { + return Inconterm.DAP; + } else if (DDP.toString().equals(value)) { + return Inconterm.DDP; + } + + throw new IllegalArgumentException(String.format(NO_MATCHING_ENUMS_ERROR_MESSAGE, value)); + } + + @Override + public String toString() { + return value; + } + } + + private String incoterm; + + public String getIncoterm() { + return incoterm; + } + + public void setIncoterm(String incoterm) { + this.incoterm = incoterm; + } + +} diff --git a/src/main/java/com/shopify/model/ShopifyLineItemsByFulfillmentOrder.java b/src/main/java/com/shopify/model/ShopifyLineItemsByFulfillmentOrder.java new file mode 100644 index 00000000..05509a75 --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyLineItemsByFulfillmentOrder.java @@ -0,0 +1,35 @@ +package com.shopify.model; + +import java.util.LinkedList; +import java.util.List; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyLineItemsByFulfillmentOrder { + + @XmlElement(name = "fulfillment_order_id") + private String fulfillmentOrderId; + @XmlElement(name = "fulfillment_order_line_items") + private List fulfillmentOrderLineItems = new LinkedList<>(); + + public String getFulfillmentOrderId() { + return fulfillmentOrderId; + } + + public void setFulfillmentOrderId(String fulfillmentOrderId) { + this.fulfillmentOrderId = fulfillmentOrderId; + } + + public List getFulfillmentOrderLineItems() { + return fulfillmentOrderLineItems; + } + + public void setFulfillmentOrderLineItems(List fulfillmentOrderLineItems) { + this.fulfillmentOrderLineItems = fulfillmentOrderLineItems; + } +} diff --git a/src/main/java/com/shopify/model/ShopifyMerchandRequest.java b/src/main/java/com/shopify/model/ShopifyMerchandRequest.java new file mode 100644 index 00000000..4b8c27ac --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyMerchandRequest.java @@ -0,0 +1,71 @@ +package com.shopify.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyMerchandRequest { + + public enum Kind { + FULFILLMENT_REQUEST("fulfillment_request"), + CANCELLATION_REQUEST("cancellation_request"), + LEGACY_FULFILL_REQUEST("legacy_fulfill_request"); + + static final String NO_MATCHING_ENUMS_ERROR_MESSAGE = "No matching enum found for status: %s"; + private final String value; + + private Kind(final String value) { + this.value = value; + } + + public static Kind toEnum(final String value) { + if (FULFILLMENT_REQUEST.toString().equals(value)) { + return Kind.FULFILLMENT_REQUEST; + } else if (CANCELLATION_REQUEST.toString().equals(value)) { + return Kind.CANCELLATION_REQUEST; + } else if (LEGACY_FULFILL_REQUEST.toString().equals(value)) { + return Kind.LEGACY_FULFILL_REQUEST; + } + + throw new IllegalArgumentException(String.format(NO_MATCHING_ENUMS_ERROR_MESSAGE, value)); + } + + @Override + public String toString() { + return value; + } + } + + private String message; + @XmlElement(name = "request_options") + private String requestOptions; + private String kind; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public String getRequestOptions() { + return requestOptions; + } + + public void setRequestOptions(String requestOptions) { + this.requestOptions = requestOptions; + } + + public String getKind() { + return kind; + } + + public void setKind(String kind) { + this.kind = kind; + } + +} diff --git a/src/main/java/com/shopify/model/ShopifyRequestOptions.java b/src/main/java/com/shopify/model/ShopifyRequestOptions.java new file mode 100644 index 00000000..07a1ed1f --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyRequestOptions.java @@ -0,0 +1,47 @@ +package com.shopify.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import org.joda.time.DateTime; + +import com.shopify.model.adapters.DateTimeAdapter; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyRequestOptions { + + @XmlElement(name = "shipping_method") + private String shippingMethod; + private String note; + @XmlJavaTypeAdapter(DateTimeAdapter.class) + private DateTime date; + + public String getShippingMethod() { + return shippingMethod; + } + + public void setShippingMethod(String shippingMethod) { + this.shippingMethod = shippingMethod; + } + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } + + public DateTime getDate() { + return date; + } + + public void setDate(DateTime date) { + this.date = date; + } + +} diff --git a/src/main/java/com/shopify/model/ShopifyTrackingInfo.java b/src/main/java/com/shopify/model/ShopifyTrackingInfo.java new file mode 100644 index 00000000..044f11df --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyTrackingInfo.java @@ -0,0 +1,38 @@ +package com.shopify.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyTrackingInfo { + + private String number; + private String url; + private String company; + + public String getNumber() { + return number; + } + + public void setNumber(String number) { + this.number = number; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getCompany() { + return company; + } + + public void setCompany(String company) { + this.company = company; + } +} diff --git a/src/main/java/com/shopify/model/ShopifyUpdateFulfillmentPayload.java b/src/main/java/com/shopify/model/ShopifyUpdateFulfillmentPayload.java new file mode 100644 index 00000000..b5f15f04 --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyUpdateFulfillmentPayload.java @@ -0,0 +1,41 @@ +package com.shopify.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyUpdateFulfillmentPayload { + + private String message; + @XmlElement(name = "notify_customer") + private boolean notifyCustomer; + @XmlElement(name = "tracking_info") + private ShopifyTrackingInfo trackingInfo; + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public boolean isNotifyCustomer() { + return notifyCustomer; + } + + public void setNotifyCustomer(boolean notifyCustomer) { + this.notifyCustomer = notifyCustomer; + } + + public ShopifyTrackingInfo getTrackingInfo() { + return trackingInfo; + } + + public void setTrackingInfo(ShopifyTrackingInfo trackingInfo) { + this.trackingInfo = trackingInfo; + } +} diff --git a/src/main/java/com/shopify/model/ShopifyUpdateFulfillmentPayloadRoot.java b/src/main/java/com/shopify/model/ShopifyUpdateFulfillmentPayloadRoot.java new file mode 100644 index 00000000..03afb1c5 --- /dev/null +++ b/src/main/java/com/shopify/model/ShopifyUpdateFulfillmentPayloadRoot.java @@ -0,0 +1,20 @@ +package com.shopify.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlRootElement; + +@XmlRootElement +@XmlAccessorType(XmlAccessType.FIELD) +public class ShopifyUpdateFulfillmentPayloadRoot { + + private ShopifyUpdateFulfillmentPayload fulfillment; + + public ShopifyUpdateFulfillmentPayload getFulfillment() { + return fulfillment; + } + + public void setFulfillment(ShopifyUpdateFulfillmentPayload fulfillment) { + this.fulfillment = fulfillment; + } +} diff --git a/src/test/java/com/shopify/ShopifySdkTest.java b/src/test/java/com/shopify/ShopifySdkTest.java index 0daed192..d5e206fe 100644 --- a/src/test/java/com/shopify/ShopifySdkTest.java +++ b/src/test/java/com/shopify/ShopifySdkTest.java @@ -30,6 +30,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.MockitoAnnotations; import org.mockito.runners.MockitoJUnitRunner; import com.fasterxml.jackson.core.JsonProcessingException; @@ -41,6 +42,7 @@ import com.github.restdriver.clientdriver.capture.JsonBodyCapture; import com.github.restdriver.clientdriver.capture.StringBodyCapture; import com.shopify.exceptions.ShopifyClientException; +import com.shopify.exceptions.ShopifyEmptyLineItemsException; import com.shopify.exceptions.ShopifyErrorResponseException; import com.shopify.mappers.ShopifySdkObjectMapper; import com.shopify.model.Count; @@ -65,6 +67,8 @@ import com.shopify.model.ShopifyCustomersRoot; import com.shopify.model.ShopifyFulfillment; import com.shopify.model.ShopifyFulfillmentCreationRequest; +import com.shopify.model.ShopifyFulfillmentOrder; +import com.shopify.model.ShopifyFulfillmentOrderLineItem; import com.shopify.model.ShopifyFulfillmentRoot; import com.shopify.model.ShopifyFulfillmentUpdateRequest; import com.shopify.model.ShopifyGetCustomersRequest; @@ -133,6 +137,7 @@ public static void beforeClass() { @Before public void setUp() throws JsonProcessingException { + MockitoAnnotations.initMocks(this); final String subdomainUrl = driver.getBaseUrl(); final String expectedPath = new StringBuilder().append(FORWARD_SLASH).append(ShopifySdk.API_VERSION_PREFIX) @@ -260,7 +265,7 @@ public void givenSomeClientCredentialsAndUnexpectedStatusWhenCallingToTheShopify } @Test - public void givenSomeShopifyFulfillmenmtCreationRequestWhenCreatingShopifyFulfillmentThenCreateAndReturnFulfillment() + public void givenSomeShopifyFulfillmentCreationRequestWhenCreatingShopifyFulfillmentThenCreateAndReturnFulfillmentWithLegacyApi() throws JsonProcessingException, ConnectException { final ShopifyLineItem lineItem = new ShopifyLineItem(); @@ -299,6 +304,214 @@ public void givenSomeShopifyFulfillmenmtCreationRequestWhenCreatingShopifyFulfil } + @Test(expected = ShopifyEmptyLineItemsException.class) + public void givenSomeShopifyFulfillmentOrderWithNoCreateFulfillmentSupportedActionWhenCreatingShopifyFulfillmentThenGetAnUnsupportedActionException() + throws JsonProcessingException, ConnectException, ShopifyEmptyLineItemsException { + final String lineItemId = "987"; + final String fulfillmentOrderId = "1234"; + + final ShopifyLineItem lineItem = new ShopifyLineItem(); + lineItem.setId(lineItemId); + lineItem.setSku("some_sku"); + lineItem.setQuantity(5L); + + List fulfillmentOrderLineItems = new LinkedList<>(); + ShopifyFulfillmentOrderLineItem fulfillmentOrderLineItem = new ShopifyFulfillmentOrderLineItem(); + fulfillmentOrderLineItem.setQuantity(1); + fulfillmentOrderLineItem.setLineItemId(lineItemId); + fulfillmentOrderLineItem.setFulfillableQuantity(1); + fulfillmentOrderLineItem.setFulfillmentOrderId(fulfillmentOrderId); + fulfillmentOrderLineItems.add(fulfillmentOrderLineItem); + + final List supportedActions = new LinkedList<>(); + supportedActions.add("move"); + + final ShopifyFulfillmentOrder fulfillmentOrder = new ShopifyFulfillmentOrder(); + fulfillmentOrder.setId(fulfillmentOrderId); + fulfillmentOrder.setLineItems(fulfillmentOrderLineItems); + fulfillmentOrder.setSupportedActions(supportedActions); + fulfillmentOrder.setAssignedLocationId("5678"); + final List fulfillmentOrders = new LinkedList<>(); + fulfillmentOrders.add(fulfillmentOrder); + + final ShopifyFulfillment currentFulfillment = buildShopifyFulfillment(lineItem); + final ShopifyFulfillmentRoot shopifyFulfillmentRoot = new ShopifyFulfillmentRoot(); + shopifyFulfillmentRoot.setFulfillment(currentFulfillment); + + final ShopifyFulfillmentCreationRequest request = ShopifyFulfillmentCreationRequest.newBuilder() + .withOrderId("1234").withTrackingCompany("USPS").withTrackingNumber("12341234").withNotifyCustomer(true) + .withLineItems(Arrays.asList(lineItem)).withLocationId("1") + .withTrackingUrls(Arrays.asList("tracking_url1", "tracking_url2")).build(); + + shopifySdk.createFulfillment(request, fulfillmentOrders); + } + + @Test(expected = ShopifyEmptyLineItemsException.class) + public void givenSomeShopifyFulfillmentOrderAndFulfillmentWithNoMatchingLineItemsWhenCreatingShopifyFulfillmentThenGetAnUnsupportedActionException() + throws JsonProcessingException, ConnectException, ShopifyEmptyLineItemsException { + final String lineItemId = "987"; + final String fulfillmentOrderId = "1234"; + + final ShopifyLineItem lineItem = new ShopifyLineItem(); + lineItem.setId("lineItemId"); + lineItem.setSku("some_sku"); + lineItem.setQuantity(5L); + + List fulfillmentOrderLineItems = new LinkedList<>(); + ShopifyFulfillmentOrderLineItem fulfillmentOrderLineItem = new ShopifyFulfillmentOrderLineItem(); + fulfillmentOrderLineItem.setQuantity(1); + fulfillmentOrderLineItem.setLineItemId(lineItemId); + fulfillmentOrderLineItem.setFulfillableQuantity(1); + fulfillmentOrderLineItem.setFulfillmentOrderId(fulfillmentOrderId); + fulfillmentOrderLineItems.add(fulfillmentOrderLineItem); + + final List supportedActions = new LinkedList<>(); + supportedActions.add("move"); + supportedActions.add("create_fulfillment"); + + final ShopifyFulfillmentOrder fulfillmentOrder = new ShopifyFulfillmentOrder(); + fulfillmentOrder.setId(fulfillmentOrderId); + fulfillmentOrder.setLineItems(fulfillmentOrderLineItems); + fulfillmentOrder.setSupportedActions(supportedActions); + fulfillmentOrder.setAssignedLocationId("5678"); + final List fulfillmentOrders = new LinkedList<>(); + fulfillmentOrders.add(fulfillmentOrder); + + final ShopifyFulfillment currentFulfillment = buildShopifyFulfillment(lineItem); + final ShopifyFulfillmentRoot shopifyFulfillmentRoot = new ShopifyFulfillmentRoot(); + shopifyFulfillmentRoot.setFulfillment(currentFulfillment); + + final ShopifyFulfillmentCreationRequest request = ShopifyFulfillmentCreationRequest.newBuilder() + .withOrderId("1234").withTrackingCompany("USPS").withTrackingNumber("12341234").withNotifyCustomer(true) + .withLineItems(Arrays.asList(lineItem)).withLocationId("1") + .withTrackingUrls(Arrays.asList("tracking_url1", "tracking_url2")).build(); + + shopifySdk.createFulfillment(request, fulfillmentOrders); + } + + @Test + public void givenSomeShopifyFulfillmentCreationRequestWhenCreatingShopifyFulfillmentThenCreateAndReturnFulfillmentWithFulfillmentOrderApi() + throws JsonProcessingException, ConnectException, ShopifyEmptyLineItemsException { + final String lineItemId = "987"; + final String fulfillmentOrderId = "1234"; + + final ShopifyLineItem lineItem = new ShopifyLineItem(); + lineItem.setId(lineItemId); + lineItem.setSku("some_sku"); + lineItem.setQuantity(5L); + + List fulfillmentOrderLineItems = new LinkedList<>(); + ShopifyFulfillmentOrderLineItem fulfillmentOrderLineItem = new ShopifyFulfillmentOrderLineItem(); + fulfillmentOrderLineItem.setQuantity(1); + fulfillmentOrderLineItem.setLineItemId(lineItemId); + fulfillmentOrderLineItem.setFulfillableQuantity(1); + fulfillmentOrderLineItem.setFulfillmentOrderId(fulfillmentOrderId); + fulfillmentOrderLineItems.add(fulfillmentOrderLineItem); + + final List supportedActions = new LinkedList<>(); + supportedActions.add("move"); + supportedActions.add("create_fulfillment"); + + final ShopifyFulfillmentOrder fulfillmentOrder = new ShopifyFulfillmentOrder(); + fulfillmentOrder.setId(fulfillmentOrderId); + fulfillmentOrder.setLineItems(fulfillmentOrderLineItems); + fulfillmentOrder.setSupportedActions(supportedActions); + fulfillmentOrder.setAssignedLocationId("5678"); + final List fulfillmentOrders = new LinkedList<>(); + fulfillmentOrders.add(fulfillmentOrder); + + final String expectedPath = new StringBuilder().append(FORWARD_SLASH).append(ShopifySdk.API_VERSION_PREFIX) + .append(FORWARD_SLASH).append(SOME_API_VERSION).append(FORWARD_SLASH).append(ShopifySdk.FULFILLMENTS) + .toString(); + final ShopifyFulfillment currentFulfillment = buildShopifyFulfillment(lineItem); + final ShopifyFulfillmentRoot shopifyFulfillmentRoot = new ShopifyFulfillmentRoot(); + shopifyFulfillmentRoot.setFulfillment(currentFulfillment); + + final String expectedResponseBodyString = getJsonString(ShopifyFulfillmentRoot.class, shopifyFulfillmentRoot); + + final Status expectedStatus = Status.CREATED; + final int expectedStatusCode = expectedStatus.getStatusCode(); + final JsonBodyCapture actualRequestBody = new JsonBodyCapture(); + driver.addExpectation( + onRequestTo(expectedPath).withHeader(ShopifySdk.ACCESS_TOKEN_HEADER, accessToken) + .withMethod(Method.POST).capturingBodyIn(actualRequestBody), + giveResponse(expectedResponseBodyString, MediaType.APPLICATION_JSON).withStatus(expectedStatusCode) + .withHeader(ShopifySdk.DEPRECATED_REASON_HEADER, "Call to be removed from API.") + .withHeader("Location", new StringBuilder().append("https://test.myshopify.com/admin") + .append(expectedPath).toString())); + + final ShopifyFulfillmentCreationRequest request = ShopifyFulfillmentCreationRequest.newBuilder() + .withOrderId("1234").withTrackingCompany("USPS").withTrackingNumber("12341234").withNotifyCustomer(true) + .withLineItems(Arrays.asList(lineItem)).withLocationId("1") + .withTrackingUrls(Arrays.asList("tracking_url1", "tracking_url2")).build(); + + final ShopifyFulfillment actualShopifyFulfillment = shopifySdk.createFulfillment(request, fulfillmentOrders); + + assertValidFulfillment(currentFulfillment, actualShopifyFulfillment); + } + + @Test + public void givenSomeShopifyFulfillmentCreationRequestWhenCreatingShopifyFulfillmentThenCreateAndReturnFulfillmentWithFulfillmentOrderApiAndFulfillmentToADifferentShopLocation() + throws JsonProcessingException, ConnectException, ShopifyEmptyLineItemsException { + final String lineItemId = "987"; + final String fulfillmentOrderId = "1234"; + + final ShopifyLineItem lineItem = new ShopifyLineItem(); + lineItem.setId(lineItemId); + lineItem.setSku("some_sku"); + lineItem.setQuantity(5L); + + List fulfillmentOrderLineItems = new LinkedList<>(); + ShopifyFulfillmentOrderLineItem fulfillmentOrderLineItem = new ShopifyFulfillmentOrderLineItem(); + fulfillmentOrderLineItem.setQuantity(1); + fulfillmentOrderLineItem.setLineItemId(lineItemId); + fulfillmentOrderLineItem.setFulfillableQuantity(1); + fulfillmentOrderLineItem.setFulfillmentOrderId(fulfillmentOrderId); + fulfillmentOrderLineItems.add(fulfillmentOrderLineItem); + + final List supportedActions = new LinkedList<>(); + supportedActions.add("move"); + supportedActions.add("create_fulfillment"); + + final ShopifyFulfillmentOrder fulfillmentOrder = new ShopifyFulfillmentOrder(); + fulfillmentOrder.setId(fulfillmentOrderId); + fulfillmentOrder.setLineItems(fulfillmentOrderLineItems); + fulfillmentOrder.setSupportedActions(supportedActions); + fulfillmentOrder.setAssignedLocationId("5678"); + final List fulfillmentOrders = new LinkedList<>(); + fulfillmentOrders.add(fulfillmentOrder); + + final String expectedPath = new StringBuilder().append(FORWARD_SLASH).append(ShopifySdk.API_VERSION_PREFIX) + .append(FORWARD_SLASH).append(SOME_API_VERSION).append(FORWARD_SLASH).append(ShopifySdk.FULFILLMENTS) + .toString(); + final ShopifyFulfillment currentFulfillment = buildShopifyFulfillment(lineItem); + final ShopifyFulfillmentRoot shopifyFulfillmentRoot = new ShopifyFulfillmentRoot(); + shopifyFulfillmentRoot.setFulfillment(currentFulfillment); + + final String expectedResponseBodyString = getJsonString(ShopifyFulfillmentRoot.class, shopifyFulfillmentRoot); + + final Status expectedStatus = Status.CREATED; + final int expectedStatusCode = expectedStatus.getStatusCode(); + final JsonBodyCapture actualRequestBody = new JsonBodyCapture(); + driver.addExpectation( + onRequestTo(expectedPath).withHeader(ShopifySdk.ACCESS_TOKEN_HEADER, accessToken) + .withMethod(Method.POST).capturingBodyIn(actualRequestBody), + giveResponse(expectedResponseBodyString, MediaType.APPLICATION_JSON).withStatus(expectedStatusCode) + .withHeader(ShopifySdk.DEPRECATED_REASON_HEADER, "Call to be removed from API.") + .withHeader("Location", new StringBuilder().append("https://test.myshopify.com/admin") + .append(expectedPath).toString())); + + final ShopifyFulfillmentCreationRequest request = ShopifyFulfillmentCreationRequest.newBuilder() + .withOrderId("1234").withTrackingCompany("USPS").withTrackingNumber("12341234").withNotifyCustomer(true) + .withLineItems(Arrays.asList(lineItem)).withLocationId("1") + .withTrackingUrls(Arrays.asList("tracking_url1", "tracking_url2")).build(); + + final ShopifyFulfillment actualShopifyFulfillment = shopifySdk.createFulfillment(request, fulfillmentOrders); + + assertValidFulfillment(currentFulfillment, actualShopifyFulfillment); + + } + @Test(expected = ShopifyClientException.class) public void givenSomeClientCredentialsAndRateLimitedWhenCallinglToTheShopifyApiThenExpectShopifyClientException() throws JsonProcessingException { @@ -331,11 +544,10 @@ public void givenSomeClientCredentialsAndRateLimitedWhenCallinglToTheShopifyApiT .withTrackingUrls(Arrays.asList("tracking_url1", "tracking_url2")).build(); shopifySdk.createFulfillment(request); - } @Test - public void givenSomeShopifyFulfillmenmtUpdateRequestWhenUpdatingShopifyFulfillmentThenUpdateAndReturnFulfillment() + public void givenSomeShopifyFulfillmentUpdateRequestWhenUpdatingShopifyFulfillmentThenUpdateAndReturnFulfillmentWithLegacyApi() throws JsonProcessingException { final ShopifyLineItem lineItem = new ShopifyLineItem(); @@ -371,6 +583,41 @@ public void givenSomeShopifyFulfillmenmtUpdateRequestWhenUpdatingShopifyFulfillm } + @Test + public void givenSomeShopifyFulfillmentUpdateRequestWhenUpdatingShopifyFulfillmentThenUpdateAndReturnFulfillmentWithFulfillmentOrderApi() + throws JsonProcessingException { + + final ShopifyLineItem lineItem = new ShopifyLineItem(); + lineItem.setId("some_line_item_id"); + lineItem.setSku("some_sku"); + lineItem.setQuantity(5L); + + final String expectedPath = new StringBuilder().append(FORWARD_SLASH).append(ShopifySdk.API_VERSION_PREFIX) + .append(FORWARD_SLASH).append(SOME_API_VERSION).append(FORWARD_SLASH).append(ShopifySdk.FULFILLMENTS) + .append("/4567").append(FORWARD_SLASH).append(ShopifySdk.UPDATE_TRACKING).toString(); + final ShopifyFulfillment currentFulfillment = buildShopifyFulfillment(lineItem); + final ShopifyFulfillmentRoot shopifyFulfillmentRoot = new ShopifyFulfillmentRoot(); + shopifyFulfillmentRoot.setFulfillment(currentFulfillment); + + final String expectedResponseBodyString = getJsonString(ShopifyFulfillmentRoot.class, shopifyFulfillmentRoot); + + final Status expectedStatus = Status.OK; + final int expectedStatusCode = expectedStatus.getStatusCode(); + final JsonBodyCapture actualRequestBody = new JsonBodyCapture(); + driver.addExpectation( + onRequestTo(expectedPath).withHeader(ShopifySdk.ACCESS_TOKEN_HEADER, accessToken) + .withMethod(Method.POST).capturingBodyIn(actualRequestBody), + giveResponse(expectedResponseBodyString, MediaType.APPLICATION_JSON).withStatus(expectedStatusCode)); + + final ShopifyFulfillmentUpdateRequest request = ShopifyFulfillmentUpdateRequest.newBuilder() + .withCurrentShopifyFulfillment(currentFulfillment).withTrackingCompany("USPS") + .withTrackingNumber("12341234").withNotifyCustomer(true).withLineItems(Arrays.asList(lineItem)) + .withLocationId("1").withTrackingUrls(Arrays.asList("tracking_url1", "tracking_url2")).build(); + + final ShopifyFulfillment actualShopifyFulfillment = shopifySdk.updateFulfillmentTrackingInfo(request); + assertValidFulfillment(currentFulfillment, actualShopifyFulfillment); + } + @Test public void givenSomePageAndCreatedAtMinAndCreatedAtMaxOrdersWhenRetrievingOrdersThenRetrieveOrdersWithCorrectValues() throws JsonProcessingException {