Merge pull request #19 from RohitAwate/state-maintenance-improvements

Improvements to application state maintenance logic
This commit is contained in:
Rohit Awate 2018-07-01 12:23:04 +05:30 committed by GitHub
commit e1e282c08b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 541 additions and 413 deletions

View file

@ -18,17 +18,16 @@ package com.rohitawate.everest.controllers;
import com.rohitawate.everest.controllers.codearea.EverestCodeArea; import com.rohitawate.everest.controllers.codearea.EverestCodeArea;
import com.rohitawate.everest.controllers.codearea.EverestCodeArea.HighlightMode; import com.rohitawate.everest.controllers.codearea.EverestCodeArea.HighlightMode;
import com.rohitawate.everest.controllers.state.DashboardState;
import com.rohitawate.everest.controllers.state.FieldState;
import com.rohitawate.everest.misc.Services; import com.rohitawate.everest.misc.Services;
import com.rohitawate.everest.misc.ThemeManager; import com.rohitawate.everest.misc.ThemeManager;
import com.rohitawate.everest.models.DashboardState;
import com.rohitawate.everest.models.requests.DataDispatchRequest;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.Parent; import javafx.scene.Parent;
import javafx.scene.control.ComboBox; import javafx.scene.control.ComboBox;
import javafx.scene.control.Tab; import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
@ -39,7 +38,6 @@ import javax.ws.rs.core.MediaType;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.Map;
import java.util.ResourceBundle; import java.util.ResourceBundle;
/* /*
@ -48,8 +46,6 @@ import java.util.ResourceBundle;
URL encoded and Form tabs have special FXMLs. URL encoded and Form tabs have special FXMLs.
*/ */
public class BodyTabController implements Initializable { public class BodyTabController implements Initializable {
@FXML
private TabPane bodyTabPane;
@FXML @FXML
ComboBox<String> rawInputTypeBox; ComboBox<String> rawInputTypeBox;
@FXML @FXML
@ -107,121 +103,98 @@ public class BodyTabController implements Initializable {
} }
} }
/**
* Returns a EverestRequest object initialized with the request body.
*/
public DataDispatchRequest getBasicRequest(String requestType) {
DataDispatchRequest request = new DataDispatchRequest(requestType);
// Raw and binary types get saved in Body.
// Form and URL encoded types use tuple objects
if (rawTab.isSelected()) {
String contentType;
switch (rawInputTypeBox.getValue()) {
case "PLAIN TEXT":
contentType = MediaType.TEXT_PLAIN;
break;
case "JSON":
contentType = MediaType.APPLICATION_JSON;
break;
case "XML":
contentType = MediaType.APPLICATION_XML;
break;
case "HTML":
contentType = MediaType.TEXT_HTML;
break;
default:
contentType = MediaType.TEXT_PLAIN;
}
request.setContentType(contentType);
request.setBody(rawInputArea.getText());
} else if (formTab.isSelected()) {
request.setStringTuples(formDataTabController.getStringTuples());
request.setFileTuples(formDataTabController.getFileTuples());
request.setContentType(MediaType.MULTIPART_FORM_DATA);
} else if (binaryTab.isSelected()) {
request.setBody(filePathField.getText());
request.setContentType(MediaType.APPLICATION_OCTET_STREAM);
} else if (urlTab.isSelected()) {
request.setStringTuples(urlTabController.getStringTuples());
request.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
}
return request;
}
@FXML @FXML
private void browseFile() { private void browseFile() {
FileChooser fileChooser = new FileChooser(); FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Choose a binary file to add to request..."); fileChooser.setTitle("Choose a binary file to add to request...");
Window dashboardWindow = filePathField.getScene().getWindow(); Window dashboardWindow = filePathField.getScene().getWindow();
String filePath; String filePath;
try { try {
filePath = fileChooser.showOpenDialog(dashboardWindow).getAbsolutePath(); filePath = fileChooser.showOpenDialog(dashboardWindow).getAbsolutePath();
} catch (NullPointerException NPE) { } catch (NullPointerException NPE) {
filePath = ""; filePath = "";
} }
filePathField.setText(filePath); filePathField.setText(filePath);
} }
public void setState(DashboardState dashboardState) { public DashboardState getState() {
try { DashboardState state = new DashboardState();
switch (dashboardState.getContentType()) {
case MediaType.TEXT_PLAIN:
setRawTab(dashboardState, "PLAIN TEXT");
break;
case MediaType.APPLICATION_JSON:
setRawTab(dashboardState, "JSON");
break;
case MediaType.APPLICATION_XML:
setRawTab(dashboardState, "XML");
break;
case MediaType.TEXT_HTML:
setRawTab(dashboardState, "HTML");
break;
case MediaType.MULTIPART_FORM_DATA:
// For file tuples
for (Map.Entry entry : dashboardState.getFileTuples().entrySet())
formDataTabController.addFileField(entry.getKey().toString(), entry.getValue().toString());
// For string tuples state.rawBodyType = rawInputTypeBox.getValue();
for (Map.Entry entry : dashboardState.getStringTuples().entrySet()) state.rawBody = rawInputArea.getText();
formDataTabController.addStringField(entry.getKey().toString(), entry.getValue().toString()); state.urlStringTuples = urlTabController.getFieldStates();
bodyTabPane.getSelectionModel().select(formTab); state.formStringTuples = formDataTabController.getStringFieldStates();
state.formFileTuples = formDataTabController.getFileFieldStates();
state.binaryFilePath = filePathField.getText();
if (rawTab.isSelected()) {
switch (rawInputTypeBox.getValue()) {
case "JSON":
state.contentType = MediaType.APPLICATION_JSON;
break; break;
case MediaType.APPLICATION_OCTET_STREAM: case "XML":
filePathField.setText(dashboardState.getBody()); state.contentType = MediaType.APPLICATION_XML;
bodyTabPane.getSelectionModel().select(binaryTab);
break; break;
case MediaType.APPLICATION_FORM_URLENCODED: case "HTML":
for (Map.Entry entry : dashboardState.getStringTuples().entrySet()) state.contentType = MediaType.TEXT_HTML;
urlTabController.addField(entry.getKey().toString(), entry.getValue().toString());
bodyTabPane.getSelectionModel().select(urlTab);
break; break;
default:
state.contentType = MediaType.TEXT_PLAIN;
} }
} catch (NullPointerException NPE) { } else if (formTab.isSelected()) {
Services.loggingService.logInfo("Dashboard loaded with blank request body.", LocalDateTime.now()); state.contentType = MediaType.MULTIPART_FORM_DATA;
} else if (urlTab.isSelected()) {
state.contentType = MediaType.APPLICATION_FORM_URLENCODED;
} else {
state.contentType = MediaType.APPLICATION_OCTET_STREAM;
} }
return state;
} }
private void setRawTab(DashboardState dashboardState, String contentType) { public void setState(DashboardState state) {
// Adding URL tab's tuples
if (state.urlStringTuples != null)
for (FieldState fieldState : state.urlStringTuples)
urlTabController.addField(fieldState);
// Adding Form tab's string tuples
if (state.formStringTuples != null)
for (FieldState fieldState : state.formStringTuples)
formDataTabController.addStringField(fieldState);
// Adding Form tab's file tuples
if (state.formFileTuples != null)
for (FieldState fieldState : state.formFileTuples)
formDataTabController.addFileField(fieldState);
setRawTab(state);
filePathField.setText(state.binaryFilePath);
}
private void setRawTab(DashboardState state) {
HighlightMode mode; HighlightMode mode;
switch (contentType) { if (state.rawBodyType != null && state.rawBody != null) {
case MediaType.APPLICATION_JSON: switch (state.rawBodyType) {
mode = HighlightMode.JSON; case "JSON":
break; mode = HighlightMode.JSON;
case MediaType.APPLICATION_XML: break;
mode = HighlightMode.XML; case "XML":
break; mode = HighlightMode.XML;
case MediaType.TEXT_HTML: break;
mode = HighlightMode.HTML; case "HTML":
break; mode = HighlightMode.HTML;
default: break;
mode = HighlightMode.PLAIN; default:
mode = HighlightMode.PLAIN;
}
rawInputArea.setText(state.rawBody, mode);
} else {
rawInputArea.setText("", HighlightMode.PLAIN);
} }
rawInputArea.setText(dashboardState.getBody(), mode);
rawInputTypeBox.getSelectionModel().select(contentType);
bodyTabPane.getSelectionModel().select(rawTab);
} }
} }

View file

@ -21,12 +21,13 @@ import com.jfoenix.controls.JFXProgressBar;
import com.jfoenix.controls.JFXSnackbar; import com.jfoenix.controls.JFXSnackbar;
import com.rohitawate.everest.controllers.codearea.EverestCodeArea; import com.rohitawate.everest.controllers.codearea.EverestCodeArea;
import com.rohitawate.everest.controllers.codearea.EverestCodeArea.HighlightMode; import com.rohitawate.everest.controllers.codearea.EverestCodeArea.HighlightMode;
import com.rohitawate.everest.controllers.state.DashboardState;
import com.rohitawate.everest.controllers.state.FieldState;
import com.rohitawate.everest.exceptions.RedirectException; import com.rohitawate.everest.exceptions.RedirectException;
import com.rohitawate.everest.exceptions.UnreliableResponseException; import com.rohitawate.everest.exceptions.UnreliableResponseException;
import com.rohitawate.everest.misc.EverestUtilities; import com.rohitawate.everest.misc.EverestUtilities;
import com.rohitawate.everest.misc.Services; import com.rohitawate.everest.misc.Services;
import com.rohitawate.everest.misc.ThemeManager; import com.rohitawate.everest.misc.ThemeManager;
import com.rohitawate.everest.models.DashboardState;
import com.rohitawate.everest.models.requests.DELETERequest; import com.rohitawate.everest.models.requests.DELETERequest;
import com.rohitawate.everest.models.requests.DataDispatchRequest; import com.rohitawate.everest.models.requests.DataDispatchRequest;
import com.rohitawate.everest.models.requests.GETRequest; import com.rohitawate.everest.models.requests.GETRequest;
@ -59,7 +60,6 @@ import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map.Entry;
import java.util.ResourceBundle; import java.util.ResourceBundle;
public class DashboardController implements Initializable { public class DashboardController implements Initializable {
@ -114,14 +114,14 @@ public class DashboardController implements Initializable {
headerTabController = headerTabLoader.getController(); headerTabController = headerTabLoader.getController();
headersTab.setContent(headerTabContent); headersTab.setContent(headerTabContent);
// Loading the body tab // Loading the rawBody tab
FXMLLoader bodyTabLoader = new FXMLLoader(getClass().getResource("/fxml/homewindow/BodyTab.fxml")); FXMLLoader bodyTabLoader = new FXMLLoader(getClass().getResource("/fxml/homewindow/BodyTab.fxml"));
Parent bodyTabContent = bodyTabLoader.load(); Parent bodyTabContent = bodyTabLoader.load();
ThemeManager.setTheme(bodyTabContent); ThemeManager.setTheme(bodyTabContent);
bodyTabController = bodyTabLoader.getController(); bodyTabController = bodyTabLoader.getController();
bodyTab.setContent(bodyTabContent); bodyTab.setContent(bodyTabContent);
} catch (IOException e) { } catch (IOException e) {
Services.loggingService.logSevere("Could not load headers/body tabs.", e, LocalDateTime.now()); Services.loggingService.logSevere("Could not load headers/rawBody tabs.", e, LocalDateTime.now());
} }
snackbar = new JFXSnackbar(dashboard); snackbar = new JFXSnackbar(dashboard);
@ -154,7 +154,7 @@ public class DashboardController implements Initializable {
responseArea.selectAll(); responseArea.selectAll();
responseArea.copy(); responseArea.copy();
responseArea.deselect(); responseArea.deselect();
snackbar.show("Response body copied to clipboard.", 5000); snackbar.show("Response rawBody copied to clipboard.", 5000);
}); });
responseTypeBox.getItems().addAll("JSON", "XML", "HTML", "PLAIN TEXT"); responseTypeBox.getItems().addAll("JSON", "XML", "HTML", "PLAIN TEXT");
@ -233,7 +233,7 @@ public class DashboardController implements Initializable {
getRequest = new GETRequest(); getRequest = new GETRequest();
getRequest.setTarget(address); getRequest.setTarget(address);
getRequest.setHeaders(headerTabController.getSelectedHeaders()); getRequest.setHeaders(headerTabController.getHeaders());
requestManager = Services.pool.get(); requestManager = Services.pool.get();
requestManager.setRequest(getRequest); requestManager.setRequest(getRequest);
@ -250,7 +250,7 @@ public class DashboardController implements Initializable {
dataRequest.setRequestType(httpMethodBox.getValue()); dataRequest.setRequestType(httpMethodBox.getValue());
dataRequest.setTarget(address); dataRequest.setTarget(address);
dataRequest.setHeaders(headerTabController.getSelectedHeaders()); dataRequest.setHeaders(headerTabController.getHeaders());
if (bodyTabController.rawTab.isSelected()) { if (bodyTabController.rawTab.isSelected()) {
String contentType; String contentType;
@ -296,7 +296,7 @@ public class DashboardController implements Initializable {
deleteRequest = new DELETERequest(); deleteRequest = new DELETERequest();
deleteRequest.setTarget(address); deleteRequest.setTarget(address);
deleteRequest.setHeaders(headerTabController.getSelectedHeaders()); deleteRequest.setHeaders(headerTabController.getHeaders());
requestManager = Services.pool.delete(); requestManager = Services.pool.delete();
requestManager.setRequest(deleteRequest); requestManager.setRequest(deleteRequest);
@ -354,7 +354,7 @@ public class DashboardController implements Initializable {
if (requestManager.getClass() == DataDispatchRequestManager.class) { if (requestManager.getClass() == DataDispatchRequestManager.class) {
if (throwable.getCause() != null && throwable.getCause().getClass() == IllegalArgumentException.class) { if (throwable.getCause() != null && throwable.getCause().getClass() == IllegalArgumentException.class) {
errorTitle.setText("Did you forget something?"); errorTitle.setText("Did you forget something?");
errorDetails.setText("Please specify at least one body part for your " + httpMethodBox.getValue() + " request."); errorDetails.setText("Please specify at least one rawBody part for your " + httpMethodBox.getValue() + " request.");
} else if (throwable.getClass() == FileNotFoundException.class) { } else if (throwable.getClass() == FileNotFoundException.class) {
errorTitle.setText("File(s) not found:"); errorTitle.setText("File(s) not found:");
errorDetails.setText(throwable.getMessage()); errorDetails.setText(throwable.getMessage());
@ -434,7 +434,7 @@ public class DashboardController implements Initializable {
} }
} else { } else {
responseTypeBox.setValue("PLAIN"); responseTypeBox.setValue("PLAIN");
responseArea.setText("No body found in the response.", HighlightMode.PLAIN); responseArea.setText("No rawBody found in the response.", HighlightMode.PLAIN);
} }
} catch (Exception e) { } catch (Exception e) {
snackbar.show("Response could not be parsed.", 5000); snackbar.show("Response could not be parsed.", 5000);
@ -468,35 +468,41 @@ public class DashboardController implements Initializable {
} }
} }
private HashMap<String, String> getParams() { /**
if (params == null) * @return List of the states of all the non-empty fields in the Params tab.
params = new HashMap<>(); */
public ArrayList<FieldState> getParamFieldStates() {
ArrayList<FieldState> states = new ArrayList<>();
params.clear();
for (StringKeyValueFieldController controller : paramsControllers) for (StringKeyValueFieldController controller : paramsControllers)
if (controller.isChecked()) if (!controller.isKeyFieldEmpty() && !controller.isValueFieldEmpty())
params.put(controller.getHeader().getKey(), controller.getHeader().getValue()); states.add(controller.getState());
return params;
return states;
} }
private void addParamField() { private void addParamField() {
addParamField("", "", null); addParamField("", "", null, false);
} }
private void addParamField(String key, String value) { private void addParamField(FieldState state) {
addParamField(key, value, null); addParamField(state.key, state.value, null, state.checked);
} }
@FXML @FXML
private void addParamField(ActionEvent event) { private void addParamField(ActionEvent event) {
addParamField("", "", event); addParamField("", "", event, false);
} }
// Adds a new URL-parameter field /**
private void addParamField(String key, String value, ActionEvent event) { * Adds a new URL-parameter field
*/
private void addParamField(String key, String value, ActionEvent event, boolean checked) {
/* /*
Re-uses previous field if it is empty, Re-uses previous field if it is empty, else loads a new one.
else loads a new one. A value of null for the 'event' parameter indicates that the method call
came from code and not from the user. This call is made while recovering
the application state.
*/ */
if (paramsControllers.size() > 0 && event == null) { if (paramsControllers.size() > 0 && event == null) {
StringKeyValueFieldController previousController = paramsControllers.get(paramsControllers.size() - 1); StringKeyValueFieldController previousController = paramsControllers.get(paramsControllers.size() - 1);
@ -515,6 +521,7 @@ public class DashboardController implements Initializable {
StringKeyValueFieldController controller = loader.getController(); StringKeyValueFieldController controller = loader.getController();
controller.setKeyField(key); controller.setKeyField(key);
controller.setValueField(value); controller.setValueField(value);
controller.setChecked(checked);
paramsControllers.add(controller); paramsControllers.add(controller);
paramsCountProperty.set(paramsCountProperty.get() + 1); paramsCountProperty.set(paramsCountProperty.get() + 1);
controller.deleteButton.visibleProperty().bind(Bindings.greaterThan(paramsCountProperty, 1)); controller.deleteButton.visibleProperty().bind(Bindings.greaterThan(paramsCountProperty, 1));
@ -530,9 +537,7 @@ public class DashboardController implements Initializable {
} }
/** /**
* Returns the current state of the Dashboard * @return Current state of the Dashboard.
*
* @return DashboardState - Current state of the Dashboard
*/ */
public DashboardState getState() { public DashboardState getState() {
DashboardState dashboardState; DashboardState dashboardState;
@ -540,22 +545,17 @@ public class DashboardController implements Initializable {
case "POST": case "POST":
case "PUT": case "PUT":
case "PATCH": case "PATCH":
dashboardState = new DashboardState(bodyTabController.getBasicRequest(httpMethodBox.getValue())); dashboardState = bodyTabController.getState();
dashboardState.setHeaders(headerTabController.getSelectedHeaders());
break; break;
default: default:
// For GET, DELETE requests // For GET, DELETE requests
dashboardState = new DashboardState(); dashboardState = new DashboardState();
} }
try { dashboardState.target = addressField.getText();
dashboardState.setTarget(addressField.getText()); dashboardState.httpMethod = httpMethodBox.getValue();
} catch (MalformedURLException e) { dashboardState.headers = headerTabController.getFieldStates();
Services.loggingService.logInfo("Dashboard state was saved with an invalid URL.", LocalDateTime.now()); dashboardState.params = getParamFieldStates();
}
dashboardState.setHttpMethod(httpMethodBox.getValue());
dashboardState.setHeaders(headerTabController.getSelectedHeaders());
dashboardState.setParams(getParams());
return dashboardState; return dashboardState;
} }
@ -563,23 +563,34 @@ public class DashboardController implements Initializable {
/** /**
* Sets the Dashboard to the given application state. * Sets the Dashboard to the given application state.
* *
* @param dashboardState - State of the dashboard * @param state - State of the dashboard
*/ */
public void setState(DashboardState dashboardState) { public void setState(DashboardState state) {
if (dashboardState.getTarget() != null) boolean validMethod = false;
addressField.setText(dashboardState.getTarget().toString()); for (String method : httpMethods) {
if (state.httpMethod.equals(method))
validMethod = true;
}
httpMethodBox.getSelectionModel().select(dashboardState.getHttpMethod()); if (!validMethod) {
Services.loggingService.logInfo("Application state file was tampered with. State could not be recovered.", LocalDateTime.now());
return;
}
if (dashboardState.getHeaders() != null) httpMethodBox.setValue(state.httpMethod);
for (Entry entry : dashboardState.getHeaders().entrySet())
headerTabController.addHeader(entry.getKey().toString(), entry.getValue().toString());
if (dashboardState.getParams() != null) if (state.target != null)
for (Entry entry : dashboardState.getParams().entrySet()) addressField.setText(state.target);
addParamField(entry.getKey().toString(), entry.getValue().toString());
if (state.headers != null)
for (FieldState fieldState : state.headers)
headerTabController.addHeader(fieldState);
if (state.params != null)
for (FieldState fieldState : state.params)
addParamField(fieldState);
if (!(httpMethodBox.getValue().equals("GET") || httpMethodBox.getValue().equals("DELETE"))) if (!(httpMethodBox.getValue().equals("GET") || httpMethodBox.getValue().equals("DELETE")))
bodyTabController.setState(dashboardState); bodyTabController.setState(state);
} }
} }

View file

@ -18,6 +18,7 @@ package com.rohitawate.everest.controllers;
import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXCheckBox; import com.jfoenix.controls.JFXCheckBox;
import com.rohitawate.everest.controllers.state.FieldState;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
@ -108,4 +109,12 @@ public class FileKeyValueFieldController implements Initializable {
public boolean isFileValueFieldEmpty() { public boolean isFileValueFieldEmpty() {
return fileValueField.getText().isEmpty(); return fileValueField.getText().isEmpty();
} }
public FieldState getState() {
return new FieldState(fileKeyField.getText(), fileValueField.getText(), checkBox.isSelected());
}
public void setChecked(boolean checked) {
checkBox.setSelected(checked);
}
} }

View file

@ -16,6 +16,7 @@
package com.rohitawate.everest.controllers; package com.rohitawate.everest.controllers;
import com.rohitawate.everest.controllers.state.FieldState;
import com.rohitawate.everest.misc.Services; import com.rohitawate.everest.misc.Services;
import com.rohitawate.everest.misc.ThemeManager; import com.rohitawate.everest.misc.ThemeManager;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
@ -59,22 +60,26 @@ public class FormDataTabController implements Initializable {
addStringField(); addStringField();
} }
public void addFileField(FieldState state) {
addFileField(state.key, state.value, null, state.checked);
}
@FXML @FXML
private void addFileField(ActionEvent event) { private void addFileField(ActionEvent event) {
addFileField("", "", event); addFileField("", "", event, false);
} }
private void addFileField() { private void addFileField() {
addFileField("", "", null); addFileField("", "", null, false);
} }
public void addFileField(String key, String value) { private void addFileField(String key, String value, ActionEvent event, boolean checked) {
addFileField(key, value, null); /*
} Re-uses previous field if it is empty, else loads a new one.
A value of null for the 'event' parameter indicates that the method call
private void addFileField(String key, String value, ActionEvent event) { came from code and not from the user. This call is made while recovering
//Re-uses previous field if it is empty else loads a new one. the application state.
*/
if (fileControllers.size() > 0 && event == null) { if (fileControllers.size() > 0 && event == null) {
FileKeyValueFieldController previousController = fileControllers.get(fileControllers.size() - 1); FileKeyValueFieldController previousController = fileControllers.get(fileControllers.size() - 1);
@ -93,6 +98,7 @@ public class FormDataTabController implements Initializable {
FileKeyValueFieldController controller = loader.getController(); FileKeyValueFieldController controller = loader.getController();
controller.setFileKeyField(key); controller.setFileKeyField(key);
controller.setFileValueField(value); controller.setFileValueField(value);
controller.setChecked(checked);
fileControllers.add(controller); fileControllers.add(controller);
fileControllersCount.set(fileControllersCount.get() + 1); fileControllersCount.set(fileControllersCount.get() + 1);
controller.deleteButton.visibleProperty().bind(Bindings.greaterThan(fileControllersCount, 1)); controller.deleteButton.visibleProperty().bind(Bindings.greaterThan(fileControllersCount, 1));
@ -103,27 +109,29 @@ public class FormDataTabController implements Initializable {
}); });
fieldsBox.getChildren().add(fileField); fieldsBox.getChildren().add(fileField);
} catch (IOException e) { } catch (IOException e) {
Services.loggingService.logSevere("Could not load file field.", e, LocalDateTime.now()); Services.loggingService.logSevere("Could not add file field.", e, LocalDateTime.now());
} }
} }
public void addStringField(FieldState state) {
addStringField(state.key, state.value, null, state.checked);
}
@FXML @FXML
private void addStringField(ActionEvent event) { private void addStringField(ActionEvent event) {
addStringField("", "", event); addStringField("", "", event, false);
} }
private void addStringField() { private void addStringField() {
addStringField("", "", null); addStringField("", "", null, false);
} }
public void addStringField(String key, String value) { private void addStringField(String key, String value, ActionEvent event, boolean checked) {
addStringField(key, value, null);
}
private void addStringField(String key, String value, ActionEvent event) {
/* /*
Re-uses previous field if it is empty, Re-uses previous field if it is empty, else loads a new one.
else loads a new one. A value of null for the 'event' parameter indicates that the method call
came from code and not from the user. This call is made while recovering
the application state.
*/ */
if (stringControllers.size() > 0 && event == null) { if (stringControllers.size() > 0 && event == null) {
StringKeyValueFieldController previousController = stringControllers.get(stringControllers.size() - 1); StringKeyValueFieldController previousController = stringControllers.get(stringControllers.size() - 1);
@ -142,6 +150,7 @@ public class FormDataTabController implements Initializable {
StringKeyValueFieldController controller = loader.getController(); StringKeyValueFieldController controller = loader.getController();
controller.setKeyField(key); controller.setKeyField(key);
controller.setValueField(value); controller.setValueField(value);
controller.setChecked(checked);
stringControllers.add(controller); stringControllers.add(controller);
stringControllersCount.set(stringControllersCount.get() + 1); stringControllersCount.set(stringControllersCount.get() + 1);
controller.deleteButton.visibleProperty().bind(Bindings.greaterThan(stringControllersCount, 1)); controller.deleteButton.visibleProperty().bind(Bindings.greaterThan(stringControllersCount, 1));
@ -152,10 +161,13 @@ public class FormDataTabController implements Initializable {
}); });
fieldsBox.getChildren().add(stringField); fieldsBox.getChildren().add(stringField);
} catch (IOException e) { } catch (IOException e) {
Services.loggingService.logSevere("Could not load string field.", e, LocalDateTime.now()); Services.loggingService.logSevere("Could not add string field.", e, LocalDateTime.now());
} }
} }
/**
* @return Map of selected string tuples from multipart-form tab
*/
public HashMap<String, String> getStringTuples() { public HashMap<String, String> getStringTuples() {
if (stringMap == null) if (stringMap == null)
stringMap = new HashMap<>(); stringMap = new HashMap<>();
@ -165,9 +177,13 @@ public class FormDataTabController implements Initializable {
if (controller.isChecked()) if (controller.isChecked())
stringMap.put(controller.getHeader().getKey(), controller.getHeader().getValue()); stringMap.put(controller.getHeader().getKey(), controller.getHeader().getValue());
} }
return stringMap; return stringMap;
} }
/**
* @return Map of selected file tuples from multipart-form tab
*/
public HashMap<String, String> getFileTuples() { public HashMap<String, String> getFileTuples() {
if (fileMap == null) if (fileMap == null)
fileMap = new HashMap<>(); fileMap = new HashMap<>();
@ -179,4 +195,32 @@ public class FormDataTabController implements Initializable {
} }
return fileMap; return fileMap;
} }
/**
* @return List of the states of all the non-empty string fields in the Form data tab.
*/
public ArrayList<FieldState> getStringFieldStates() {
ArrayList<FieldState> states = new ArrayList<>();
for (StringKeyValueFieldController controller : stringControllers)
if (!controller.isKeyFieldEmpty() && !controller.isValueFieldEmpty())
states.add(controller.getState());
return states;
}
/**
* @return List of the states of all the non-empty file fields in the Form data tab.
*/
public ArrayList<FieldState> getFileFieldStates() {
ArrayList<FieldState> states = new ArrayList<>();
for (FileKeyValueFieldController controller : fileControllers)
if (!controller.isFileKeyFieldEmpty() && !controller.isFileValueFieldEmpty())
states.add(controller.getState());
return states;
}
} }

View file

@ -16,6 +16,7 @@
package com.rohitawate.everest.controllers; package com.rohitawate.everest.controllers;
import com.rohitawate.everest.controllers.state.FieldState;
import com.rohitawate.everest.misc.Services; import com.rohitawate.everest.misc.Services;
import com.rohitawate.everest.misc.ThemeManager; import com.rohitawate.everest.misc.ThemeManager;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
@ -52,23 +53,25 @@ public class HeaderTabController implements Initializable {
addHeader(); addHeader();
} }
public void addHeader(String key, String value) { public void addHeader(FieldState state) {
addHeader(key, value, null); addHeader(state.key, state.value, null, state.checked);
} }
private void addHeader() { private void addHeader() {
addHeader("", "", null); addHeader("", "", null, false);
} }
@FXML @FXML
private void addHeader(ActionEvent event) { private void addHeader(ActionEvent event) {
addHeader("", "", event); addHeader("", "", event, false);
} }
private void addHeader(String key, String value, ActionEvent event) { private void addHeader(String key, String value, ActionEvent event, boolean checked) {
/* /*
Re-uses previous field if it is empty, Re-uses previous field if it is empty, else loads a new one.
else loads a new one. A value of null for the 'event' parameter indicates that the method call
came from code and not from the user. This call is made while recovering
the application state.
*/ */
if (controllers.size() > 0 && event == null) { if (controllers.size() > 0 && event == null) {
StringKeyValueFieldController previousController = controllers.get(controllers.size() - 1); StringKeyValueFieldController previousController = controllers.get(controllers.size() - 1);
@ -88,6 +91,7 @@ public class HeaderTabController implements Initializable {
StringKeyValueFieldController controller = loader.getController(); StringKeyValueFieldController controller = loader.getController();
controller.setKeyField(key); controller.setKeyField(key);
controller.setValueField(value); controller.setValueField(value);
controller.setChecked(checked);
controllers.add(controller); controllers.add(controller);
controllersCount.set(controllersCount.get() + 1); controllersCount.set(controllersCount.get() + 1);
controller.deleteButton.visibleProperty().bind(Bindings.greaterThan(controllersCount, 1)); controller.deleteButton.visibleProperty().bind(Bindings.greaterThan(controllersCount, 1));
@ -98,14 +102,14 @@ public class HeaderTabController implements Initializable {
}); });
headersBox.getChildren().add(headerField); headersBox.getChildren().add(headerField);
} catch (IOException e) { } catch (IOException e) {
Services.loggingService.logSevere("Could not string field.", e, LocalDateTime.now()); Services.loggingService.logSevere("Could not add string field.", e, LocalDateTime.now());
} }
} }
/** /**
* Returns a map of the selected headers. * @return Map of the selected headers.
*/ */
public HashMap<String, String> getSelectedHeaders() { public HashMap<String, String> getHeaders() {
if (headers == null) if (headers == null)
headers = new HashMap<>(); headers = new HashMap<>();
@ -114,6 +118,20 @@ public class HeaderTabController implements Initializable {
if (controller.isChecked()) if (controller.isChecked())
headers.put(controller.getHeader().getKey(), controller.getHeader().getValue()); headers.put(controller.getHeader().getKey(), controller.getHeader().getValue());
} }
return headers; return headers;
} }
/**
* Return a list of the state of all the non-empty fields in the Headers tab.
*/
public ArrayList<FieldState> getFieldStates() {
ArrayList<FieldState> states = new ArrayList<>();
for (StringKeyValueFieldController controller : controllers)
if (!controller.isKeyFieldEmpty() && !controller.isValueFieldEmpty())
states.add(controller.getState());
return states;
}
} }

View file

@ -16,15 +16,18 @@
package com.rohitawate.everest.controllers; package com.rohitawate.everest.controllers;
import com.rohitawate.everest.models.DashboardState; import com.rohitawate.everest.controllers.state.DashboardState;
import com.rohitawate.everest.controllers.state.FieldState;
import com.rohitawate.everest.misc.Services;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.Tooltip; import javafx.scene.control.Tooltip;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.Map; import java.time.LocalDateTime;
import java.util.ResourceBundle; import java.util.ResourceBundle;
public class HistoryItemController implements Initializable { public class HistoryItemController implements Initializable {
@ -33,35 +36,21 @@ public class HistoryItemController implements Initializable {
@FXML @FXML
private Tooltip tooltip; private Tooltip tooltip;
private DashboardState dashboardState; private DashboardState state;
public void setRequestType(String requestType) {
this.requestType.setText(requestType);
}
public void setAddress(String address) {
this.address.setText(address);
}
public String getRequestType() {
return requestType.getText();
}
public String getAddress() {
return address.getText();
}
@Override @Override
public void initialize(URL location, ResourceBundle resources) { public void initialize(URL location, ResourceBundle resources) {
tooltip.textProperty().bind(address.textProperty()); tooltip.textProperty().bind(address.textProperty());
} }
public DashboardState getDashboardState() { public DashboardState getState() {
return dashboardState; return state;
} }
public void setDashboardState(DashboardState dashboardState) { public void setState(DashboardState state) {
this.dashboardState = dashboardState; this.state = state;
this.requestType.setText(state.httpMethod);
this.address.setText(state.target);
} }
public int getRelativityIndex(String searchString) { public int getRelativityIndex(String searchString) {
@ -69,70 +58,76 @@ public class HistoryItemController implements Initializable {
String comparisonString; String comparisonString;
// Checks if matches with target // Checks if matches with target
comparisonString = dashboardState.getTarget().toString().toLowerCase(); comparisonString = state.target.toLowerCase();
if (comparisonString.contains(searchString)) if (comparisonString.contains(searchString))
return 10; return 10;
// Checks if matches with target's hostname try {
comparisonString = dashboardState.getTarget().getHost().toLowerCase(); URL url = new URL(state.target);
if (comparisonString.contains(searchString))
return 10;
// Checks if matches with target's path // Checks if matches with target's hostname
comparisonString = dashboardState.getTarget().getPath().toLowerCase(); comparisonString = url.getHost().toLowerCase();
if (comparisonString.contains(searchString)) if (comparisonString.contains(searchString))
return 9; return 10;
// Checks if matches with target's path
comparisonString = url.getPath().toLowerCase();
if (comparisonString.contains(searchString))
return 9;
} catch (MalformedURLException e) {
Services.loggingService.logInfo("Failed to parse URL while calculating relativity index.", LocalDateTime.now());
}
// Checks if matches with HTTP method // Checks if matches with HTTP method
comparisonString = dashboardState.getHttpMethod().toLowerCase(); comparisonString = state.httpMethod.toLowerCase();
if (comparisonString.contains(searchString)) if (comparisonString.contains(searchString))
return 7; return 7;
// Checks for a match in the params // Checks for a match in the params
for (Map.Entry param : dashboardState.getParams().entrySet()) { for (FieldState state : state.params) {
if (param.getKey().toString().toLowerCase().contains(searchString) || if (state.key.toLowerCase().contains(searchString) ||
param.getKey().toString().toLowerCase().contains(searchString)) state.value.toLowerCase().contains(searchString))
return 5; return 5;
} }
// Checks for a match in the headers // Checks for a match in the headers
for (Map.Entry header : dashboardState.getHeaders().entrySet()) { for (FieldState state : state.headers) {
if (header.getKey().toString().toLowerCase().contains(searchString) || if (state.key.toLowerCase().contains(searchString) ||
header.getValue().toString().toLowerCase().contains(searchString)) state.value.toLowerCase().contains(searchString))
return 6; return 6;
} }
if (dashboardState.getHttpMethod().equals("POST") || dashboardState.getHttpMethod().equals("PUT")) { if (state.httpMethod.equals("POST") || state.httpMethod.equals("PUT")) {
switch (dashboardState.getContentType()) { switch (state.contentType) {
case MediaType.TEXT_PLAIN: case MediaType.TEXT_PLAIN:
case MediaType.APPLICATION_JSON: case MediaType.APPLICATION_JSON:
case MediaType.APPLICATION_XML: case MediaType.APPLICATION_XML:
case MediaType.TEXT_HTML: case MediaType.TEXT_HTML:
case MediaType.APPLICATION_OCTET_STREAM: case MediaType.APPLICATION_OCTET_STREAM:
// Checks for match in body of the request // Checks for match in rawBody of the request
comparisonString = dashboardState.getBody().toLowerCase(); comparisonString = state.rawBody.toLowerCase();
if (comparisonString.contains(searchString)) if (comparisonString.contains(searchString))
return 8; return 8;
break; break;
case MediaType.APPLICATION_FORM_URLENCODED: case MediaType.APPLICATION_FORM_URLENCODED:
// Checks for match in string tuples // Checks for match in string tuples
for (Map.Entry tuple : dashboardState.getStringTuples().entrySet()) { for (FieldState state : state.urlStringTuples) {
if (tuple.getKey().toString().toLowerCase().contains(searchString) || if (state.key.toLowerCase().contains(searchString) ||
tuple.getValue().toString().toLowerCase().contains(searchString)) state.value.toLowerCase().contains(searchString))
return 8; return 8;
} }
break; break;
case MediaType.MULTIPART_FORM_DATA: case MediaType.MULTIPART_FORM_DATA:
// Checks for match in string and file tuples // Checks for match in string and file tuples
for (Map.Entry tuple : dashboardState.getStringTuples().entrySet()) { for (FieldState state : state.formStringTuples) {
if (tuple.getKey().toString().toLowerCase().contains(searchString) || if (state.key.toLowerCase().contains(searchString) ||
tuple.getValue().toString().toLowerCase().contains(searchString)) state.value.toLowerCase().contains(searchString))
return 8; return 8;
} }
for (Map.Entry tuple : dashboardState.getFileTuples().entrySet()) { for (FieldState state : state.formFileTuples) {
if (tuple.getKey().toString().toLowerCase().contains(searchString) || if (state.key.toLowerCase().contains(searchString) ||
tuple.getValue().toString().toLowerCase().contains(searchString)) state.value.toLowerCase().contains(searchString))
return 8; return 8;
} }
break; break;

View file

@ -16,11 +16,13 @@
package com.rohitawate.everest.controllers; package com.rohitawate.everest.controllers;
import com.fasterxml.jackson.core.type.TypeReference;
import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXButton;
import com.rohitawate.everest.controllers.state.DashboardState;
import com.rohitawate.everest.misc.EverestUtilities;
import com.rohitawate.everest.misc.KeyMap; import com.rohitawate.everest.misc.KeyMap;
import com.rohitawate.everest.misc.Services; import com.rohitawate.everest.misc.Services;
import com.rohitawate.everest.misc.ThemeManager; import com.rohitawate.everest.misc.ThemeManager;
import com.rohitawate.everest.models.DashboardState;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
@ -39,7 +41,8 @@ import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.io.*; import java.io.File;
import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.*; import java.util.*;
@ -92,7 +95,7 @@ public class HomeWindowController implements Initializable {
if (searchResults.size() != 0) { if (searchResults.size() != 0) {
for (HistoryItemController controller : searchResults) { for (HistoryItemController controller : searchResults) {
addSearchItem(controller.getDashboardState()); addSearchItem(controller.getState());
} }
} else { } else {
searchFailedLayer.setVisible(true); searchFailedLayer.setVisible(true);
@ -243,39 +246,36 @@ public class HomeWindowController implements Initializable {
} }
private void saveState() { private void saveState() {
List<DashboardState> dashboardStates = new ArrayList<>(); ArrayList<DashboardState> dashboardStates = new ArrayList<>();
// Get the states of all the tabs // Get the states of all the tabs
for (DashboardController controller : tabControllerMap.values()) for (DashboardController controller : tabControllerMap.values())
dashboardStates.add(controller.getState()); dashboardStates.add(controller.getState());
try { try {
File stateFile = new File("Everest/config/state.json");
File configFolder = new File("Everest/config/"); EverestUtilities.jsonMapper.writeValue(stateFile, dashboardStates);
if (!configFolder.exists()) Services.loggingService.logInfo("Application state saved.", LocalDateTime.now());
configFolder.mkdirs();
OutputStream fileStream = new FileOutputStream("Everest/config/everest.state");
ObjectOutputStream objectStream = new ObjectOutputStream(fileStream);
objectStream.writeObject(dashboardStates);
objectStream.close();
fileStream.close();
Services.loggingService.logInfo("Application state was saved successfully.", LocalDateTime.now());
} catch (IOException e) { } catch (IOException e) {
Services.loggingService.logSevere("Failed to save the application's state.", e, LocalDateTime.now()); Services.loggingService.logSevere("Failed to save application state.", e, LocalDateTime.now());
} }
} }
private void recoverState() { private void recoverState() {
try { try {
InputStream fileStream = new FileInputStream("Everest/config/everest.state"); File stateFile = new File("Everest/config/state.json");
ObjectInputStream objectStream = new ObjectInputStream(fileStream);
Services.loggingService.logInfo("Application state file found.", LocalDateTime.now()); if (!stateFile.exists()) {
Services.loggingService.logInfo("Application state file not found. Loading default state.", LocalDateTime.now());
addTab();
return;
}
List<DashboardState> dashboardStates = (List<DashboardState>) objectStream.readObject(); ArrayList<DashboardState> dashboardStates = EverestUtilities.jsonMapper
objectStream.close(); .reader()
fileStream.close(); .forType(new TypeReference<ArrayList<DashboardState>>() {
})
.readValue(stateFile);
if (dashboardStates.size() > 0) { if (dashboardStates.size() > 0) {
for (DashboardState dashboardState : dashboardStates) for (DashboardState dashboardState : dashboardStates)
@ -283,15 +283,12 @@ public class HomeWindowController implements Initializable {
} else { } else {
addTab(); addTab();
} }
} catch (FileNotFoundException e) { } catch (IOException e) {
Services.loggingService.logWarning("Application state file not found. Loading default state.", e, LocalDateTime.now()); Services.loggingService.logWarning("Application state file is possibly corrupted. State recovery failed. Loading default state.", e, LocalDateTime.now());
addTab();
} catch (IOException | ClassNotFoundException e) {
Services.loggingService.logWarning("Application state file is possibly corrupted. Could not recover the state.\nLoading default state.", e, LocalDateTime.now());
addTab();
} finally { } finally {
Services.loggingService.logInfo("Application loaded.", LocalDateTime.now()); Services.loggingService.logInfo("Application loaded.", LocalDateTime.now());
} }
} }
public void addHistoryItem(DashboardState state) { public void addHistoryItem(DashboardState state) {
@ -311,11 +308,7 @@ public class HomeWindowController implements Initializable {
Parent historyItem = loader.load(); Parent historyItem = loader.load();
controller = loader.getController(); controller = loader.getController();
controller.setState(state);
controller.setRequestType(state.getHttpMethod());
controller.setAddress(state.getTarget().toString());
controller.setDashboardState(state);
if (appendToStart) if (appendToStart)
layer.getChildren().add(0, historyItem); layer.getChildren().add(0, historyItem);

View file

@ -18,6 +18,7 @@ package com.rohitawate.everest.controllers;
import com.jfoenix.controls.JFXButton; import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXCheckBox; import com.jfoenix.controls.JFXCheckBox;
import com.rohitawate.everest.controllers.state.FieldState;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.fxml.Initializable; import javafx.fxml.Initializable;
@ -91,4 +92,12 @@ public class StringKeyValueFieldController implements Initializable {
public boolean isValueFieldEmpty() { public boolean isValueFieldEmpty() {
return valueField.getText().isEmpty(); return valueField.getText().isEmpty();
} }
public FieldState getState() {
return new FieldState(keyField.getText(), valueField.getText(), checkBox.isSelected());
}
public void setChecked(boolean checked) {
checkBox.setSelected(checked);
}
} }

View file

@ -16,6 +16,7 @@
package com.rohitawate.everest.controllers; package com.rohitawate.everest.controllers;
import com.rohitawate.everest.controllers.state.FieldState;
import com.rohitawate.everest.misc.Services; import com.rohitawate.everest.misc.Services;
import com.rohitawate.everest.misc.ThemeManager; import com.rohitawate.everest.misc.ThemeManager;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
@ -51,23 +52,26 @@ public class URLTabController implements Initializable {
addField(); addField();
} }
private void addField() {
addField("", "", null); public void addField(FieldState state) {
addField(state.key, state.value, null, state.checked);
} }
public void addField(String key, String value) { private void addField() {
addField(key, value, null); addField("", "", null, false);
} }
@FXML @FXML
private void addField(ActionEvent event) { private void addField(ActionEvent event) {
addField("", "", event); addField("", "", event, false);
} }
private void addField(String key, String value, ActionEvent event) { private void addField(String key, String value, ActionEvent event, boolean checked) {
/* /*
Re-uses previous field if it is empty, Re-uses previous field if it is empty, else loads a new one.
else loads a new one. A value of null for the 'event' parameter indicates that the method call
came from code and not from the user. This call is made while recovering
the application state.
*/ */
if (controllers.size() > 0 && event == null) { if (controllers.size() > 0 && event == null) {
StringKeyValueFieldController previousController = controllers.get(controllers.size() - 1); StringKeyValueFieldController previousController = controllers.get(controllers.size() - 1);
@ -87,6 +91,7 @@ public class URLTabController implements Initializable {
StringKeyValueFieldController controller = loader.getController(); StringKeyValueFieldController controller = loader.getController();
controller.setKeyField(key); controller.setKeyField(key);
controller.setValueField(value); controller.setValueField(value);
controller.setChecked(checked);
controllers.add(controller); controllers.add(controller);
controllersCount.set(controllersCount.get() + 1); controllersCount.set(controllersCount.get() + 1);
controller.deleteButton.visibleProperty().bind(Bindings.greaterThan(controllersCount, 1)); controller.deleteButton.visibleProperty().bind(Bindings.greaterThan(controllersCount, 1));
@ -97,10 +102,13 @@ public class URLTabController implements Initializable {
}); });
fieldsBox.getChildren().add(stringField); fieldsBox.getChildren().add(stringField);
} catch (IOException e) { } catch (IOException e) {
Services.loggingService.logSevere("Could not load string field.", e, LocalDateTime.now()); Services.loggingService.logSevere("Could not add string field.", e, LocalDateTime.now());
} }
} }
/**
* @return Map of selected string tuples from URL-encoded tab.
*/
public HashMap<String, String> getStringTuples() { public HashMap<String, String> getStringTuples() {
if (tuples == null) if (tuples == null)
tuples = new HashMap<>(); tuples = new HashMap<>();
@ -110,6 +118,21 @@ public class URLTabController implements Initializable {
if (controller.isChecked()) if (controller.isChecked())
tuples.put(controller.getHeader().getKey(), controller.getHeader().getValue()); tuples.put(controller.getHeader().getKey(), controller.getHeader().getValue());
} }
return tuples; return tuples;
} }
/**
* @return A list of the states of all the non-empty fields in the URL-encoded tab.
*/
public ArrayList<FieldState> getFieldStates() {
ArrayList<FieldState> states = new ArrayList<>();
for (StringKeyValueFieldController controller : controllers)
if (!controller.isKeyFieldEmpty() && !controller.isValueFieldEmpty())
states.add(controller.getState());
return states;
}
} }

View file

@ -0,0 +1,46 @@
/*
* Copyright 2018 Rohit Awate.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.rohitawate.everest.controllers.state;
import java.util.ArrayList;
/**
* Convenience class to abstract the state of the application.
*/
public class DashboardState {
public String target;
public String httpMethod;
public ArrayList<FieldState> params;
public ArrayList<FieldState> headers;
// Determined from the active tab within the Body tab
public String contentType;
// Body and content-type of requests with raw bodies
public String rawBody;
public String rawBodyType;
// Tuples of URL-encoded requests
public ArrayList<FieldState> urlStringTuples;
// String and file tuples of multipart-form requests
public ArrayList<FieldState> formStringTuples;
public ArrayList<FieldState> formFileTuples;
// File path of application/octet-stream requests
public String binaryFilePath;
}

View file

@ -0,0 +1,51 @@
/*
* Copyright 2018 Rohit Awate.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.rohitawate.everest.controllers.state;
import java.util.Objects;
/**
* Convenience class to represent the state of StringKeyValueFieldController and FileKeyValueField for
* application state maintenance logic.
*/
public class FieldState {
public String key;
public String value;
public boolean checked;
public FieldState() {
this.key = null;
this.value = null;
this.checked = false;
}
public FieldState(String key, String value, boolean checked) {
this.key = key;
this.value = value;
this.checked = checked;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FieldState that = (FieldState) o;
return checked == that.checked &&
Objects.equals(key, that.key) &&
Objects.equals(value, that.value);
}
}

View file

@ -18,23 +18,21 @@ package com.rohitawate.everest.history;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.rohitawate.everest.controllers.state.DashboardState;
import com.rohitawate.everest.controllers.state.FieldState;
import com.rohitawate.everest.misc.EverestUtilities; import com.rohitawate.everest.misc.EverestUtilities;
import com.rohitawate.everest.misc.Services; import com.rohitawate.everest.misc.Services;
import com.rohitawate.everest.models.DashboardState;
import com.rohitawate.everest.settings.Settings; import com.rohitawate.everest.settings.Settings;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.MalformedURLException;
import java.sql.*; import java.sql.*;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map.Entry;
public class HistoryManager { public class HistoryManager {
private Connection conn; private Connection conn;
@ -61,7 +59,7 @@ public class HistoryManager {
/** /**
* Creates and initializes the database with necessary tables if not already done. * Creates and initializes the database with necessary tables if not already done.
* *
* @throws IOException - If unable to establish a connection to the database. * @throws IOException - If unable to establish a connection to the database.
* @throws SQLException - If invalid statement is executed on the database. * @throws SQLException - If invalid statement is executed on the database.
*/ */
private void initDatabase() throws IOException, SQLException { private void initDatabase() throws IOException, SQLException {
@ -126,18 +124,14 @@ public class HistoryManager {
while (resultSet.next()) { while (resultSet.next()) {
state = new DashboardState(); state = new DashboardState();
try { state.target = resultSet.getString("Target");
state.setTarget(resultSet.getString("Target"));
} catch (MalformedURLException e) {
e.printStackTrace();
}
int requestID = resultSet.getInt("ID"); int requestID = resultSet.getInt("ID");
state.setHeaders(getRequestHeaders(requestID)); state.headers = getRequestHeaders(requestID);
state.setParams(getTuples(requestID, "Param")); state.params = getTuples(requestID, "Param");
state.setHttpMethod(resultSet.getString("Type")); state.httpMethod = resultSet.getString("Type");
if (!(state.getHttpMethod().equals("GET") || state.getHttpMethod().equals("DELETE"))) { if (!(state.httpMethod.equals("GET") || state.httpMethod.equals("DELETE"))) {
// Retrieves request body ContentType for querying corresponding table // Retrieves request body ContentType for querying corresponding table
statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("selectRequestContentType").toString())); statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("selectRequestContentType").toString()));
statement.setInt(1, requestID); statement.setInt(1, requestID);
@ -148,7 +142,7 @@ public class HistoryManager {
if (RS.next()) if (RS.next())
contentType = RS.getString("ContentType"); contentType = RS.getString("ContentType");
state.setContentType(contentType); state.contentType = contentType;
// Retrieves body from corresponding table // Retrieves body from corresponding table
switch (contentType) { switch (contentType) {
@ -163,14 +157,14 @@ public class HistoryManager {
RS = statement.executeQuery(); RS = statement.executeQuery();
if (RS.next()) if (RS.next())
state.setBody(RS.getString("Body")); state.rawBody = RS.getString("Body");
break; break;
case MediaType.APPLICATION_FORM_URLENCODED: case MediaType.APPLICATION_FORM_URLENCODED:
state.setStringTuples(getTuples(requestID, "String")); state.urlStringTuples = getTuples(requestID, "String");
break; break;
case MediaType.MULTIPART_FORM_DATA: case MediaType.MULTIPART_FORM_DATA:
state.setStringTuples(getTuples(requestID, "String")); state.formStringTuples = getTuples(requestID, "String");
state.setFileTuples(getTuples(requestID, "File")); state.formFileTuples = getTuples(requestID, "File");
break; break;
} }
} }
@ -183,8 +177,8 @@ public class HistoryManager {
return history; return history;
} }
private HashMap<String, String> getRequestHeaders(int requestID) { private ArrayList<FieldState> getRequestHeaders(int requestID) {
HashMap<String, String> headers = new HashMap<>(); ArrayList<FieldState> headers = new ArrayList<>();
try { try {
PreparedStatement statement = PreparedStatement statement =
@ -194,10 +188,12 @@ public class HistoryManager {
ResultSet RS = statement.executeQuery(); ResultSet RS = statement.executeQuery();
String key, value; String key, value;
boolean checked;
while (RS.next()) { while (RS.next()) {
key = RS.getString("Key"); key = RS.getString("Key");
value = RS.getString("Value"); value = RS.getString("Value");
headers.put(key, value); checked = RS.getBoolean("Checked");
headers.add(new FieldState(key, value, checked));
} }
} catch (SQLException e) { } catch (SQLException e) {
Services.loggingService.logWarning("Database error.", e, LocalDateTime.now()); Services.loggingService.logWarning("Database error.", e, LocalDateTime.now());
@ -210,11 +206,11 @@ public class HistoryManager {
* @param type Type of tuples needed ('String', 'File' or 'Param') * @param type Type of tuples needed ('String', 'File' or 'Param')
* @return tuples - Map of tuples of corresponding type * @return tuples - Map of tuples of corresponding type
*/ */
private HashMap<String, String> getTuples(int requestID, String type) { private ArrayList<FieldState> getTuples(int requestID, String type) {
if (!(type.equals("String") || type.equals("File") || type.equals("Param"))) if (!(type.equals("String") || type.equals("File") || type.equals("Param")))
return null; return null;
HashMap<String, String> tuples = new HashMap<>(); ArrayList<FieldState> tuples = new ArrayList<>();
try { try {
PreparedStatement statement = PreparedStatement statement =
@ -225,14 +221,17 @@ public class HistoryManager {
ResultSet RS = statement.executeQuery(); ResultSet RS = statement.executeQuery();
String key, value; String key, value;
boolean checked;
while (RS.next()) { while (RS.next()) {
key = RS.getString("Key"); key = RS.getString("Key");
value = RS.getString("Value"); value = RS.getString("Value");
tuples.put(key, value); checked = RS.getBoolean("Checked");
tuples.add(new FieldState(key, value, checked));
} }
} catch (SQLException e) { } catch (SQLException e) {
Services.loggingService.logWarning("Database error.", e, LocalDateTime.now()); Services.loggingService.logWarning("Database error.", e, LocalDateTime.now());
} }
return tuples; return tuples;
} }
@ -249,8 +248,8 @@ public class HistoryManager {
int lastRequestID = -1; int lastRequestID = -1;
if (RS.next()) { if (RS.next()) {
if (!(newState.getHttpMethod().equals(RS.getString("Type"))) || if (!(newState.httpMethod.equals(RS.getString("Type"))) ||
!(newState.getTarget().toString().equals(RS.getString("Target"))) || !(newState.target.equals(RS.getString("Target"))) ||
!(LocalDate.now().equals(LocalDate.parse(RS.getString("Date"))))) !(LocalDate.now().equals(LocalDate.parse(RS.getString("Date")))))
return false; return false;
else else
@ -261,20 +260,32 @@ public class HistoryManager {
if (lastRequestID == -1) if (lastRequestID == -1)
return false; return false;
HashMap<String, String> map; ArrayList<FieldState> states;
// Checks for new or modified headers // Checks for new or modified headers
map = getRequestHeaders(lastRequestID); states = getRequestHeaders(lastRequestID);
if (!areMapsIdentical(map, newState.getHeaders())) if (!areListsEqual(states, newState.headers))
return false; return false;
// Checks for new or modified params // Checks for new or modified params
map = getTuples(lastRequestID, "Param"); states = getTuples(lastRequestID, "Param");
if (!areMapsIdentical(map, newState.getParams())) if (!areListsEqual(states, newState.params))
return false; return false;
if (!(newState.getHttpMethod().equals("GET") || newState.getHttpMethod().equals("DELETE"))) { if (!(newState.httpMethod.equals("GET") || newState.httpMethod.equals("DELETE"))) {
switch (newState.getContentType()) { statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("selectRequestContentType").toString()));
statement.setInt(1, lastRequestID);
RS = statement.executeQuery();
String previousContentType = "";
if (RS.next())
previousContentType = RS.getString("ContentType");
if (!newState.contentType.equals(previousContentType))
return false;
switch (newState.contentType) {
case MediaType.TEXT_PLAIN: case MediaType.TEXT_PLAIN:
case MediaType.APPLICATION_JSON: case MediaType.APPLICATION_JSON:
case MediaType.APPLICATION_XML: case MediaType.APPLICATION_XML:
@ -286,22 +297,22 @@ public class HistoryManager {
RS = statement.executeQuery(); RS = statement.executeQuery();
if (RS.next()) if (RS.next())
if (!RS.getString("Body").equals(newState.getBody())) if (!RS.getString("Body").equals(newState.rawBody))
return false; return false;
break; break;
case MediaType.APPLICATION_FORM_URLENCODED: case MediaType.APPLICATION_FORM_URLENCODED:
// Checks for new or modified string tuples // Checks for new or modified string tuples
map = getTuples(lastRequestID, "String"); states = getTuples(lastRequestID, "String");
return areMapsIdentical(map, newState.getStringTuples()); return areListsEqual(states, newState.urlStringTuples);
case MediaType.MULTIPART_FORM_DATA: case MediaType.MULTIPART_FORM_DATA:
// Checks for new or modified string tuples // Checks for new or modified string tuples
map = getTuples(lastRequestID, "String"); states = getTuples(lastRequestID, "String");
boolean stringComparison = areMapsIdentical(map, newState.getStringTuples()); boolean stringComparison = areListsEqual(states, newState.formStringTuples);
// Checks for new or modified file tuples // Checks for new or modified file tuples
map = getTuples(lastRequestID, "File"); states = getTuples(lastRequestID, "File");
boolean fileComparison = areMapsIdentical(map, newState.getFileTuples()); boolean fileComparison = areListsEqual(states, newState.formFileTuples);
return stringComparison && fileComparison; return stringComparison && fileComparison;
} }
@ -318,19 +329,19 @@ public class HistoryManager {
return true; return true;
} }
private boolean areMapsIdentical(HashMap<String, String> firstMap, HashMap<String, String> secondMap) { private static boolean areListsEqual(ArrayList<FieldState> firstList, ArrayList<FieldState> secondList) {
if (firstMap == null && secondMap == null) if (firstList == null && secondList == null)
return true; return true;
if ((firstMap == null && secondMap != null) || if ((firstList == null && secondList != null) ||
(firstMap != null && secondMap == null)) (firstList != null && secondList == null))
return false; return false;
for (Entry entry : secondMap.entrySet()) { for (FieldState state : secondList) {
if (!firstMap.containsKey(entry.getKey().toString()) || if (!firstList.contains(state))
!firstMap.get(entry.getKey().toString()).equals(entry.getValue().toString()))
return false; return false;
} }
return true; return true;
} }
@ -343,8 +354,8 @@ public class HistoryManager {
statement = statement =
conn.prepareStatement(EverestUtilities.trimString(queries.get("saveRequest").toString())); conn.prepareStatement(EverestUtilities.trimString(queries.get("saveRequest").toString()));
statement.setString(1, state.getHttpMethod()); statement.setString(1, state.httpMethod);
statement.setString(2, String.valueOf(state.getTarget())); statement.setString(2, state.target);
statement.setString(3, LocalDate.now().toString()); statement.setString(3, LocalDate.now().toString());
statement.executeUpdate(); statement.executeUpdate();
@ -357,41 +368,44 @@ public class HistoryManager {
if (RS.next()) if (RS.next())
requestID = RS.getInt("MaxID"); requestID = RS.getInt("MaxID");
if (state.getHeaders().size() > 0) { if (state.headers.size() > 0) {
// Saves request headers // Saves request headers
statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveHeader").toString())); statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveHeader").toString()));
for (Entry entry : state.getHeaders().entrySet()) {
for (FieldState fieldState : state.headers) {
statement.setInt(1, requestID); statement.setInt(1, requestID);
statement.setString(2, entry.getKey().toString()); statement.setString(2, fieldState.key);
statement.setString(3, entry.getValue().toString()); statement.setString(3, fieldState.value);
statement.setInt(4, fieldState.checked ? 1 : 0);
statement.executeUpdate(); statement.executeUpdate();
} }
} }
if (state.getParams().size() > 0) { if (state.params.size() > 0) {
// Saves request parameters // Saves request parameters
statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveTuple").toString())); statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveTuple").toString()));
for (Entry entry : state.getParams().entrySet()) { for (FieldState fieldState : state.params) {
statement.setInt(1, requestID); statement.setInt(1, requestID);
statement.setString(2, "Param"); statement.setString(2, "Param");
statement.setString(3, entry.getKey().toString()); statement.setString(3, fieldState.key);
statement.setString(4, entry.getValue().toString()); statement.setString(4, fieldState.value);
statement.setInt(5, fieldState.checked ? 1 : 0);
statement.executeUpdate(); statement.executeUpdate();
} }
} }
if (!(state.getHttpMethod().equals("GET") || state.getHttpMethod().equals("DELETE"))) { if (!(state.httpMethod.equals("GET") || state.httpMethod.equals("DELETE"))) {
// Maps the request to its ContentType for faster recovery // Maps the request to its ContentType for faster retrieval
statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveRequestContentPair").toString())); statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveRequestContentPair").toString()));
statement.setInt(1, requestID); statement.setInt(1, requestID);
statement.setString(2, state.getContentType()); statement.setString(2, state.contentType);
statement.executeUpdate(); statement.executeUpdate();
// Determines where to fetch the body from, based on the ContentType // Determines where to fetch the body from, based on the ContentType
switch (state.getContentType()) { switch (state.contentType) {
case MediaType.TEXT_PLAIN: case MediaType.TEXT_PLAIN:
case MediaType.APPLICATION_JSON: case MediaType.APPLICATION_JSON:
case MediaType.APPLICATION_XML: case MediaType.APPLICATION_XML:
@ -400,45 +414,48 @@ public class HistoryManager {
// Saves the body in case of raw content, or the file location in case of binary // Saves the body in case of raw content, or the file location in case of binary
statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveBody").toString())); statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveBody").toString()));
statement.setInt(1, requestID); statement.setInt(1, requestID);
statement.setString(2, state.getBody()); statement.setString(2, state.rawBody);
statement.executeUpdate(); statement.executeUpdate();
break; break;
case MediaType.APPLICATION_FORM_URLENCODED: case MediaType.APPLICATION_FORM_URLENCODED:
if (state.getStringTuples().size() > 0) { if (state.urlStringTuples.size() > 0) {
for (Entry<String, String> entry : state.getStringTuples().entrySet()) { for (FieldState fieldState : state.urlStringTuples) {
// Saves the string tuples // Saves the string tuples
statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveTuple").toString())); statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveTuple").toString()));
statement.setInt(1, requestID); statement.setInt(1, requestID);
statement.setString(2, "String"); statement.setString(2, "String");
statement.setString(3, entry.getKey()); statement.setString(3, fieldState.key);
statement.setString(4, entry.getValue()); statement.setString(4, fieldState.value);
statement.setInt(5, fieldState.checked ? 1 : 0);
statement.executeUpdate(); statement.executeUpdate();
} }
} }
break; break;
case MediaType.MULTIPART_FORM_DATA: case MediaType.MULTIPART_FORM_DATA:
if (state.getStringTuples().size() > 0) { if (state.formStringTuples.size() > 0) {
for (Entry<String, String> entry : state.getStringTuples().entrySet()) { for (FieldState fieldState : state.formStringTuples) {
// Saves the string tuples // Saves the string tuples
statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveTuple").toString())); statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveTuple").toString()));
statement.setInt(1, requestID); statement.setInt(1, requestID);
statement.setString(2, "String"); statement.setString(2, "String");
statement.setString(3, entry.getKey()); statement.setString(3, fieldState.key);
statement.setString(4, entry.getValue()); statement.setString(4, fieldState.value);
statement.setInt(5, fieldState.checked ? 1 : 0);
statement.executeUpdate(); statement.executeUpdate();
} }
} }
if (state.getFileTuples().size() > 0) { if (state.formFileTuples.size() > 0) {
for (Entry<String, String> entry : state.getFileTuples().entrySet()) { for (FieldState fieldState : state.formFileTuples) {
// Saves the file tuples // Saves the string tuples
statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveTuple").toString())); statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("saveTuple").toString()));
statement.setInt(1, requestID); statement.setInt(1, requestID);
statement.setString(2, "File"); statement.setString(2, "File");
statement.setString(3, entry.getKey()); statement.setString(3, fieldState.key);
statement.setString(4, entry.getValue()); statement.setString(4, fieldState.value);
statement.setInt(5, fieldState.checked ? 1 : 0);
statement.executeUpdate(); statement.executeUpdate();
} }

View file

@ -1,62 +0,0 @@
/*
* Copyright 2018 Rohit Awate.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.rohitawate.everest.models;
import com.rohitawate.everest.models.requests.DataDispatchRequest;
import java.io.Serializable;
import java.util.HashMap;
/**
* Convenience class to abstract the state of the application.
*/
public class DashboardState extends DataDispatchRequest implements Serializable {
private HashMap<String, String> params;
private String httpMethod;
public DashboardState() {
}
/*
Special copy constructor to instantiate DashboardState from
BodyTabController's getBasicRequest()
*/
public DashboardState(DataDispatchRequest dataDispatchRequest) {
super();
this.setHttpMethod(dataDispatchRequest.getRequestType());
this.setBody(dataDispatchRequest.getBody());
this.setContentType(dataDispatchRequest.getContentType());
this.setStringTuples(dataDispatchRequest.getStringTuples());
this.setFileTuples(dataDispatchRequest.getFileTuples());
}
public void setParams(HashMap<String, String> params) {
this.params = params;
}
public HashMap<String, String> getParams() {
return params;
}
public void setHttpMethod(String httpMethod) {
this.httpMethod = httpMethod;
}
public String getHttpMethod() {
return httpMethod;
}
}

View file

@ -41,7 +41,8 @@ public class SettingsLoader implements Runnable {
try { try {
File settingsFile = new File("Everest/config/settings.json"); File settingsFile = new File("Everest/config/settings.json");
System.out.println("Settings file found. Loading settings... "); if (settingsFile.exists())
System.out.println("Settings file found. Loading settings... ");
nodes = EverestUtilities.jsonMapper.readTree(settingsFile); nodes = EverestUtilities.jsonMapper.readTree(settingsFile);

View file

@ -1,14 +1,14 @@
{ {
"createRequestsTable": "CREATE TABLE IF NOT EXISTS Requests(ID INTEGER PRIMARY KEY, Type TEXT NOT NULL, Target TEXT NOT NULL, Date TEXT NOT NULL)", "createRequestsTable": "CREATE TABLE IF NOT EXISTS Requests(ID INTEGER PRIMARY KEY, Type TEXT NOT NULL, Target TEXT NOT NULL, Date TEXT NOT NULL)",
"createHeadersTable": "CREATE TABLE IF NOT EXISTS Headers(RequestID INTEGER, Key TEXT NOT NULL, Value TEXT NOT NULL, FOREIGN KEY(RequestID) REFERENCES Requests(ID))", "createHeadersTable": "CREATE TABLE IF NOT EXISTS Headers(RequestID INTEGER, Key TEXT NOT NULL, Value TEXT NOT NULL, Checked INTEGER CHECK (Checked IN (0, 1)), FOREIGN KEY(RequestID) REFERENCES Requests(ID))",
"createRequestContentMapTable": "CREATE TABLE IF NOT EXISTS RequestContentMap(RequestID INTEGER, ContentType TEXT NOT NULL, FOREIGN KEY(RequestID) REFERENCES Requests(ID))", "createRequestContentMapTable": "CREATE TABLE IF NOT EXISTS RequestContentMap(RequestID INTEGER, ContentType TEXT NOT NULL, FOREIGN KEY(RequestID) REFERENCES Requests(ID))",
"createBodiesTable": "CREATE TABLE IF NOT EXISTS Bodies(RequestID INTEGER, Body TEXT NOT NULL, FOREIGN KEY(RequestID) REFERENCES Requests(ID))", "createBodiesTable": "CREATE TABLE IF NOT EXISTS Bodies(RequestID INTEGER, Body TEXT NOT NULL, FOREIGN KEY(RequestID) REFERENCES Requests(ID))",
"createTuplesTable": "CREATE TABLE IF NOT EXISTS Tuples(RequestID INTEGER, TupleType TEXT NOT NULL, Key TEXT NOT NULL, Value TEXT NOT NULL, FOREIGN KEY(RequestID) REFERENCES Requests(ID))", "createTuplesTable": "CREATE TABLE IF NOT EXISTS Tuples(RequestID INTEGER, TupleType TEXT NOT NULL, Key TEXT NOT NULL, Value TEXT NOT NULL, Checked INTEGER CHECK (Checked IN (0, 1)), FOREIGN KEY(RequestID) REFERENCES Requests(ID))",
"saveRequest": "INSERT INTO Requests(Type, Target, Date) VALUES(?, ?, ?)", "saveRequest": "INSERT INTO Requests(Type, Target, Date) VALUES(?, ?, ?)",
"saveHeader": "INSERT INTO Headers(RequestID, Key, Value) VALUES(?, ?, ?)", "saveHeader": "INSERT INTO Headers(RequestID, Key, Value, Checked) VALUES(?, ?, ?, ?)",
"saveRequestContentPair": "INSERT INTO RequestContentMap(RequestID, ContentType) VALUES(?, ?)", "saveRequestContentPair": "INSERT INTO RequestContentMap(RequestID, ContentType) VALUES(?, ?)",
"saveBody": "INSERT INTO Bodies(RequestID, Body) VALUES(?, ?)", "saveBody": "INSERT INTO Bodies(RequestID, Body) VALUES(?, ?)",
"saveTuple": "INSERT INTO Tuples(RequestID, TupleType, Key, Value) VALUES(?, ?, ?, ?)", "saveTuple": "INSERT INTO Tuples(RequestID, TupleType, Key, Value, Checked) VALUES(?, ?, ?, ?, ?)",
"selectRecentRequests": "SELECT * FROM Requests WHERE Requests.Date > ?", "selectRecentRequests": "SELECT * FROM Requests WHERE Requests.Date > ?",
"selectRequestHeaders": "SELECT * FROM Headers WHERE RequestID == ?", "selectRequestHeaders": "SELECT * FROM Headers WHERE RequestID == ?",
"selectRequestContentType": "SELECT ContentType FROM RequestContentMap WHERE RequestID == ?", "selectRequestContentType": "SELECT ContentType FROM RequestContentMap WHERE RequestID == ?",