Skip to content

Commit

Permalink
non strict json support
Browse files Browse the repository at this point in the history
  • Loading branch information
xnbox committed Sep 23, 2021
1 parent 78d630d commit 954ccc7
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 34 deletions.
1 change: 0 additions & 1 deletion .classpath
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,5 @@
<classpathentry kind="lib" path="lib/jackson-dataformat-yaml-2.12.0-rc2.jar"/>
<classpathentry kind="lib" path="lib/snakeyaml-1.27.jar"/>
<classpathentry kind="lib" path="lib/jackson-syntax-highlight-1.0.6.jar"/>
<classpathentry kind="lib" path="lib/guava-30.1.1-jre.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
Binary file removed lib/guava-30.1.1-jre.jar
Binary file not shown.
31 changes: 20 additions & 11 deletions src/org/deepfake_http/common/servlet/DeepfakeHttpServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,7 @@ of this software and associated documentation files (the "Software"), to deal
import org.mozilla.javascript.ImporterTopLevel;
import org.mozilla.javascript.ScriptableObject;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import com.fasterxml.jackson.databind.ObjectMapper;

import freemarker.template.Configuration;
import freemarker.template.TemplateException;
Expand All @@ -95,6 +93,7 @@ of this software and associated documentation files (the "Software"), to deal
/**
* TODO
* - support block comments in dumps
* --no-cors
*/
public class DeepfakeHttpServlet extends HttpServlet {
/**
Expand Down Expand Up @@ -130,6 +129,7 @@ public class DeepfakeHttpServlet extends HttpServlet {
private boolean noEtag;
private boolean noLog;
private boolean noColor;
private boolean strictJson;

private String collectFile;
private String openApiPath;
Expand Down Expand Up @@ -178,6 +178,7 @@ public void init(ServletConfig servletConfig) throws ServletException {
boolean[] noEtagArr = new boolean[1];
boolean[] noLogArr = new boolean[1];
boolean[] noColorArr = new boolean[1];
boolean[] strictJsonArr = new boolean[1];
String[] collectFileArr = new String[1];
String[] openApiPathArr = new String[1];
String[] openApiTitleArr = new String[1];
Expand All @@ -189,12 +190,12 @@ public void init(ServletConfig servletConfig) throws ServletException {
/* get custom command-line args */
String[] args = (String[]) ctx.lookup("java:comp/env/tommy/args");

ParseCommandLineUtils.parseCommandLineArgs(logger, args, dumps, noWatchArr, noEtagArr, noLogArr, noColorArr, collectFileArr, openApiPathArr, openApiTitleArr, dataFileArr);

ParseCommandLineUtils.parseCommandLineArgs(logger, args, dumps, noWatchArr, noEtagArr, noLogArr, noColorArr, strictJsonArr, collectFileArr, openApiPathArr, openApiTitleArr, dataFileArr);
noWatch = noWatchArr[0];
noEtag = noEtagArr[0];
noLog = noLogArr[0];
noColor = noColorArr[0];
strictJson = strictJsonArr[0];
collectFile = collectFileArr[0];
openApiPath = openApiPathArr[0];
openApiTitle = openApiTitleArr[0];
Expand Down Expand Up @@ -269,6 +270,16 @@ public void destroy() {

@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// TODO --no-cors
/* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin */
response.addHeader("Access-Control-Allow-Origin", "*");

/* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods */
response.addHeader("Access-Control-Allow-Methods", "*");

/* https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers */
response.addHeader("Access-Control-Allow-Headers", "*");

if (request.getMethod().equalsIgnoreCase("PATCH"))
doPatch(request, response);
else
Expand Down Expand Up @@ -513,14 +524,12 @@ else if (INTERNAL_HTTP_HEADER_X_SERVER_RESPONSE_DELAY.toLowerCase(Locale.ENGLISH
break;
} else {
boolean jsonContent = requestHeaderContentType != null && requestHeaderContentType.startsWith("application/json");
// TODO Add CLI option --no-strict
if (jsonContent) {
Map<String, Object> mapTemplate = JacksonUtils.parseJsonYamlToMap(templateBody);
Map<String, Object> mapPovided = JacksonUtils.parseJsonYamlToMap(providedBody.strip());
MapDifference<String, Object> md = Maps.difference(mapTemplate, mapPovided);
if (md.entriesOnlyOnRight().isEmpty())
if (jsonContent && !strictJson) {
ObjectMapper om = new ObjectMapper();
if (om.readTree(templateBody).equals(om.readTree(providedBody.strip()))) {
reqResp = crr;
break;
}
}
if (templateBody.equals(providedBody.strip())) {
reqResp = crr;
Expand Down
3 changes: 2 additions & 1 deletion src/org/deepfake_http/common/utils/JsonUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ of this software and associated documentation files (the "Software"), to deal

public class JsonUtils {
/**
*
* This method is not in use!
*
* @param json
* @param var
* @return
Expand Down
77 changes: 60 additions & 17 deletions src/org/deepfake_http/common/utils/OpenApiUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ of this software and associated documentation files (the "Software"), to deal

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
Expand All @@ -52,7 +53,6 @@ public class OpenApiUtils {
* @throws Exception
*/
public static List<ReqResp> openApiMapToListReqResps(Map<String, Object> openApiMap) throws Throwable {
// TODO: Handle OpenAPI request examples
List<ReqResp> allReqResps = new ArrayList<>();
Map<String, Object> mapPaths = (Map<String, Object>) openApiMap.get("paths");
if (mapPaths == null)
Expand All @@ -67,6 +67,27 @@ public static List<ReqResp> openApiMapToListReqResps(Map<String, Object> openApi
if (mapMethodProps == null || mapMethodProps.isEmpty())
continue;

String requestExample = null;
String requestMime = null;
Map<String, Object> mapRequestBody = (Map<String, Object>) mapMethodProps.get("requestBody");
if (mapRequestBody != null && !mapRequestBody.isEmpty()) {
Map<String, Object> mapRequestBodyContent = (Map<String, Object>) mapRequestBody.get("content");
if (mapRequestBodyContent != null && !mapRequestBodyContent.isEmpty()) {
Map.Entry<String, Object> application_jsonMapEntry = mapRequestBodyContent.entrySet().iterator().next();
requestMime = application_jsonMapEntry.getKey();
Map<String, Object> mapOpenApiRequestBodyContentMime = (Map<String, Object>) application_jsonMapEntry.getValue();
if (mapOpenApiRequestBodyContentMime != null && !mapOpenApiRequestBodyContentMime.isEmpty()) {
Object exampleObj = mapOpenApiRequestBodyContentMime.get("example");
if (exampleObj != null) {
if (exampleObj instanceof String)
requestExample = (String) exampleObj;
else if (exampleObj instanceof Map || exampleObj instanceof List)
requestExample = JacksonUtils.stringifyToJsonYaml(exampleObj, JacksonUtils.FORMAT_JSON, false, false);
}
}
}
}

Map<String, Object> mapStatuses = (Map<String, Object>) mapMethodProps.get("responses");
if (mapStatuses == null || mapStatuses.isEmpty())
continue;
Expand Down Expand Up @@ -133,7 +154,11 @@ public static List<ReqResp> openApiMapToListReqResps(Map<String, Object> openApi
reqResp.request.headers.add(DeepfakeHttpServlet.INTERNAL_HTTP_HEADER_X_OPENAPI_SUMMARY + ": " + openApiMethodSummary);
if (openApiMethodDescription != null && !openApiMethodDescription.isEmpty())
reqResp.request.headers.add(DeepfakeHttpServlet.INTERNAL_HTTP_HEADER_X_OPENAPI_DESCRIPTION + ": " + openApiMethodDescription);
reqResp.request.firstLine = method.toUpperCase(Locale.ENGLISH) + ' ' + path + (queryString.isEmpty() ? "" : '?' + queryString) + ' ' + ParseDumpUtils.HTTP_1_1;
if (requestMime != null)
reqResp.request.headers.add(DeepfakeHttpServlet.HTTP_HEADER_CONTENT_TYPE + ": " + requestMime);
reqResp.request.firstLine = method.toUpperCase(Locale.ENGLISH) + ' ' + path + (queryString.isEmpty() ? "" : '?' + queryString) + ' ' + ParseDumpUtils.HTTP_1_1;
if (requestExample != null)
reqResp.request.body = requestExample;
reqResp.response.firstLine = ParseDumpUtils.HTTP_1_1 + ' ' + statusStr;
reqResp.response.headers.add(DeepfakeHttpServlet.HTTP_HEADER_CONTENT_TYPE + ": " + contentType);
reqResp.response.body = example;
Expand All @@ -150,29 +175,27 @@ public static List<ReqResp> openApiMapToListReqResps(Map<String, Object> openApi
* @throws Exception
*/
public static Map<String, Object> createOpenApiMap(List<ReqResp> allReqResps, String title) throws Throwable {
// TODO: Handle OpenAPI request examples
Map<String, Object> mapPaths = new LinkedHashMap<>(allReqResps.size());
for (ReqResp reqResp : allReqResps) {
FirstLineReq firstLineReq = new FirstLineReq(reqResp.request.firstLine);
FirstLineResp firstLineResp = new FirstLineResp(reqResp.response.firstLine);

String path = HttpPathUtils.extractPathFromUri(firstLineReq.getUri());
String method = firstLineReq.getMethod();
String methodLowerCase = method.toLowerCase(Locale.ENGLISH);
String queryString = HttpPathUtils.extractQueryStringFromUri(firstLineReq.getUri());

Map<String, List<String>> queryParams = new LinkedHashMap<>();
String path = HttpPathUtils.extractPathFromUri(firstLineReq.getUri());
String method = firstLineReq.getMethod();
String methodLowerCase = method.toLowerCase(Locale.ENGLISH);
String queryString = HttpPathUtils.extractQueryStringFromUri(firstLineReq.getUri());
Map<String, List<String>> queryParams = new LinkedHashMap<>();
String requestBody = reqResp.request.body;

String requestContentType = null;
for (String headerLine : reqResp.request.headers) {
Header header = new Header(headerLine);
if (header.name.toLowerCase().equals(DeepfakeHttpServlet.HTTP_HEADER_CONTENT_TYPE.toLowerCase(Locale.ENGLISH))) {
String contentType = header.value;
if (contentType.startsWith("application/x-www-form-urlencoded")) {
String body = reqResp.response.body.toString().strip();
if (!body.isEmpty())
MatchUtils.parseQuery(body, queryParams);
break;
}
if (header.name.toLowerCase(Locale.ENGLISH).equals(DeepfakeHttpServlet.HTTP_HEADER_CONTENT_TYPE.toLowerCase(Locale.ENGLISH))) {
requestContentType = header.value;
if (requestContentType.startsWith("application/x-www-form-urlencoded"))
if (!requestBody.isEmpty())
MatchUtils.parseQuery(requestBody, queryParams);
break;
}
}

Expand Down Expand Up @@ -269,6 +292,26 @@ else if (lowerCaseHeaderName.equals(DeepfakeHttpServlet.INTERNAL_HTTP_HEADER_X_O
if (!openApiMethodTags.isEmpty())
mapMethodProps.put("tags", openApiMethodTags);

Object objBody;
if (requestContentType != null) {
Map<String, Object> mapOpenApiRequestBody = new LinkedHashMap<>();
mapMethodProps.put("requestBody", mapOpenApiRequestBody);

Map<String, Object> mapOpenApiRequestBodyContent = new LinkedHashMap<>();
mapOpenApiRequestBody.put("content", mapOpenApiRequestBodyContent);

Map<String, Object> mapOpenApiRequestBodyContentMime = new LinkedHashMap<>();
if (requestContentType != null)
mapOpenApiRequestBodyContent.put(requestContentType, mapOpenApiRequestBodyContentMime);

if (requestContentType.startsWith("application/json"))
objBody = JacksonUtils.parseJsonYamlToMap(requestBody);
else
objBody = requestBody;
mapOpenApiRequestBodyContentMime.put("example", objBody);
mapOpenApiRequestBodyContentMime.put("schema", new HashMap<>(0));
}

String contentType = null;
for (String headerLine : reqResp.response.headers) {
Header header = new Header(headerLine);
Expand Down
18 changes: 17 additions & 1 deletion src/org/deepfake_http/common/utils/ParseCommandLineUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public class ParseCommandLineUtils {
private static final String ARGS_NO_WATCH = "--no-watch"; // disable watch dump files for changes
private static final String ARGS_NO_ETAG = "--no-etag"; // disable ETag optimization
private static final String ARGS_NO_LOG = "--no-log"; // disable request/response console logging
private static final String ARGS_STRICT_JSON = "--strict-json"; // disable request/response console logging
private static final String ARGS_COLLECT = "--collect"; // collect live request/response dumps to file
private static final String ARGS_OPENAPI_PATH = "--openapi-path"; // serve OpenAPI client at specified context path
private static final String ARGS_OPENAPI_TITLE = "--openapi-title"; // provide custom OpenAPI spec title
Expand Down Expand Up @@ -147,7 +148,19 @@ public static List<ReqResp> getAllReqResp(Logger logger, List<String /* dump fil
* @param openApiTitleArr
* @throws Throwable
*/
public static void parseCommandLineArgs(Logger logger, String[] args, List<String /* dump file */> dumps, boolean[] noWatchArr, boolean[] noEtagArr, boolean[] noLogArr, boolean[] noColorArr, String[] collectFileArr, String[] openApiPathArr, String[] openApiTitleArr, String[] dataFileArr) throws Throwable {
public static void parseCommandLineArgs(Logger logger, //
String[] args, //
List<String /* dump file */> dumps, //
boolean[] noWatchArr, //
boolean[] noEtagArr, //
boolean[] noLogArr, //
boolean[] noColorArr, //
boolean[] strictJsonArr, //
String[] collectFileArr, //
String[] openApiPathArr, //
String[] openApiTitleArr, //
String[] dataFileArr //
) throws Throwable {
for (int i = 0; i < args.length; i++) {
/* skip original Tommy options */

Expand All @@ -174,6 +187,9 @@ else if (args[i].equals(ARGS_NO_ETAG))
noEtagArr[0] = true;
else if (args[i].equals(ARGS_NO_LOG))
noLogArr[0] = true;
else if (args[i].equals(ARGS_STRICT_JSON))
strictJsonArr[0] = true;

else if (args[i].equals(CustomMain.ARGS_NO_COLOR))
noColorArr[0] = true;
else if (args[i].equals(ARGS_COLLECT)) {
Expand Down
10 changes: 7 additions & 3 deletions src/org/tommy/main/CustomMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ else if (args[i].equals(ARGS_FORMAT)) {
sb.append(" --no-watch disable watch files for changes \n");
sb.append(" --no-color disable ANSI color output for --print-* commands \n");
sb.append(" --no-pretty disable prettyprint for --print-* commands \n");
sb.append(" --strict-json enable strict JSON compare \n");
sb.append(" --redirect redirect HTTP to HTTPS \n");
sb.append(" \n");
sb.append(" COMMANDS: \n");
Expand All @@ -136,13 +137,14 @@ else if (args[i].equals(ARGS_FORMAT)) {
boolean[] noEtagArr = new boolean[1];
boolean[] noLogArr = new boolean[1];
boolean[] noColorArr = new boolean[1];
boolean[] strictJsonArr = new boolean[1];
String[] collectFileArr = new String[1];
String[] openApiPathArr = new String[1];
String[] openApiTitleArr = new String[1];
String[] dataFileArr = new String[1];

try {
ParseCommandLineUtils.parseCommandLineArgs(null, args, dumps, noWatchArr, noEtagArr, noLogArr, noColorArr, collectFileArr, openApiPathArr, openApiTitleArr, dataFileArr);
ParseCommandLineUtils.parseCommandLineArgs(null, args, dumps, noWatchArr, noEtagArr, noLogArr, noColorArr, strictJsonArr, collectFileArr, openApiPathArr, openApiTitleArr, dataFileArr);

String json = serializeInfoToJson(dumps, format, !noPretty, !noColor);
System.out.println(json);
Expand All @@ -156,13 +158,14 @@ else if (args[i].equals(ARGS_FORMAT)) {
boolean[] noEtagArr = new boolean[1];
boolean[] noLogArr = new boolean[1];
boolean[] noColorArr = new boolean[1];
boolean[] strictJsonArr = new boolean[1];
String[] collectFileArr = new String[1];
String[] openApiPathArr = new String[1];
String[] openApiTitleArr = new String[1];
String[] dataFileArr = new String[1];

try {
ParseCommandLineUtils.parseCommandLineArgs(null, args, dumps, noWatchArr, noEtagArr, noLogArr, noColorArr, collectFileArr, openApiPathArr, openApiTitleArr, dataFileArr);
ParseCommandLineUtils.parseCommandLineArgs(null, args, dumps, noWatchArr, noEtagArr, noLogArr, noColorArr, strictJsonArr, collectFileArr, openApiPathArr, openApiTitleArr, dataFileArr);
List<ReqResp> allReqResps = ParseCommandLineUtils.getAllReqResp(null, dumps);

String json = serializeRequestsToJson(allReqResps, format, !noPretty, !noColor);
Expand All @@ -177,13 +180,14 @@ else if (args[i].equals(ARGS_FORMAT)) {
boolean[] noEtagArr = new boolean[1];
boolean[] noLogArr = new boolean[1];
boolean[] noColorArr = new boolean[1];
boolean[] strictJsonArr = new boolean[1];
String[] collectFileArr = new String[1];
String[] openApiPathArr = new String[1];
String[] openApiTitleArr = new String[1];
String[] dataFileArr = new String[1];

try {
ParseCommandLineUtils.parseCommandLineArgs(null, args, dumps, noWatchArr, noEtagArr, noLogArr, noColorArr, collectFileArr, openApiPathArr, openApiTitleArr, dataFileArr);
ParseCommandLineUtils.parseCommandLineArgs(null, args, dumps, noWatchArr, noEtagArr, noLogArr, noColorArr, strictJsonArr, collectFileArr, openApiPathArr, openApiTitleArr, dataFileArr);
List<ReqResp> allReqResps = ParseCommandLineUtils.getAllReqResp(null, dumps);

Map<String, Object> openApiMap = OpenApiUtils.createOpenApiMap(allReqResps, openApiTitleArr[0]);
Expand Down

0 comments on commit 954ccc7

Please sign in to comment.