diff --git a/shopify/HELP.adoc b/shopify/HELP.adoc
new file mode 100644
index 000000000..c7c07ab54
--- /dev/null
+++ b/shopify/HELP.adoc
@@ -0,0 +1 @@
+The Shopify Help file template.
\ No newline at end of file
diff --git a/shopify/README.adoc b/shopify/README.adoc
new file mode 100644
index 000000000..1fef6b4d0
--- /dev/null
+++ b/shopify/README.adoc
@@ -0,0 +1,3 @@
+
+The files in this directory are used as templates for the ant create-component target, +
+please do not modify them without understanding the effect that it will have on that build target.
\ No newline at end of file
diff --git a/shopify/config/ShopifyUiLabels.xml b/shopify/config/ShopifyUiLabels.xml
new file mode 100644
index 000000000..21272c820
--- /dev/null
+++ b/shopify/config/ShopifyUiLabels.xml
@@ -0,0 +1,72 @@
+
+
+
+
+
+ Shopify Application
+ Shopify应用程序
+ Shopify應用程式
+
+
+ OFBiz: Shopify
+ OFBiz: Shopify
+
+
+ Part of the Apache OFBiz Family of Open Source Software
+ Un modulo della famiglia di software open source Apache OFBiz
+ 开源软件OFBiz的组成部分
+ 開源軟體OFBiz的組成部分
+
+
+ You are not allowed to view this page.
+ 不允许你浏览这个页面。
+ 不允許您檢視這個頁面.
+
+
+ Shopify Configurations
+
+
+ Add Shopify Configuration
+
+
+ Shopify Configuration added successfully
+
+
+ Required Field is missing : API Url
+
+
+ Required Field is missing : Username
+
+
+ Required Field is missing : Password
+
+
+ Exception in createUpdateShopifyConfiguration
+
+
+ Shopify api url is not configured.
+
+
+ Shopify api username is not configured.
+
+
+ Shopify api password is not configured.
+
+
diff --git a/shopify/data/ShopifyDemoData.xml b/shopify/data/ShopifyDemoData.xml
new file mode 100644
index 000000000..ad7d88fdb
--- /dev/null
+++ b/shopify/data/ShopifyDemoData.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shopify/data/ShopifySecurityGroupDemoData.xml b/shopify/data/ShopifySecurityGroupDemoData.xml
new file mode 100644
index 000000000..ed98da0af
--- /dev/null
+++ b/shopify/data/ShopifySecurityGroupDemoData.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/shopify/data/ShopifySecurityPermissionSeedData.xml b/shopify/data/ShopifySecurityPermissionSeedData.xml
new file mode 100644
index 000000000..47d6aa721
--- /dev/null
+++ b/shopify/data/ShopifySecurityPermissionSeedData.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/shopify/data/ShopifyTypeData.xml b/shopify/data/ShopifyTypeData.xml
new file mode 100644
index 000000000..b76e67e05
--- /dev/null
+++ b/shopify/data/ShopifyTypeData.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shopify/data/helpdata/HELP_Shopify.xml b/shopify/data/helpdata/HELP_Shopify.xml
new file mode 100644
index 000000000..5502e64bb
--- /dev/null
+++ b/shopify/data/helpdata/HELP_Shopify.xml
@@ -0,0 +1,29 @@
+
+
+
+
+ Shopify Overview
+ The Shopify Help file template.
+
+
diff --git a/shopify/documents/Shopify.xml b/shopify/documents/Shopify.xml
new file mode 100644
index 000000000..87812afff
--- /dev/null
+++ b/shopify/documents/Shopify.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+ The Shopify Component
+
+
+
+
diff --git a/shopify/entitydef/entitymodel.xml b/shopify/entitydef/entitymodel.xml
new file mode 100644
index 000000000..77f21fcf5
--- /dev/null
+++ b/shopify/entitydef/entitymodel.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+ Entity of Shopify Component
+ None
+
+
+
+
\ No newline at end of file
diff --git a/shopify/ofbiz-component.xml b/shopify/ofbiz-component.xml
new file mode 100644
index 000000000..aef00932e
--- /dev/null
+++ b/shopify/ofbiz-component.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/shopify/servicedef/services.xml b/shopify/servicedef/services.xml
new file mode 100644
index 000000000..4aa424f9e
--- /dev/null
+++ b/shopify/servicedef/services.xml
@@ -0,0 +1,46 @@
+
+
+
+
+ Shopify Services
+
+ 1.0
+
+
+ Create Update Shopify Configuration
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/shopify/src/main/java/org/apache/ofbiz/shopify/ShopifyServices.java b/shopify/src/main/java/org/apache/ofbiz/shopify/ShopifyServices.java
new file mode 100644
index 000000000..276bcc722
--- /dev/null
+++ b/shopify/src/main/java/org/apache/ofbiz/shopify/ShopifyServices.java
@@ -0,0 +1,210 @@
+package org.apache.ofbiz.shopify;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import org.apache.ofbiz.base.lang.JSON;
+import org.apache.ofbiz.base.util.*;
+import org.apache.ofbiz.entity.Delegator;
+import org.apache.ofbiz.entity.GenericEntityException;
+import org.apache.ofbiz.entity.GenericValue;
+import org.apache.ofbiz.entity.util.EntityQuery;
+import org.apache.ofbiz.entity.util.EntityUtilProperties;
+import org.apache.ofbiz.service.DispatchContext;
+import org.apache.ofbiz.service.LocalDispatcher;
+import org.apache.ofbiz.service.ModelService;
+import org.apache.ofbiz.service.ServiceUtil;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.*;
+
+public class ShopifyServices {
+
+ public static final String MODULE = ShopifyServices.class.getName();
+ public static final String resource = "ShopifyUiLabels";
+
+ public static Map createUpdateShopifyConfiguration(DispatchContext dctx, Map context) {
+ Delegator delegator = dctx.getDelegator();
+ LocalDispatcher dispatcher = dctx.getDispatcher();
+ Locale locale = (Locale) context.get("locale");
+ Map result = new HashMap<>();
+ String apiUrl = (String) context.get("apiUrl");
+ String username = (String) context.get("username");
+ String password = (String) context.get("password");
+ try {
+ if (UtilValidate.isNotEmpty(apiUrl)) {
+ GenericValue apiUrlProperty = EntityQuery.use(delegator).from("SystemProperty").where("systemResourceId", "ShopifyConfig", "systemPropertyId", "shopifyApiUrl").queryOne();
+ if (apiUrlProperty == null) {
+ GenericValue systemProperty = delegator.makeValue("SystemProperty", UtilMisc.toMap("systemResourceId", "ShopifyConfig", "systemPropertyId", "shopifyApiUrl", "systemPropertyValue", apiUrl));
+ systemProperty.create();
+ } else {
+ apiUrlProperty.set("systemPropertyValue", apiUrl);
+ apiUrlProperty.store();
+ }
+ }
+ if (UtilValidate.isNotEmpty(username)) {
+ GenericValue usernameProperty = EntityQuery.use(delegator).from("SystemProperty").where("systemResourceId", "ShopifyConfig", "systemPropertyId", "shopifyUsername").queryOne();
+ if (usernameProperty == null) {
+ GenericValue systemProperty = delegator.makeValue("SystemProperty", UtilMisc.toMap("systemResourceId", "ShopifyConfig", "systemPropertyId", "shopifyUsername", "systemPropertyValue", username));
+ systemProperty.create();
+ } else {
+ usernameProperty.set("systemPropertyValue", username);
+ usernameProperty.store();
+ }
+ }
+ if (UtilValidate.isNotEmpty(password)) {
+ GenericValue passwordProperty = EntityQuery.use(delegator).from("SystemProperty").where("systemResourceId", "ShopifyConfig", "systemPropertyId", "shopifyPassword").queryOne();
+ if (passwordProperty == null) {
+ GenericValue systemProperty = delegator.makeValue("SystemProperty", UtilMisc.toMap("systemResourceId", "ShopifyConfig", "systemPropertyId", "shopifyPassword", "systemPropertyValue", password));
+ systemProperty.create();
+ } else {
+ passwordProperty.set("systemPropertyValue", password);
+ passwordProperty.store();
+ }
+ }
+ } catch (GenericEntityException e) {
+ Debug.logError("Exception in createUpdateShopifyConfiguration " + e, MODULE);
+ return ServiceUtil.returnFailure(UtilProperties.getMessage(resource, "ExceptionInCreateUpdateShopifyConfiguration", locale));
+ }
+ result.put(ModelService.SUCCESS_MESSAGE, UtilProperties.getMessage(resource, "ShopifyConfigurationAdded", locale));
+ return result;
+ }
+
+ private static Map sendShopifyRequest(Map context) {
+ Map parameters = (Map) (context.get("parameters"));
+ Map queryMap = (Map) (context.get("queryMap"));
+ Map headerMap = (Map) (context.get("headerMap"));
+ Delegator delegator = (Delegator) context.get("delegator");
+ Locale locale = (Locale) context.get("locale");
+ String endpoint = (String) context.get("endpoint");
+ String contentType = (String) context.get("contentType");
+ String requestType = (String) context.get("requestType");
+ Map httpResponse = new HashMap<>();
+
+ try {
+ Properties configProperties = EntityUtilProperties.getProperties(delegator, "ShopifyConfig");
+ String apiUrl = (String) configProperties.get("shopifyApiUrl");
+ if (UtilValidate.isEmpty(apiUrl)) {
+ Debug.logError(UtilProperties.getMessage(resource, "ShopifyApiUrlMissing", locale), MODULE);
+ return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ShopifyApiUrlMissing", locale));
+ } else {
+ String nextLink = "";
+ List responseStringList = new ArrayList<>();
+ String username = (String) configProperties.get("shopifyUsername");
+ String password = (String) configProperties.get("shopifyPassword");
+ if (UtilValidate.isEmpty(username)) {
+ Debug.logError(UtilProperties.getMessage(resource, "ShopifyApiUsernameMissing", locale), MODULE);
+ return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ShopifyApiUsernameMissing", locale));
+ }
+ if (UtilValidate.isEmpty(password)) {
+ Debug.logError(UtilProperties.getMessage(resource, "ShopifyApiPasswordMissing", locale), MODULE);
+ return ServiceUtil.returnError(UtilProperties.getMessage(resource, "ShopifyApiPasswordMissing", locale));
+ }
+ do {
+ String url = "";
+ url = apiUrl + endpoint;
+ if (UtilValidate.isNotEmpty(nextLink)) {
+ url = nextLink;
+ }
+ HttpClient httpClient = new HttpClient(url);
+ httpClient.setBasicAuthInfo(username, password);
+ if (UtilValidate.isNotEmpty(headerMap)) {
+ httpClient.setHeaders(headerMap);
+ }
+
+ // Setting default limit
+ if (UtilValidate.isEmpty(parameters.get("limit"))) {
+ parameters.put("limit", 250);
+ }
+
+ if (UtilValidate.isNotEmpty(parameters)) {
+ httpClient.setParameters(parameters);
+ }
+ if (UtilValidate.isNotEmpty(queryMap)) {
+ JSON json = JSON.from(queryMap);
+ String jsonStr = null;
+ if (UtilValidate.isNotEmpty(json)) {
+ jsonStr = json.toString();
+ }
+ httpClient.setRawStream(jsonStr);
+ }
+ httpClient.setTimeout(60 * 1000);
+ httpClient.setContentType(contentType);
+ httpClient.setDebug(false);
+
+ String httpResponseString = null;
+ if ("GET".equalsIgnoreCase(requestType)) {
+ httpResponseString = httpClient.get();
+ } else {
+ httpResponseString = httpClient.post();
+ }
+ if (httpResponseString != null) {
+ //Preparing a list of responses, this will be later iterated to prepare a combined response coming from paginated requests
+ responseStringList.add(httpResponseString);
+ }
+
+ /* Handling pagination, Shopify returns 250 records at maximum. It does return links to previous and next set of records in the response header.*/
+ String paginationLink = httpClient.getResponseHeader("Link");
+ List links = StringUtil.split(paginationLink, ","); // May contain previous and next link
+ if (UtilValidate.isNotEmpty(links)) {
+ for (String link : links) {
+ List parsedLink = StringUtil.split(link, ";");
+ // obtaining next link
+ if (link.contains("\"next\"")) {
+ nextLink = parsedLink.get(0);
+ if (UtilValidate.isNotEmpty(nextLink)) {
+ if (nextLink.indexOf('<') >= 0 && nextLink.lastIndexOf('>') >= 0) {
+ nextLink = nextLink.substring(nextLink.indexOf('<') + 1, nextLink.lastIndexOf('>'));
+ }
+ }
+ } else {
+ nextLink = "";
+ }
+ }
+ } else {
+ nextLink = "";
+ }
+ } while (UtilValidate.isNotEmpty(nextLink));
+
+ if (UtilValidate.isNotEmpty(responseStringList)) {
+ if (responseStringList.size() > 1) {
+ String key = "";
+ List