From 3583073347f796e89deaff93b8e6abc10e3d0d9c Mon Sep 17 00:00:00 2001 From: Pasupathi Rajamanickam Date: Sat, 8 Sep 2018 14:11:25 -0500 Subject: [PATCH] Added first commit --- .classpath | 36 ++ .gitignore | 3 + .settings/.gitignore | 2 + README.md | 12 + app.conf | 0 input.json | 56 ++++ pom.xml | 93 +++++ .../utilities/diffbox/DiffResponse.java | 42 +++ .../utilities/diffbox/Executor.java | 163 +++++++++ .../utilities/diffbox/HTTPClientManager.java | 208 ++++++++++++ .../utilities/diffbox/InputRequest.java | 73 ++++ .../utilities/diffbox/JSONDiff.java | 317 ++++++++++++++++++ .../utilities/diffbox/UtilityFunction.java | 23 ++ src/main/resources/final-list.html | 13 + src/main/resources/report-main.html | 12 + src/main/resources/report-template.html | 44 +++ 16 files changed, 1097 insertions(+) create mode 100644 .classpath create mode 100644 .gitignore create mode 100644 .settings/.gitignore create mode 100644 README.md create mode 100644 app.conf create mode 100644 input.json create mode 100644 pom.xml create mode 100644 src/main/java/com/nexttimespace/utilities/diffbox/DiffResponse.java create mode 100644 src/main/java/com/nexttimespace/utilities/diffbox/Executor.java create mode 100644 src/main/java/com/nexttimespace/utilities/diffbox/HTTPClientManager.java create mode 100644 src/main/java/com/nexttimespace/utilities/diffbox/InputRequest.java create mode 100644 src/main/java/com/nexttimespace/utilities/diffbox/JSONDiff.java create mode 100644 src/main/java/com/nexttimespace/utilities/diffbox/UtilityFunction.java create mode 100644 src/main/resources/final-list.html create mode 100644 src/main/resources/report-main.html create mode 100644 src/main/resources/report-template.html diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..fae1a2b --- /dev/null +++ b/.classpath @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f3e959 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target/ +/.project +/Report/ diff --git a/.settings/.gitignore b/.settings/.gitignore new file mode 100644 index 0000000..e2635f0 --- /dev/null +++ b/.settings/.gitignore @@ -0,0 +1,2 @@ +/org.eclipse.jdt.core.prefs +/org.eclipse.m2e.core.prefs diff --git a/README.md b/README.md new file mode 100644 index 0000000..8b97e13 --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# json-diff-report +Command line API call compare JSON and provide great json comparison report + +## What it does? +This utility built on JAVA, it's a command line based tool to execute API calls and do comparison + +## Setup +1. Download latest release from https://github.com/next-time-space/json-diff-report/releases +2. Extract to a directory +3. Edit file input.json with all the API information +4. Execute the utiliy using java -jar json-diff-report.jar +5. Report will be created in the folder called "Report" \ No newline at end of file diff --git a/app.conf b/app.conf new file mode 100644 index 0000000..e69de29 diff --git a/input.json b/input.json new file mode 100644 index 0000000..e18403f --- /dev/null +++ b/input.json @@ -0,0 +1,56 @@ +{ + "inputs": [ + { + "urlSource": "https://api.stackexchange.com/2.2/questions/51227849?order=desc&sort=activity&site=stackoverflow", + "urlCompare": "https://api.stackexchange.com/2.2/questions/28668452?order=desc&sort=activity&site=stackoverflow", + "method": null, + "headersSource": { + "accept": "application/json" + }, + "headersCompare": { + "accept": "application/json" + }, + "requestSource": null, + "requestCompare": null + }, + { + "urlSource": "https://api3.sunrise-sunset.org/json?lat=36.7201600&lng=-4.4203400&date=today", + "urlCompare": "https://api3.sunrise-sunset.org/json?lat=36.7201600&lng=-4.4203400&date=2018-09-10", + "method": null, + "headersSource": { + "accept": "application/json" + }, + "headersCompare": { + "accept": "application/json" + }, + "requestSource": null, + "requestCompare": null + }, + { + "urlSource": "https://api.sunrise-sunset.org/json?lat=36.7201600&lng=-4.4203400&date=today", + "urlCompare": "https://api.sunrise-sunset.org/json?lat=36.7201600&lng=-4.4203400&date=2018-09-10", + "method": null, + "headersSource": { + "accept": "application/json" + }, + "headersCompare": { + "accept": "application/json" + }, + "requestSource": null, + "requestCompare": null + }, + { + "urlSource": "http://api.oceandrivers.com/static/resources.json", + "urlCompare": "http://api.oceandrivers.com/static/resources.json", + "method": null, + "headersSource": { + "accept": "application/json" + }, + "headersCompare": { + "accept": "application/json" + }, + "requestSource": null, + "requestCompare": null + } + ] +} \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..6981e79 --- /dev/null +++ b/pom.xml @@ -0,0 +1,93 @@ + + 4.0.0 + com.nexttimespace.utilities + json-diff-report + 0.0.1-SNAPSHOT + Provides JSON differences in HTML format + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.1.0 + + + jar-with-dependencies + + + + com.nexttimespace.utilities.diffbox.Executor + + + + + + + make-assembly + package + + single + + + + + + + + + 1.8 + 1.8 + true + + + + com.google.guava + guava + 26.0-jre + + + com.google.code.gson + gson + 2.8.5 + + + org.json + json + 20090211 + + + org.apache.commons + commons-lang3 + 3.8 + + + + commons-io + commons-io + 2.5 + + + com.github.spullara.mustache.java + compiler + 0.9.5 + + + org.springframework + spring-web + 5.0.8.RELEASE + + + org.apache.httpcomponents + httpclient + 4.5.6 + + + com.fasterxml.jackson.core + jackson-databind + 2.9.5 + + + \ No newline at end of file diff --git a/src/main/java/com/nexttimespace/utilities/diffbox/DiffResponse.java b/src/main/java/com/nexttimespace/utilities/diffbox/DiffResponse.java new file mode 100644 index 0000000..0750337 --- /dev/null +++ b/src/main/java/com/nexttimespace/utilities/diffbox/DiffResponse.java @@ -0,0 +1,42 @@ +package com.nexttimespace.utilities.diffbox; + +import java.io.Serializable; + +public class DiffResponse implements Serializable{ + private static final long serialVersionUID = 3451985135067844197L; + private String sourceTag; + private String compareWithTag; + private String sourceValue; + private String compareWithValue; + private boolean status; + public String getSourceTag() { + return sourceTag; + } + public void setSourceTag(String sourceTag) { + this.sourceTag = sourceTag; + } + public String getCompareWithTag() { + return compareWithTag; + } + public void setCompareWithTag(String compareWithTag) { + this.compareWithTag = compareWithTag; + } + public String getSourceValue() { + return sourceValue; + } + public void setSourceValue(String sourceValue) { + this.sourceValue = sourceValue; + } + public String getCompareWithValue() { + return compareWithValue; + } + public void setCompareWithValue(String compareWithValue) { + this.compareWithValue = compareWithValue; + } + public boolean isStatus() { + return status; + } + public void setStatus(boolean status) { + this.status = status; + } +} diff --git a/src/main/java/com/nexttimespace/utilities/diffbox/Executor.java b/src/main/java/com/nexttimespace/utilities/diffbox/Executor.java new file mode 100644 index 0000000..cd86cce --- /dev/null +++ b/src/main/java/com/nexttimespace/utilities/diffbox/Executor.java @@ -0,0 +1,163 @@ +package com.nexttimespace.utilities.diffbox; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.json.JSONException; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.github.mustachejava.DefaultMustacheFactory; +import com.github.mustachejava.Mustache; +import com.github.mustachejava.MustacheFactory; + +public class Executor { + + static ObjectMapper mapper = new ObjectMapper(); + static JSONDiff jsonDiff = new JSONDiff(); + static HTTPClientManager httpClient = new HTTPClientManager(); + + public static void main(String... s) throws IOException, KeyManagementException, NoSuchAlgorithmException, KeyStoreException { + httpClient.setup(); + String inputFile = "input.json"; + if (StringUtils.isNotBlank(System.getProperty("input"))) { + inputFile = System.getProperty("input"); + } + + System.out.println("INFO: Started processing"); + InputRequest inputRequest = mapper.readValue(new File(inputFile), InputRequest.class); + String reportFile = ""; + if (!inputRequest.getInputs().isEmpty()) { + reportFile = prepareReport(); + } + int index = 0; + + List> finalList = new ArrayList<>(); + for (InputRequest.Input request : inputRequest.getInputs()) { + Map list = new LinkedHashMap<>(); + index++; + list.put("index", index + ""); + list.put("sourceURL", request.getUrlSource()); + list.put("compareURL", request.getUrlCompare()); + + Map httpRequest = new LinkedHashMap<>(); + httpRequest.put(HTTPClientManager.Const.METHOD, StringUtils.isNotBlank(request.getMethod()) ? request.getMethod() : "GET"); + + httpRequest.put(HTTPClientManager.Const.REQUEST_HEADER, request.getHeadersSource()); + httpRequest.put(HTTPClientManager.Const.URL, request.getUrlSource()); + + if (StringUtils.isNotBlank(request.getRequestSource())) { + httpRequest.put(HTTPClientManager.Const.REQUEST, request.getRequestSource()); + } + Map response = httpClient.getResponse(httpRequest); + + httpRequest = new LinkedHashMap<>(); + httpRequest.put(HTTPClientManager.Const.METHOD, StringUtils.isNotBlank(request.getMethod()) ? request.getMethod() : "GET"); + + httpRequest.put(HTTPClientManager.Const.REQUEST_HEADER, request.getHeadersCompare()); + httpRequest.put(HTTPClientManager.Const.URL, request.getUrlCompare()); + + if (StringUtils.isNotBlank(request.getRequestCompare())) { + httpRequest.put(HTTPClientManager.Const.REQUEST, request.getRequestCompare()); + } + Map responseCompare = httpClient.getResponse(httpRequest); + String status = "Failed"; + try { + status = appendCompareReport(response, responseCompare, reportFile, "" + index, request.getUrlSource(), request.getUrlCompare()); + } catch (Exception e) { + e.printStackTrace(); + System.err.println("ERROR: Error Processing " + request.getUrlSource()); + } + if(status == null) { + list.put("overAllStatus", "Error"); + } else { + list.put("overAllStatus", status); + } + + finalList.add(list); + int percentOfClassPassed = (int) ((double)index * 100 / inputRequest.getInputs().size()); + System.out.println(percentOfClassPassed + "% Completed"); + } + addListReport(finalList, reportFile); + + System.out.println("INFO: Compare process completed. Check report at " + reportFile); + } + + private static void addListReport(List> listReport, String filename) throws IOException { + MustacheFactory mf = new DefaultMustacheFactory(); + Mustache mustache = mf.compile("final-list.html"); + StringWriter writer = new StringWriter(); + Map objectToTemplate = new LinkedHashMap<>(); + objectToTemplate.put("listReport", listReport); + mustache.execute(writer, objectToTemplate).flush(); + if(writer != null) { + String listReportHTML = writer.toString(); + if (listReportHTML !=null && !listReportHTML.isEmpty()) { + try (FileWriter fw = new FileWriter(filename, true); BufferedWriter bw = new BufferedWriter(fw); PrintWriter out = new PrintWriter(bw)) { + out.println(listReportHTML); + } catch (Exception e) { + System.err.println(e.getMessage()); + } + } + } + + } + + private static String appendCompareReport(Map responseSource, Map responseCompare, String filename, String index, String sourceURL, String compareURL) throws JSONException, IOException { + String sourceResponse = (String) responseSource.get(HTTPClientManager.Const.RESPONSE); + String compareResponse = (String) responseCompare.get(HTTPClientManager.Const.RESPONSE); + Map compareReport = null; + String returnStatus = null; + + if (sourceResponse.startsWith("{") && compareResponse.startsWith("{")) { + compareReport = jsonDiff.createCompareHTML(sourceResponse, compareResponse, index, sourceURL, compareURL); + } else { + System.err.println("WARN: Received Non JSON for " + sourceURL); + } + if (compareReport !=null && !compareReport.isEmpty()) { + try (FileWriter fw = new FileWriter(filename, true); BufferedWriter bw = new BufferedWriter(fw); PrintWriter out = new PrintWriter(bw)) { + out.println(compareReport.get("compareReport")); + } catch (Exception e) { + throw e; + } + returnStatus = compareReport.get("status"); + } + return returnStatus; + } + + private static String getReportFileName() { + return "Report/CompareReport_" + new SimpleDateFormat("yyyyMMddHHmmss'.html'").format(new Date()); + } + + private static String prepareReport() throws IOException { + File reportFolder = new File("Report"); + if(!reportFolder.exists()) { + reportFolder.mkdirs(); + } + String reportFileName = getReportFileName(); + Resource resource = new ClassPathResource("/report-template.html"); + OutputStream outputStream = new FileOutputStream(reportFileName); + IOUtils.copy(resource.getInputStream(), outputStream); + outputStream.close(); + return reportFileName; + } + +} diff --git a/src/main/java/com/nexttimespace/utilities/diffbox/HTTPClientManager.java b/src/main/java/com/nexttimespace/utilities/diffbox/HTTPClientManager.java new file mode 100644 index 0000000..c25ce53 --- /dev/null +++ b/src/main/java/com/nexttimespace/utilities/diffbox/HTTPClientManager.java @@ -0,0 +1,208 @@ +package com.nexttimespace.utilities.diffbox; + +import java.nio.charset.Charset; +import java.security.KeyManagementException; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import javax.annotation.PostConstruct; +import javax.net.ssl.SSLContext; +import java.security.cert.X509Certificate; + +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.ssl.TrustStrategy; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +public class HTTPClientManager { + + RestTemplate restTemplate = null; + Map commonHeaders = new LinkedHashMap<>(); + + @PostConstruct + public void setup() throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException{ + + HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = null; + clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory( + HttpClientBuilder.create().build()); + if(Boolean.valueOf(UtilityFunction.appProperties.getProperty("disablessl"))) { + try { + TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true; + + SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom() + .loadTrustMaterial(null, acceptingTrustStrategy) + .build(); + + SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext); + + CloseableHttpClient httpClient = HttpClients.custom() + .setSSLSocketFactory(csf) + .build(); + clientHttpRequestFactory.setHttpClient(httpClient); + System.out.println("INFO: SSL Validations disabled"); + } catch(Exception e) { + System.out.println("ERROR: Exception disabling SSL validation"); + } + + } + clientHttpRequestFactory.setReadTimeout(5 * 1000); + clientHttpRequestFactory.setConnectTimeout(5 * 1000); + + restTemplate = new RestTemplate(clientHttpRequestFactory); + registerCommonHeaders(); + } + + + private void registerCommonHeaders() { + try { + String headers = UtilityFunction.appProperties.getProperty("common.headers"); + + String[] headersArray = headers.split(";", -1); + + for(String heads : headersArray) { + String[] head = heads.split(":", -1); + if(head.length > 1) { + commonHeaders.put(head[0].trim(), head[0].trim()); + } + } + System.out.println("INFO: Common header Enabled for" + commonHeaders); + } catch(Exception e) { + System.out.println("WARN: Error parsing common headers, ignoring all common headers"); + } + + } + public static class Const { + public static final String REQUEST = "request"; + public static final String URL = "url"; + public static final String CHAR_SET = "charSet"; + public static final String TARGET_CHAR_SET = "targetcharSet"; + public static final String RESPONSE = "response"; + public static final String TIME_ELASPED = "timeElasped"; + public static final String METHOD = "method"; + public static final String REQUEST_HEADER = "reqHeader"; + public static final String RESPONSE_HEADER = "resHeader"; + public static final String RESPONSE_STATUS = "resStatus"; + public static final String RESPONSE_LENGTH = "resLength"; + public static final String ERROR_RESPONSE = "errorResponse"; + } + + + + public Map getResponse(Map properties) { + HttpHeaders headers = new HttpHeaders(); + HttpEntity requestEntity = null; + Map responseReturn = new LinkedHashMap<>(); + HttpMethod method = null; + if (properties.get(Const.REQUEST_HEADER) != null && properties.get(Const.REQUEST_HEADER) instanceof String) { + String header = properties.get(Const.REQUEST_HEADER).toString().trim(); + if (!header.isEmpty()) { + String[] splitHeaderNewLine = header.split("\\r?\\n"); + if (splitHeaderNewLine.length > 0) { + for (String headerIterator : splitHeaderNewLine) { + if (headerIterator.contains(":")) { + String[] splitHedaer = headerIterator.split(":", 2); + if(splitHedaer[0].equalsIgnoreCase("accept")){ + if(splitHedaer.length > 1){ + headers.setAccept(MediaType.parseMediaTypes(splitHedaer[1])); + } + }else{ + if (splitHedaer.length >= 2) { + headers.add(splitHedaer[0], splitHedaer[1]); + } else { + headers.add(splitHedaer[0], ""); + } + } + } + } + } + } + } + + if (properties.get(Const.REQUEST_HEADER) != null && properties.get(Const.REQUEST_HEADER) instanceof Map) { + Map headersRequest = (Map) properties.get(Const.REQUEST_HEADER); + + for(Map.Entry headerEntry : headersRequest.entrySet()) { + if(headerEntry.getKey().equalsIgnoreCase("accept")){ + headers.setAccept(MediaType.parseMediaTypes(headerEntry.getValue())); + }else{ + headers.add(headerEntry.getKey(), headerEntry.getValue()); + } + } + } + + if(!commonHeaders.isEmpty()) { + for(Entry entry: commonHeaders.entrySet()) { + headers.add(entry.getKey(), entry.getValue()); + } + } + + if (properties.get(Const.METHOD).toString().equals("GET")) { + method = HttpMethod.GET; + requestEntity = new HttpEntity("", headers); + } else if (properties.get(Const.METHOD).toString().equals("POST")) { + method = HttpMethod.POST; + requestEntity = new HttpEntity(properties.get(Const.REQUEST).toString(), headers); + }else if (properties.get(Const.METHOD).toString().equals("PUT")) { + method = HttpMethod.PUT; + requestEntity = new HttpEntity(properties.get(Const.REQUEST).toString(), headers); + }else if (properties.get(Const.METHOD).toString().equals("DELETE")) { + method = HttpMethod.DELETE; + requestEntity = new HttpEntity(properties.get(Const.REQUEST).toString(), headers); + } + //StopWatch stopwatch = StopWatch.createStarted(); + ResponseEntity response = null; + try { + response = restTemplate.exchange(properties.get(Const.URL).toString(), method, requestEntity, String.class); + //stopwatch.stop(); + String body = response.getBody(); + if(properties.get(Const.CHAR_SET) != null) { + try { + String targetCharSet = properties.get(Const.TARGET_CHAR_SET) != null ? properties.get(Const.TARGET_CHAR_SET).toString() : "UTF-8"; + body = new String(body.getBytes(Charset.forName(properties.get(Const.CHAR_SET).toString())), targetCharSet); + body = body.replaceAll("[^\\x00-\\x7F]", ""); + }catch(Exception e) { + System.out.println("Error using charSet" + e); + } + } + //responseReturn.put(Const.TIME_ELASPED, stopwatch.getTime(TimeUnit.MILLISECONDS)); + responseReturn.put(Const.RESPONSE_STATUS, response.getStatusCode()); + + responseReturn.put(Const.RESPONSE_LENGTH, body!=null?body.getBytes().length:0); + responseReturn.put(Const.RESPONSE, body!=null?body:""); + responseReturn.put(Const.RESPONSE_HEADER, response.getHeaders()); + responseReturn.put(Const.RESPONSE_STATUS, response.getStatusCode()); + } catch (org.springframework.web.client.HttpClientErrorException |org.springframework.web.client.HttpServerErrorException exception) { + String body = exception.getResponseBodyAsString(); + responseReturn.put(Const.RESPONSE_LENGTH, body.getBytes().length); + responseReturn.put(Const.RESPONSE, body); + responseReturn.put(Const.RESPONSE_HEADER, exception.getResponseHeaders()); + responseReturn.put(Const.RESPONSE_STATUS, exception.getStatusCode()); + responseReturn.put(Const.ERROR_RESPONSE, "true"); + }catch(org.springframework.web.client.ResourceAccessException exception){ + responseReturn.put(Const.RESPONSE_LENGTH, "0"); + responseReturn.put(Const.RESPONSE, exception.getMessage()); + responseReturn.put(Const.RESPONSE_HEADER,"NA" ); + responseReturn.put(Const.RESPONSE_STATUS, "NA"); + responseReturn.put(Const.ERROR_RESPONSE, "true"); + }catch(Exception exception){ + responseReturn.put(Const.RESPONSE_LENGTH, "0"); + responseReturn.put(Const.RESPONSE, exception.getMessage()); + responseReturn.put(Const.RESPONSE_HEADER,"NA" ); + responseReturn.put(Const.RESPONSE_STATUS, "NA"); + responseReturn.put(Const.ERROR_RESPONSE, "true"); + } + //responseReturn.put(Const.TIME_ELASPED, stopwatch.getTime(TimeUnit.MILLISECONDS)); + return responseReturn; + } + +} diff --git a/src/main/java/com/nexttimespace/utilities/diffbox/InputRequest.java b/src/main/java/com/nexttimespace/utilities/diffbox/InputRequest.java new file mode 100644 index 0000000..7ad9255 --- /dev/null +++ b/src/main/java/com/nexttimespace/utilities/diffbox/InputRequest.java @@ -0,0 +1,73 @@ +package com.nexttimespace.utilities.diffbox; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class InputRequest { + + private List inputs = new ArrayList<>(); + + public List getInputs() { + return inputs; + } + + public void setInputs(List inputs) { + this.inputs = inputs; + } + + + public static class Input { + private String urlSource; + private String urlCompare; + private String method; + private Map headersSource; + private Map headersCompare; + private String requestSource; + private String requestCompare; + public String getUrlSource() { + return urlSource; + } + public void setUrlSource(String urlSource) { + this.urlSource = urlSource; + } + public String getUrlCompare() { + return urlCompare; + } + public void setUrlCompare(String urlCompare) { + this.urlCompare = urlCompare; + } + public String getMethod() { + return method; + } + public void setMethod(String method) { + this.method = method; + } + public Map getHeadersSource() { + return headersSource; + } + public void setHeadersSource(Map headersSource) { + this.headersSource = headersSource; + } + public Map getHeadersCompare() { + return headersCompare; + } + public void setHeadersCompare(Map headersCompare) { + this.headersCompare = headersCompare; + } + public String getRequestSource() { + return requestSource; + } + public void setRequestSource(String requestSource) { + this.requestSource = requestSource; + } + public String getRequestCompare() { + return requestCompare; + } + public void setRequestCompare(String requestCompare) { + this.requestCompare = requestCompare; + } + + } + +} diff --git a/src/main/java/com/nexttimespace/utilities/diffbox/JSONDiff.java b/src/main/java/com/nexttimespace/utilities/diffbox/JSONDiff.java new file mode 100644 index 0000000..acd4b29 --- /dev/null +++ b/src/main/java/com/nexttimespace/utilities/diffbox/JSONDiff.java @@ -0,0 +1,317 @@ +package com.nexttimespace.utilities.diffbox; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import com.github.mustachejava.DefaultMustacheFactory; +import com.github.mustachejava.Mustache; +import com.github.mustachejava.MustacheFactory; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +public class JSONDiff { + + public Map createCompareHTML(String sourceJSON, String compareJSON, String index, String sourceURL, String compareURL) throws JSONException, IOException { + Map compareHTML = new LinkedHashMap<>(); + Map pathList = getMeAll(sourceJSON); + Map pathList1 = getMeAll(compareJSON); + Map reportDiff = prettyPrintDiff(pathList, pathList1); + List listReport = (List) reportDiff.get("diff"); + String status = (boolean) reportDiff.get("status") ? "Passed" : "Failed"; + Map finalReport = new LinkedHashMap<>(); + finalReport.put("diffResponse", listReport); + finalReport.put("index", index); + finalReport.put("sourceURL", sourceURL); + finalReport.put("compareURL", compareURL); + finalReport.put("overAllStatus", status); + + MustacheFactory mf = new DefaultMustacheFactory(); + Mustache mustache = mf.compile("report-main.html"); + StringWriter writer = new StringWriter(); + mustache.execute(writer, finalReport).flush(); + compareHTML.put("compareReport", writer.toString()); + compareHTML.put("status", status); + return compareHTML; + } + + public Map prettyPrintDiff(Map sourceList, Map compareWithList) { + Map finalReport = new LinkedHashMap<>(); + boolean overall = true; + + List differences = new ArrayList<>(); + + for (Map.Entry entry : sourceList.entrySet()) { + DiffResponse diffResponse = new DiffResponse(); + diffResponse.setSourceTag(entry.getKey()); + diffResponse.setSourceValue(entry.getValue() + ""); + + if (compareWithList.get(entry.getKey()) != null) { + diffResponse.setCompareWithTag(entry.getKey()); + diffResponse.setCompareWithValue(compareWithList.get(entry.getKey()) + ""); + if (entry.getValue() != null && compareWithList.get(entry.getKey()) != null) { + boolean compareStatus = entry.getValue().equals(compareWithList.get(entry.getKey())); + diffResponse.setStatus(compareStatus); + if (!compareStatus) { + overall = false; + } + } else { + overall = false; + diffResponse.setStatus(false); + } + } else { + overall = false; + diffResponse.setStatus(false); + } + differences.add(diffResponse); + } + + for (Map.Entry entry : compareWithList.entrySet()) { + if (!sourceList.containsKey(entry.getKey())) { + overall = false; + DiffResponse diffResponse = new DiffResponse(); + diffResponse.setSourceTag("NULL"); + diffResponse.setSourceValue("NULL"); + diffResponse.setCompareWithTag(entry.getKey()); + diffResponse.setCompareWithValue(compareWithList.get(entry.getKey()) + ""); + differences.add(diffResponse); + } + } + + finalReport.put("diff", differences); + finalReport.put("status", overall); + + return finalReport; + } + + private static String getMeAll(Map pathList) throws JSONException { + JSONObject json = new JSONObject(); + for (Map.Entry entry : pathList.entrySet()) { + String dolorStripped = entry.getKey().replace("$.", ""); + if (dolorStripped.contains(".")) { + String[] dotSeparated = dolorStripped.split("\\."); + int depth = 0; + Object jsonParent = json; + for (int indexMain = 0; indexMain < dotSeparated.length - 1; indexMain++) { + String pathFixed = dotSeparated[indexMain].replaceAll("\\[.*\\]", ""); + String arrayIndex = StringUtils.substringBetween(dotSeparated[indexMain], "[", "]"); + if ((jsonParent instanceof JSONObject && ((JSONObject) jsonParent).has(pathFixed))) { + if (((JSONObject) jsonParent).get(pathFixed) instanceof JSONObject) { + jsonParent = (JSONObject) ((JSONObject) jsonParent).get(pathFixed); + depth++; + } else if (json.get(pathFixed) instanceof JSONArray) { + int arrayIndexNumber = 0; + arrayIndexNumber = Integer.parseInt(arrayIndex); + JSONArray parrentArray = ((JSONArray) json.get(pathFixed)); + if (arrayIndexNumber == -1) { + arrayIndexNumber = -1; + } + if (parrentArray.length() > arrayIndexNumber) { + jsonParent = ((JSONArray) json.get(pathFixed)).get(arrayIndexNumber); + } else { + jsonParent = ((JSONArray) json.get(pathFixed)); + } + depth++; + + } + + } else if ((jsonParent instanceof JSONArray)) { + JSONArray array = (JSONArray) jsonParent; + for (int index = 0; index < array.length(); index++) { + Object child = array.get(index); + if (child instanceof JSONObject && ((JSONObject) child).has(pathFixed)) { + jsonParent = ((JSONObject) child).get(pathFixed); + depth++; + break; + } else if (child instanceof JSONArray) { + JSONArray array1 = (JSONArray) child; + jsonParent = (JSONArray) json.get(pathFixed); + depth++; + break; + } + + } + } else { + break; + } + } + + if (!dotSeparated[dotSeparated.length - 1].contains("[")) { + JSONObject previous = new JSONObject(); + previous.put(dotSeparated[dotSeparated.length - 1], entry.getValue()); + if (depth == 0) { + for (int splitIndex = dotSeparated.length - 2; splitIndex >= 0; splitIndex--) { + if (dotSeparated[splitIndex].contains("[")) { + JSONArray newone = new JSONArray(); + newone.put(previous); + JSONObject newone1 = new JSONObject(); + newone1.put(dotSeparated[splitIndex].replaceAll("\\[.*\\]", ""), newone); + previous = newone1; + } else { + JSONObject newone = new JSONObject(); + newone.put(dotSeparated[splitIndex], previous); + previous = newone; + } + } + } else { + for (int splitIndex = dotSeparated.length - 2; splitIndex >= depth; splitIndex--) { + JSONObject newone = new JSONObject(); + newone.put(dotSeparated[splitIndex], previous); + previous = newone; + } + } + + if (jsonParent instanceof JSONObject && ((JSONObject) jsonParent).keys().hasNext()) { + ((JSONObject) jsonParent).put(previous.keys().next().toString(), previous.get(previous.keys().next().toString())); + } else if (jsonParent instanceof JSONArray) { + // ((JSONArray) + // jsonParent).get(0).put(previous.keys().next().toString(), + // previous.get(previous.keys().next().toString())); + ((JSONArray) jsonParent).put(previous); + } else { + json = previous; + } + } else { + JSONArray jsonDot = new JSONArray(); + ((JSONArray) jsonDot).put(entry.getValue()); + Object previous = new JSONArray(); + String endKey = dotSeparated[dotSeparated.length - 1].replaceAll("\\[.*\\]", ""); + if (depth == 0) { + for (int splitIndex = dotSeparated.length - 2; splitIndex >= 0; splitIndex--) { + if (dotSeparated[splitIndex].contains("[")) { + JSONArray newone = new JSONArray(); + newone.put(previous); + JSONObject newone1 = new JSONObject(); + newone1.put(dotSeparated[splitIndex].replaceAll("\\[.*\\]", ""), newone); + previous = newone1; + } else { + JSONObject newone = new JSONObject(); + newone.put(dotSeparated[splitIndex], previous); + previous = newone; + } + } + } else { + for (int splitIndex = dotSeparated.length - 2; splitIndex >= depth; splitIndex--) { + JSONObject newone = new JSONObject(); + newone.put(dotSeparated[splitIndex], previous); + previous = newone; + } + } + + if (jsonParent instanceof JSONObject && ((JSONObject) jsonParent).keys().hasNext()) { + if (((JSONObject) jsonParent).has(endKey)) { + ((JSONArray) ((JSONObject) jsonParent).get(endKey)).put(entry.getValue()); + } else { + ((JSONObject) jsonParent).put(endKey, jsonDot); + } + // ((JSONObject) jsonParent).put(previous.keys().next().toString(), + // previous.get(previous.keys().next().toString())); + } else if (jsonParent instanceof JSONArray) { + // ((JSONArray) + // jsonParent).get(0).put(previous.keys().next().toString(), + // previous.get(previous.keys().next().toString())); + ((JSONArray) jsonParent).put(previous); + } else { + json = (JSONObject) previous; + } + + } + // json = previous; + + /* + * for(int splitIndex=dotSeparated.length -1; splitIndex > 0; splitIndex--){ + * JSONObject jsonDot1 = new JSONObject(); jsonDot1 } + */ + } else { + if (entry.getKey().contains("[")) { + String key = entry.getKey().replace("$.", "").replaceAll("\\[.*\\]", ""); + if (json.has(key)) { + ((JSONArray) json.get(key)).put(entry.getValue()); + } else { + + JSONArray jsonDot = new JSONArray(); + ((JSONArray) jsonDot).put(entry.getValue()); + json.put(entry.getKey().replace("$.", "").replaceAll("\\[.*\\]", ""), jsonDot); + } + } else { + json.put(entry.getKey().replace("$.", ""), entry.getValue()); + } + } + + } + return prettyFormatJSON(json.toString()); + } + + public static String prettyFormatJSON(String input) { + try { + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + JsonParser jp = new JsonParser(); + JsonElement je = jp.parse(input); + return gson.toJson(je); + } catch (Exception e) { + return input; + } + } + + private Map getMeAll(String json) throws JSONException { + Map pathList = new LinkedHashMap<>(); + if (json.startsWith("{")) { + JSONObject object = new JSONObject(json); + + String jsonPath = "$"; + if (json != JSONObject.NULL) { + readObject(object, jsonPath, pathList); + } + } else if (json.startsWith("[")) { + System.out.println("Json starts with [ does not support now."); + } + return pathList; + } + + private void readObject(JSONObject object, String jsonPath, Map pathList) throws JSONException { + + Iterator keysItr = object.keys(); + String parentPath = jsonPath; + while (keysItr.hasNext()) { + String key = keysItr.next(); + Object value = object.get(key); + jsonPath = parentPath + "." + key; + + if (value instanceof JSONArray) { + readArray((JSONArray) value, jsonPath, pathList); + } else if (value instanceof JSONObject) { + readObject((JSONObject) value, jsonPath, pathList); + } else { // is a value + pathList.put(jsonPath, value + ""); + } + } + + } + + private void readArray(JSONArray array, String jsonPath, Map pathList) throws JSONException { + String parentPath = jsonPath; + for (int i = 0; i < array.length(); i++) { + Object value = array.get(i); + jsonPath = parentPath + "[" + i + "]"; + + if (value instanceof JSONArray) { + readArray((JSONArray) value, jsonPath, pathList); + } else if (value instanceof JSONObject) { + readObject((JSONObject) value, jsonPath, pathList); + } else { // is a value + pathList.put(jsonPath, value + ""); + } + } + } +} diff --git a/src/main/java/com/nexttimespace/utilities/diffbox/UtilityFunction.java b/src/main/java/com/nexttimespace/utilities/diffbox/UtilityFunction.java new file mode 100644 index 0000000..ffdca5f --- /dev/null +++ b/src/main/java/com/nexttimespace/utilities/diffbox/UtilityFunction.java @@ -0,0 +1,23 @@ +package com.nexttimespace.utilities.diffbox; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Properties; + +public class UtilityFunction { + + public static Properties appProperties; + + static { + appProperties = new Properties(); + if(new File("app.conf").exists()) { + try { + appProperties.load(new FileInputStream(new File("app.conf"))); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + +} diff --git a/src/main/resources/final-list.html b/src/main/resources/final-list.html new file mode 100644 index 0000000..533674e --- /dev/null +++ b/src/main/resources/final-list.html @@ -0,0 +1,13 @@ + + +
+ + +{{#listReport}} + +{{/listReport}} +
Source URLCompare URLStatusShow Different
{{sourceURL}}{{compareURL}}{{overAllStatus}}Show Different
+
+ + + \ No newline at end of file diff --git a/src/main/resources/report-main.html b/src/main/resources/report-main.html new file mode 100644 index 0000000..126acdb --- /dev/null +++ b/src/main/resources/report-main.html @@ -0,0 +1,12 @@ + diff --git a/src/main/resources/report-template.html b/src/main/resources/report-template.html new file mode 100644 index 0000000..5bb7202 --- /dev/null +++ b/src/main/resources/report-template.html @@ -0,0 +1,44 @@ + + + + + + +