Repository: authorjapps/zerocode
Pull request #145 (2018-10-23 15:58:50)

ISS-141 # Added support for x-www-form-urlencoded request format


Patch:
diff --git a/src/main/java/org/jsmart/zerocode/core/httpclient/BasicHttpClient.java b/src/main/java/org/jsmart/zerocode/core/httpclient/BasicHttpClient.java
index 62895850..2aaa27bb 100644
--- a/src/main/java/org/jsmart/zerocode/core/httpclient/BasicHttpClient.java
+++ b/src/main/java/org/jsmart/zerocode/core/httpclient/BasicHttpClient.java
@@ -1,10 +1,13 @@
 package org.jsmart.zerocode.core.httpclient;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
 import org.apache.commons.io.IOUtils;
 import org.apache.http.Header;
 import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
 import org.apache.http.client.CookieStore;
 import org.apache.http.client.entity.EntityBuilder;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
 import org.apache.http.client.methods.CloseableHttpResponse;
 import org.apache.http.client.methods.RequestBuilder;
 import org.apache.http.conn.ssl.NoopHostnameVerifier;
@@ -13,18 +16,20 @@ import org.apache.http.entity.mime.MultipartEntityBuilder;
 import org.apache.http.impl.client.BasicCookieStore;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicNameValuePair;
+import org.jsmart.zerocode.core.utils.HelperJsonUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import javax.net.ssl.SSLContext;
 import javax.ws.rs.core.Response;
 import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 
 import static org.apache.http.entity.ContentType.APPLICATION_JSON;
 import static org.jsmart.zerocode.core.httpclient.utils.FileUploadUtils.*;
-import static org.jsmart.zerocode.core.httpclient.utils.HeaderUtils.hasMultiPartHeader;
 import static org.jsmart.zerocode.core.httpclient.utils.HeaderUtils.processFrameworkDefault;
 import static org.jsmart.zerocode.core.httpclient.utils.UrlQueryParamsUtils.setQueryParams;
 import static org.jsmart.zerocode.core.utils.HelperJsonUtils.getContentAsItIsJson;
@@ -35,6 +40,7 @@ public class BasicHttpClient {
     public static final String FILES_FIELD = "files";
     public static final String BOUNDARY_FIELD = "boundary";
     public static final String MULTIPART_FORM_DATA = "multipart/form-data";
+    public static final String APPLICATION_FORM_URL_ENCODED = "application/x-www-form-urlencoded";
     public static final String CONTENT_TYPE = "Content-Type";
 
     private Object COOKIE_JSESSIONID_VALUE;
@@ -186,7 +192,7 @@ public class BasicHttpClient {
      * @param queryParams - Query parameters to pass
      * @return : Effective url
      */
-    public String handleUrlAndQueryParams(String httpUrl, Map<String, Object> queryParams) {
+    public String handleUrlAndQueryParams(String httpUrl, Map<String, Object> queryParams) throws IOException {
         if (queryParams != null) {
             httpUrl = setQueryParams(httpUrl, queryParams);
         }
@@ -211,7 +217,7 @@ public class BasicHttpClient {
     /**
      * Override this method when you want to manipulate the request body passed from your test cases.
      * Otherwise the framework falls back to this default implementation.
-     *
+     * You can override this method via @UseHttpClient(YourCustomHttpClient.class)
      * @param body
      * @return
      */
@@ -226,6 +232,8 @@ public class BasicHttpClient {
      * Please see the following request builder to handle file uploads.
      *     - BasicHttpClient#createFileUploadRequestBuilder(java.lang.String, java.lang.String, java.lang.String)
      *
+     * You can override this method via @UseHttpClient(YourCustomHttpClient.class)
+     *
      * @param httpUrl
      * @param methodName
      * @param reqBodyAsString
@@ -246,6 +254,34 @@ public class BasicHttpClient {
         return requestBuilder;
     }
 
+    /**
+     * This is how framework makes the KeyValue pair when "application/x-www-form-urlencoded" headers
+     * is passed in the request.  In case you want to build or prepare the requests differently,
+     * you can override this method via @UseHttpClient(YourCustomHttpClient.class).
+     *
+     * @param httpUrl
+     * @param methodName
+     * @param reqBodyAsString
+     * @return
+     * @throws IOException
+     */
+    public RequestBuilder createFormUrlEncodedRequestBuilder(String httpUrl, String methodName, String reqBodyAsString) throws IOException {
+        RequestBuilder requestBuilder = RequestBuilder
+                .create(methodName)
+                .setUri(httpUrl);
+        if (reqBodyAsString != null) {
+            Map<String, Object> reqBodyMap = HelperJsonUtils.readObjectAsMap(reqBodyAsString);
+            List<NameValuePair> reqBody = new ArrayList<>();
+             for(String key : reqBodyMap.keySet()) {
+                 reqBody.add(new BasicNameValuePair(key, reqBodyMap.get(key).toString()));
+             }
+             HttpEntity httpEntity = new UrlEncodedFormEntity(reqBody);
+             requestBuilder.setEntity(httpEntity);
+            requestBuilder.setHeader(CONTENT_TYPE, APPLICATION_FORM_URL_ENCODED);
+        }
+        return requestBuilder;
+    }
+
     /**
      * This is the http request builder for file uploads, using Apache Http Client. In case you want to build
      * or prepare the requests differently, you can override this method.
@@ -256,6 +292,8 @@ public class BasicHttpClient {
      * also send more request-params and "boundary" from the test cases if needed. The boundary defaults to an unique
      * string of local-date-time-stamp if not provided in the request.
      *
+     * You can override this method via @UseHttpClient(YourCustomHttpClient.class)
+     *
      * @param httpUrl
      * @param methodName
      * @param reqBodyAsString
@@ -307,13 +345,27 @@ public class BasicHttpClient {
         }
     }
 
-    private RequestBuilder createRequestBuilder(String httpUrl, String methodName, Map<String, Object> headers, String reqBodyAsString) throws IOException {
-        RequestBuilder requestBuilder;
-        if (hasMultiPartHeader(headers)) {
-            requestBuilder = createFileUploadRequestBuilder(httpUrl, methodName, reqBodyAsString);
-        } else {
-            requestBuilder = createDefaultRequestBuilder(httpUrl, methodName, reqBodyAsString);
+    public RequestBuilder createRequestBuilder(String httpUrl, String methodName, Map<String, Object> headers, String reqBodyAsString) throws IOException {
+
+        String contentType = headers != null? (String) headers.get(CONTENT_TYPE) :null;
+
+        if(contentType!=null){
+
+            if(contentType.equals(MULTIPART_FORM_DATA)){
+
+                return createFileUploadRequestBuilder(httpUrl, methodName, reqBodyAsString);
+
+            } else if(contentType.equals(APPLICATION_FORM_URL_ENCODED)) {
+
+                return createFormUrlEncodedRequestBuilder(httpUrl, methodName, reqBodyAsString);
+            }
+            // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+            // Extention- Any other header types to be specially handled here
+            // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
+            // else if(contentType.equals("OTHER-TYPES")){
+            //    Handling logic
+            // }
         }
-        return requestBuilder;
+        return createDefaultRequestBuilder(httpUrl, methodName, reqBodyAsString);
     }
 }
diff --git a/src/main/java/org/jsmart/zerocode/core/httpclient/utils/HeaderUtils.java b/src/main/java/org/jsmart/zerocode/core/httpclient/utils/HeaderUtils.java
index ffadcfee..6da59139 100644
--- a/src/main/java/org/jsmart/zerocode/core/httpclient/utils/HeaderUtils.java
+++ b/src/main/java/org/jsmart/zerocode/core/httpclient/utils/HeaderUtils.java
@@ -31,12 +31,4 @@ public class HeaderUtils {
             requestBuilder.removeHeaders(key);
         }
     }
-
-    public static boolean hasMultiPartHeader(Map headersMap) {
-        if(headersMap == null){
-            return false;
-        }
-        String contentType = (String) headersMap.get(CONTENT_TYPE);
-        return contentType != null ? contentType.contains(MULTIPART_FORM_DATA) : false;
-    }
 }
diff --git a/src/main/java/org/jsmart/zerocode/core/httpclient/utils/UrlQueryParamsUtils.java b/src/main/java/org/jsmart/zerocode/core/httpclient/utils/UrlQueryParamsUtils.java
index 9c193799..7ed1f51d 100644
--- a/src/main/java/org/jsmart/zerocode/core/httpclient/utils/UrlQueryParamsUtils.java
+++ b/src/main/java/org/jsmart/zerocode/core/httpclient/utils/UrlQueryParamsUtils.java
@@ -1,32 +1,41 @@
 package org.jsmart.zerocode.core.httpclient.utils;
 
+import org.apache.http.HttpEntity;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.util.EntityUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
-import static java.lang.String.format;
+import static java.util.Optional.ofNullable;
 
 public class UrlQueryParamsUtils {
     private static final Logger LOGGER = LoggerFactory.getLogger(UrlQueryParamsUtils.class);
 
-    public static String setQueryParams(String httpUrl, Map<String, Object> queryParams) {
+    public static String setQueryParams(String httpUrl, Map<String, Object> queryParams) throws IOException {
         String qualifiedQueryParams = createQualifiedQueryParams(queryParams);
-        httpUrl = httpUrl + qualifiedQueryParams;
+        httpUrl = httpUrl + "?" + qualifiedQueryParams;
 
         LOGGER.info("### Effective url is : " + httpUrl);
         return httpUrl;
     }
 
-    protected static String createQualifiedQueryParams(Map<String, Object> queryParamsMap) {
-        String qualifiedQueryParam = "";
-        for (Object key : queryParamsMap.keySet()) {
-            if ("".equals(qualifiedQueryParam)) {
-                qualifiedQueryParam = "?" + format("%s=%s", key, queryParamsMap.get(key));
-            } else {
-                qualifiedQueryParam = qualifiedQueryParam + format("&%s=%s", key, queryParamsMap.get(key));
-            }
+    protected static String createQualifiedQueryParams(Map<String, Object> queryParamsMap) throws IOException {
+        queryParamsMap = ofNullable(queryParamsMap).orElse(new HashMap<>());
+        List<NameValuePair> nameValueList = new ArrayList<>();
+        for(String key : queryParamsMap.keySet()) {
+            nameValueList.add(new BasicNameValuePair(key, queryParamsMap.get(key).toString()));
         }
+        HttpEntity httpEntity = new UrlEncodedFormEntity(nameValueList);
+        String qualifiedQueryParam = EntityUtils.toString(httpEntity, "UTF-8");
+
         LOGGER.info("### qualifiedQueryParams : " + qualifiedQueryParam);
         return qualifiedQueryParam;
     }
diff --git a/src/main/java/org/jsmart/zerocode/core/utils/HelperJsonUtils.java b/src/main/java/org/jsmart/zerocode/core/utils/HelperJsonUtils.java
index c2317875..3e264f6d 100755
--- a/src/main/java/org/jsmart/zerocode/core/utils/HelperJsonUtils.java
+++ b/src/main/java/org/jsmart/zerocode/core/utils/HelperJsonUtils.java
@@ -13,7 +13,7 @@ import java.util.Map;
 
 // TODO: Move this to Smartutils class
 public class HelperJsonUtils {
-    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(HelperJsonUtils.class);
+    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(HelperJsonUtils.class);
     private static ObjectMapper mapper = new ObjectMapperProvider().get();
 
 
@@ -49,13 +49,13 @@ public class HelperJsonUtils {
 
     }
 
-    public static Map<String, Object> readHeadersAsMap(Object headers) {
+    public static Map<String, Object> readObjectAsMap(Object jsonContent) {
         Map<String, Object> map = new HashMap<String, Object>();
         try {
-            map = (new ObjectMapperProvider()).get().readValue(headers.toString(), HashMap.class);
-        } catch (IOException e) {
-            // return empty map if exception occurs
-            return map;
+            map = mapper.readValue(jsonContent.toString(), HashMap.class);
+        } catch (IOException exx) {
+            LOGGER.error("Exception occurred during parse to HashMap - " + exx);
+            throw new RuntimeException(exx);
         }
 
         return map;
@@ -63,7 +63,7 @@ public class HelperJsonUtils {
 
     public static String createAndReturnAssertionResultJson(int httpResponseCode,
                                                             String resultBodyContent, String locationHref) {
-        logger.debug("\n#locationHref: " + locationHref);
+        LOGGER.debug("\n#locationHref: " + locationHref);
 
         if (StringUtils.isEmpty(resultBodyContent)) {
             resultBodyContent = "{}";
@@ -81,7 +81,7 @@ public class HelperJsonUtils {
     }
 
     private void setRequestHeaders(Object headers, ClientRequest clientExecutor) {
-        Map<String, Object> headersMap = HelperJsonUtils.readHeadersAsMap(headers);
+        Map<String, Object> headersMap = HelperJsonUtils.readObjectAsMap(headers);
         for (Object key : headersMap.keySet()) {
             clientExecutor.header((String) key, headersMap.get(key));
         }


Test patch:
diff --git a/src/test/java/org/jsmart/zerocode/core/httpclient/BasicHttpClientTest.java b/src/test/java/org/jsmart/zerocode/core/httpclient/BasicHttpClientTest.java
new file mode 100644
index 00000000..14eb6963
--- /dev/null
+++ b/src/test/java/org/jsmart/zerocode/core/httpclient/BasicHttpClientTest.java
@@ -0,0 +1,80 @@
+package org.jsmart.zerocode.core.httpclient;
+
+import org.apache.http.client.methods.RequestBuilder;
+import org.apache.http.util.EntityUtils;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.Is.is;
+
+public class BasicHttpClientTest {
+    private BasicHttpClient basicHttpClient;
+    private Map<String, Object> header;
+
+    @Before
+    public void setUp() {
+        basicHttpClient = new BasicHttpClient();
+        header = new HashMap<String, Object>();
+        header.put("Content-Type", "application/x-www-form-urlencoded");
+    }
+
+    @Test
+    public void createRequestBuilder() throws IOException {
+        header.put("Content-Type", "application/x-www-form-urlencoded");
+        String reqBodyAsString = "{\"Company\":\"Amazon\",\"age\":30,\"worthInBillion\":999.999}";
+        RequestBuilder requestBuilder = basicHttpClient.createRequestBuilder("/api/v1/founder", "POST", header, reqBodyAsString);
+        String nameValuePairString = EntityUtils.toString(requestBuilder.getEntity(), "UTF-8");
+        assertThat(requestBuilder.getMethod(), is("POST"));
+        assertThat(nameValuePairString, is("Company=Amazon&worthInBillion=999.999&age=30"));
+    }
+
+    @Test
+    public void createRequestBuilder_spaceInKeyValue() throws IOException {
+        header.put("Content-Type", "application/x-www-form-urlencoded");
+        String reqBodyAsString = "{\"Name\":\"Larry Pg\",\"Company\":\"Amazon\",\"Title\":\"CEO\"}";
+        RequestBuilder requestBuilder = basicHttpClient.createRequestBuilder("/api/v1/founder", "POST", header, reqBodyAsString);
+        String nameValuePairString = EntityUtils.toString(requestBuilder.getEntity(), "UTF-8");
+        assertThat(nameValuePairString, is("Company=Amazon&Title=CEO&Name=Larry+Pg"));
+    }
+
+    @Test
+    public void createRequestBuilder_frontSlash() throws IOException {
+        String reqBodyAsString = "{\"state/region\":\"singapore north\",\"Company\":\"Amazon\",\"Title\":\"CEO\"}";
+        RequestBuilder requestBuilder = basicHttpClient.createRequestBuilder("/api/v1/founder", "POST", header, reqBodyAsString);
+        String nameValuePairString = EntityUtils.toString(requestBuilder.getEntity(), "UTF-8");
+        assertThat(nameValuePairString, is("Company=Amazon&Title=CEO&state%2Fregion=singapore+north"));
+    }
+
+    @Test
+    public void test_queryParamEncodedChar() throws IOException {
+        Map<String, Object> queryParamsMap = new HashMap<>();
+        queryParamsMap.put("q1", "value1");
+        queryParamsMap.put("q2", "value2");
+        queryParamsMap.put("state/region", "London UK");
+        String effectiveUrl = basicHttpClient.handleUrlAndQueryParams("http://abc.com", queryParamsMap);
+
+        assertThat(effectiveUrl, is("http://abc.com?q1=value1&q2=value2&state%2Fregion=London+UK"));
+    }
+
+    @Test
+    public void createRequestBuilder_jsonValue() throws IOException {
+        header.put("Content-Type", "application/x-www-form-urlencoded");
+        String reqBodyAsString = "{\n" +
+                "  \"Company\": \"Amazon\",\n" +
+                "  \"addresses\": {\n" +
+                "    \"city\": \"NewYork\",\n" +
+                "    \"type\": \"HeadOffice\"\n" +
+                "  }\n" +
+                "}";
+        RequestBuilder requestBuilder = basicHttpClient.createRequestBuilder("/api/v1/founder", "POST", header, reqBodyAsString);
+        String nameValuePairString = EntityUtils.toString(requestBuilder.getEntity(), "UTF-8");
+        assertThat(requestBuilder.getMethod(), is("POST"));
+        //On the server side: address={city=NewYork, type=HeadOffice}
+        assertThat(nameValuePairString, is("Company=Amazon&addresses=%7Bcity%3DNewYork%2C+type%3DHeadOffice%7D"));
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/jsmart/zerocode/core/httpclient/utils/UrlQueryParamsUtilsTest.java b/src/test/java/org/jsmart/zerocode/core/httpclient/utils/UrlQueryParamsUtilsTest.java
index b8273174..094791f7 100644
--- a/src/test/java/org/jsmart/zerocode/core/httpclient/utils/UrlQueryParamsUtilsTest.java
+++ b/src/test/java/org/jsmart/zerocode/core/httpclient/utils/UrlQueryParamsUtilsTest.java
@@ -2,6 +2,7 @@ package org.jsmart.zerocode.core.httpclient.utils;
 
 import org.junit.Test;
 
+import java.io.IOException;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -11,23 +12,40 @@ import static org.hamcrest.core.Is.is;
 public class UrlQueryParamsUtilsTest {
 
     @Test
-    public void testNotNull_queryParams() {
+    public void testNotNull_queryParams() throws IOException {
         Map<String, Object> queryParamsMap = new HashMap<>();
         queryParamsMap.put("q1", "value1");
-        queryParamsMap.put("q2", "value2");
+        queryParamsMap.put("q2", 3);
+        queryParamsMap.put("q3", 1.9);
 
         String qualifiedQueryParams = UrlQueryParamsUtils.createQualifiedQueryParams(queryParamsMap);
 
-        assertThat(qualifiedQueryParams, is("?q1=value1&q2=value2"));
+        assertThat(qualifiedQueryParams, is("q1=value1&q2=3&q3=1.9"));
     }
 
     @Test
-    public void testNull_queryParams() {
+    public void testEmpty_queryParams() throws IOException {
         Map<String, Object> queryParamsMap = new HashMap<>();
         String qualifiedQueryParams = UrlQueryParamsUtils.createQualifiedQueryParams(queryParamsMap);
+        assertThat(qualifiedQueryParams, is(""));
+    }
 
+    @Test
+    public void testNull_queryParams() throws IOException {
+        Map<String, Object> queryParamsMap = null;
+        String qualifiedQueryParams = UrlQueryParamsUtils.createQualifiedQueryParams(queryParamsMap);
         assertThat(qualifiedQueryParams, is(""));
+    }
+
+    @Test
+    public void testQueryParams_frontSlash() throws IOException {
+        Map<String, Object> queryParamsMap = new HashMap<>();
+        queryParamsMap.put("state/region", "singapore north");
+        queryParamsMap.put("q2", "value2");
+
+        String qualifiedQueryParams = UrlQueryParamsUtils.createQualifiedQueryParams(queryParamsMap);
 
+        assertThat(qualifiedQueryParams, is("q2=value2&state%2Fregion=singapore+north"));
     }
 
 }
\ No newline at end of file