Added HistoryTab

This commit is contained in:
Rohit Awate 2018-02-18 22:58:21 +05:30
parent 921654200d
commit 83f557dd38
16 changed files with 542 additions and 154 deletions

View file

@ -79,6 +79,8 @@ public class BodyTabController implements Initializable {
public DataDispatchRequest getBasicRequest(String requestType) { public DataDispatchRequest getBasicRequest(String requestType) {
DataDispatchRequest request = new DataDispatchRequest(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()) { if (rawTab.isSelected()) {
String contentType; String contentType;
switch (rawInputTypeBox.getValue()) { switch (rawInputTypeBox.getValue()) {

View file

@ -27,6 +27,7 @@ import com.rohitawate.restaurant.requestsmanager.DELETERequestManager;
import com.rohitawate.restaurant.requestsmanager.DataDispatchRequestManager; import com.rohitawate.restaurant.requestsmanager.DataDispatchRequestManager;
import com.rohitawate.restaurant.requestsmanager.GETRequestManager; import com.rohitawate.restaurant.requestsmanager.GETRequestManager;
import com.rohitawate.restaurant.requestsmanager.RequestManager; import com.rohitawate.restaurant.requestsmanager.RequestManager;
import com.rohitawate.restaurant.util.Services;
import com.rohitawate.restaurant.util.settings.Settings; import com.rohitawate.restaurant.util.settings.Settings;
import com.rohitawate.restaurant.util.themes.ThemeManager; import com.rohitawate.restaurant.util.themes.ThemeManager;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
@ -64,7 +65,7 @@ public class DashboardController implements Initializable {
@FXML @FXML
private Label statusCode, statusCodeDescription, responseTime, responseSize, errorTitle, errorDetails; private Label statusCode, statusCodeDescription, responseTime, responseSize, errorTitle, errorDetails;
@FXML @FXML
private JFXButton cancelButton; private JFXButton sendButton, cancelButton;
@FXML @FXML
private TabPane requestOptionsTab; private TabPane requestOptionsTab;
@FXML @FXML
@ -291,6 +292,7 @@ public class DashboardController implements Initializable {
default: default:
loadingLayer.setVisible(false); loadingLayer.setVisible(false);
} }
Services.historyManager.saveHistory(getState());
} catch (MalformedURLException MURLE) { } catch (MalformedURLException MURLE) {
promptLayer.setVisible(true); promptLayer.setVisible(true);
snackBar.show("Invalid address. Please verify and try again.", 3000); snackBar.show("Invalid address. Please verify and try again.", 3000);
@ -394,7 +396,7 @@ public class DashboardController implements Initializable {
* @return DashboardState - Current state of the Dashboard * @return DashboardState - Current state of the Dashboard
*/ */
public DashboardState getState() { public DashboardState getState() {
DashboardState dashboardState = null; DashboardState dashboardState;
switch (httpMethodBox.getValue()) { switch (httpMethodBox.getValue()) {
case "POST": case "POST":
case "PUT": case "PUT":

View file

@ -0,0 +1,30 @@
package com.rohitawate.restaurant.homewindow;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.layout.HBox;
import java.net.URL;
import java.util.ResourceBundle;
public class HistoryItemController {
@FXML
private Label requestType, address;
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();
}
}

View file

@ -17,45 +17,91 @@
package com.rohitawate.restaurant.homewindow; package com.rohitawate.restaurant.homewindow;
import com.rohitawate.restaurant.models.DashboardState; import com.rohitawate.restaurant.models.DashboardState;
import com.rohitawate.restaurant.models.requests.DataDispatchRequest;
import com.rohitawate.restaurant.models.requests.GETRequest;
import com.rohitawate.restaurant.models.requests.RestaurantRequest;
import com.rohitawate.restaurant.util.Services;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.concurrent.Task;
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.Scene; import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.Tab; import javafx.scene.control.Tab;
import javafx.scene.control.TabPane; import javafx.scene.control.TabPane;
import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination; import javafx.scene.input.KeyCombination;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.io.*; import java.io.*;
import java.net.MalformedURLException;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.ResourceBundle; import java.util.ResourceBundle;
import java.util.concurrent.ExecutionException;
public class HomeWindowController implements Initializable { public class HomeWindowController implements Initializable {
@FXML @FXML
private TabPane homeWindowTabPane; private TabPane homeWindowTabPane;
@FXML
private VBox historyTab;
@FXML
private StackPane historyPromptLayer;
private KeyCombination newTab = new KeyCodeCombination(KeyCode.T, KeyCombination.CONTROL_DOWN); private KeyCombination newTab = new KeyCodeCombination(KeyCode.T, KeyCombination.CONTROL_DOWN);
private List<DashboardController> controllers; private List<DashboardController> dashboardControllers;
private List<HistoryItemController> historyItemControllers;
@Override @Override
public void initialize(URL location, ResourceBundle resources) { public void initialize(URL location, ResourceBundle resources) {
controllers = new ArrayList<>(); dashboardControllers = new ArrayList<>();
historyItemControllers = new ArrayList<>();
recoverState(); recoverState();
Platform.runLater(() -> { Platform.runLater(() -> {
// Adds a new tab if the last tab is closed
Scene thisScene = homeWindowTabPane.getScene(); Scene thisScene = homeWindowTabPane.getScene();
thisScene.setOnKeyPressed(e -> { thisScene.setOnKeyPressed(e -> {
if (newTab.match(e)) if (newTab.match(e))
addTab(); addTab();
}); });
// Saves the state of the application before closing
Stage thisStage = (Stage) thisScene.getWindow(); Stage thisStage = (Stage) thisScene.getWindow();
thisStage.setOnCloseRequest(e -> saveState()); thisStage.setOnCloseRequest(e -> saveState());
// Loads the history
Task<List<DashboardState>> historyLoader = new Task<List<DashboardState>>() {
@Override
protected List<DashboardState> call() throws Exception {
return Services.historyManager.getHistory();
}
};
// Appends the history items to the HistoryTab
historyLoader.setOnSucceeded(e -> {
try {
List<DashboardState> history = historyLoader.get();
if (history.size() == 0) {
historyPromptLayer.setVisible(true);
return;
}
for (DashboardState state : history)
addHistoryItem(state);
} catch (InterruptedException | ExecutionException E) {
E.printStackTrace();
}
});
historyLoader.setOnFailed(e -> historyLoader.getException().printStackTrace());
new Thread(historyLoader).start();
}); });
} }
@ -79,10 +125,11 @@ public class HomeWindowController implements Initializable {
newTab.setOnCloseRequest(e -> { newTab.setOnCloseRequest(e -> {
if (homeWindowTabPane.getTabs().size() == 1) if (homeWindowTabPane.getTabs().size() == 1)
addTab(); addTab();
controllers.remove(controller); dashboardControllers.remove(controller);
}); });
homeWindowTabPane.getTabs().add(newTab); homeWindowTabPane.getTabs().add(newTab);
controllers.add(controller); homeWindowTabPane.getSelectionModel().select(newTab);
dashboardControllers.add(controller);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
@ -92,7 +139,7 @@ public class HomeWindowController implements Initializable {
List<DashboardState> dashboardStates = new ArrayList<>(); List<DashboardState> dashboardStates = new ArrayList<>();
// Get the states of all the tabs // Get the states of all the tabs
for (DashboardController controller : controllers) for (DashboardController controller : dashboardControllers)
dashboardStates.add(controller.getState()); dashboardStates.add(controller.getState());
try { try {
@ -139,4 +186,27 @@ public class HomeWindowController implements Initializable {
System.out.println("Application loaded."); System.out.println("Application loaded.");
} }
} }
public void addHistoryItem(DashboardState state) {
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/homewindow/HistoryItem.fxml"));
Parent historyItem = loader.load();
HistoryItemController controller = loader.getController();
controller.setRequestType(state.getHttpMethod());
controller.setAddress(state.getTarget().toString());
historyTab.getChildren().add(0, historyItem);
historyItemControllers.add(controller);
// Clicking on HistoryItem opens it up in a new tab
historyItem.setOnMouseClicked(mouseEvent -> {
if (mouseEvent.getButton() == MouseButton.PRIMARY)
addTab(state);
});
} catch (IOException IOE) {
IOE.printStackTrace();
}
}
} }

View file

@ -33,7 +33,9 @@ public class Main extends Application {
new Services(); new Services();
Parent dashboard = FXMLLoader.load(getClass().getResource("/fxml/homewindow/HomeWindow.fxml")); FXMLLoader loader = new FXMLLoader(getClass().getResource("/fxml/homewindow/HomeWindow.fxml"));
Parent dashboard = loader.load();
Services.homeWindowController = loader.getController();
Stage dashboardStage = new Stage(); Stage dashboardStage = new Stage();
ThemeManager.setTheme(dashboard); ThemeManager.setTheme(dashboard);

View file

@ -39,8 +39,6 @@ public class DELETERequestManager extends RequestManager {
protected RestaurantResponse call() throws Exception { protected RestaurantResponse call() throws Exception {
DELETERequest deleteRequest = (DELETERequest) request; DELETERequest deleteRequest = (DELETERequest) request;
Services.historyManager.saveHistory(deleteRequest);
RestaurantResponse response = new RestaurantResponse(); RestaurantResponse response = new RestaurantResponse();
WebTarget target = client.target(deleteRequest.getTarget().toString()); WebTarget target = client.target(deleteRequest.getTarget().toString());
Map.Entry<String, String> mapEntry; Map.Entry<String, String> mapEntry;

View file

@ -54,8 +54,6 @@ public class DataDispatchRequestManager extends RequestManager {
DataDispatchRequest dataDispatchRequest = (DataDispatchRequest) request; DataDispatchRequest dataDispatchRequest = (DataDispatchRequest) request;
String requestType = dataDispatchRequest.getRequestType(); String requestType = dataDispatchRequest.getRequestType();
Services.historyManager.saveHistory(dataDispatchRequest);
RestaurantResponse response = new RestaurantResponse(); RestaurantResponse response = new RestaurantResponse();
WebTarget target = client.target(dataDispatchRequest.getTarget().toString()); WebTarget target = client.target(dataDispatchRequest.getTarget().toString());
Map.Entry<String, String> mapEntry; Map.Entry<String, String> mapEntry;

View file

@ -39,8 +39,6 @@ public class GETRequestManager extends RequestManager {
RestaurantResponse response = new RestaurantResponse(); RestaurantResponse response = new RestaurantResponse();
WebTarget target = client.target(request.getTarget().toString()); WebTarget target = client.target(request.getTarget().toString());
Services.historyManager.saveHistory(request);
Builder requestBuilder = target.request(); Builder requestBuilder = target.request();
HashMap<String, String> headers = request.getHeaders(); HashMap<String, String> headers = request.getHeaders();

View file

@ -16,10 +16,12 @@
package com.rohitawate.restaurant.util; package com.rohitawate.restaurant.util;
import com.rohitawate.restaurant.homewindow.HomeWindowController;
import com.rohitawate.restaurant.util.history.HistoryManager; import com.rohitawate.restaurant.util.history.HistoryManager;
public class Services { public class Services {
public static HistoryManager historyManager; public static HistoryManager historyManager;
public static HomeWindowController homeWindowController;
static { static {
historyManager = new HistoryManager(); historyManager = new HistoryManager();

View file

@ -18,16 +18,25 @@ package com.rohitawate.restaurant.util.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.restaurant.models.DashboardState;
import com.rohitawate.restaurant.models.requests.DELETERequest; import com.rohitawate.restaurant.models.requests.DELETERequest;
import com.rohitawate.restaurant.models.requests.DataDispatchRequest; import com.rohitawate.restaurant.models.requests.DataDispatchRequest;
import com.rohitawate.restaurant.models.requests.GETRequest; import com.rohitawate.restaurant.models.requests.GETRequest;
import com.rohitawate.restaurant.models.requests.RestaurantRequest; import com.rohitawate.restaurant.models.requests.RestaurantRequest;
import com.rohitawate.restaurant.util.Services;
import com.rohitawate.restaurant.util.json.JSONUtils; import com.rohitawate.restaurant.util.json.JSONUtils;
import com.rohitawate.restaurant.util.settings.Settings;
import javafx.util.Pair;
import javax.ws.rs.core.MediaType;
import java.io.File; import java.io.File;
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.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
public class HistoryManager { public class HistoryManager {
@ -55,6 +64,18 @@ public class HistoryManager {
statement = statement =
conn.prepareStatement(JSONUtils.trimString(queries.get("createHeadersTable").toString())); conn.prepareStatement(JSONUtils.trimString(queries.get("createHeadersTable").toString()));
statement.execute(); statement.execute();
statement =
conn.prepareStatement(JSONUtils.trimString(queries.get("createRequestContentMapTable").toString()));
statement.execute();
statement =
conn.prepareStatement(JSONUtils.trimString(queries.get("createBodiesTable").toString()));
statement.execute();
statement =
conn.prepareStatement(JSONUtils.trimString(queries.get("createTuplesTable").toString()));
statement.execute();
} catch (Exception E) { } catch (Exception E) {
E.printStackTrace(); E.printStackTrace();
} finally { } finally {
@ -63,47 +84,225 @@ public class HistoryManager {
} }
// Method is made synchronized to allow only one database transaction at a time. // Method is made synchronized to allow only one database transaction at a time.
public synchronized void saveHistory(RestaurantRequest request) { public synchronized void saveHistory(DashboardState state) {
try { new Thread(() -> {
statement = try {
conn.prepareStatement(JSONUtils.trimString(queries.get("saveRequest").toString())); statement =
conn.prepareStatement(JSONUtils.trimString(queries.get("saveRequest").toString()));
// Determines the request type statement.setString(1, state.getHttpMethod());
if (request.getClass() == GETRequest.class) statement.setString(2, String.valueOf(state.getTarget()));
statement.setString(1, "GET"); statement.setString(3, LocalDate.now().toString());
else if (request.getClass() == DataDispatchRequest.class) {
if (((DataDispatchRequest) request).getRequestType().equals("POST"))
statement.setString(1, "POST");
else
statement.setString(1, "PUT");
} else if (request.getClass() == DELETERequest.class)
statement.setString(1, "DELETE");
statement.setString(2, String.valueOf(request.getTarget())); statement.executeUpdate();
statement.setString(3, LocalDate.now().toString());
statement.executeUpdate(); if (state.getHeaders().size() > 0) {
// Get latest RequestID to insert into Headers table
statement = conn.prepareStatement("SELECT MAX(ID) AS MaxID FROM Requests");
if (request.getHeaders().size() > 0) { ResultSet RS = statement.executeQuery();
// Get latest RequestID to insert into Headers table int requestID = -1;
statement = conn.prepareStatement("SELECT MAX(ID) AS MaxID FROM Requests"); if (RS.next())
requestID = RS.getInt("MaxID");
ResultSet RS = statement.executeQuery(); // Saves request headers
int requestID = -1; statement = conn.prepareStatement(JSONUtils.trimString(queries.get("saveHeader").toString()));
if (RS.next()) for (Map.Entry entry : state.getHeaders().entrySet()) {
requestID = RS.getInt("MaxID"); statement.setInt(1, requestID);
statement.setString(2, entry.getKey().toString());
statement.setString(3, entry.getValue().toString());
statement = conn.prepareStatement(JSONUtils.trimString(queries.get("saveHeader").toString())); statement.executeUpdate();
for (Map.Entry entry : request.getHeaders().entrySet()) { }
statement.setInt(1, requestID);
statement.setString(2, entry.getKey().toString());
statement.setString(3, entry.getValue().toString());
statement.executeUpdate(); if (state.getHttpMethod().equals("POST") || state.getHttpMethod().equals("PUT")) {
// Maps the request to its ContentType for faster recovery
statement = conn.prepareStatement(JSONUtils.trimString(queries.get("saveRequestContentPair").toString()));
statement.setInt(1, requestID);
statement.setString(2, state.getContentType());
statement.executeUpdate();
// Determines where to fetch the body from, based on the ContentType
switch (state.getContentType()) {
case MediaType.TEXT_PLAIN:
case MediaType.APPLICATION_JSON:
case MediaType.APPLICATION_XML:
case MediaType.TEXT_HTML:
case MediaType.APPLICATION_OCTET_STREAM:
// Saves the body in case of raw content, or the file location in case of binary
statement = conn.prepareStatement(JSONUtils.trimString(queries.get("saveBody").toString()));
statement.setInt(1, requestID);
statement.setString(2, state.getBody());
statement.executeUpdate();
break;
case MediaType.APPLICATION_FORM_URLENCODED:
for (Map.Entry<String, String> entry : state.getStringTuples().entrySet()) {
// Saves the string tuples
statement = conn.prepareStatement(JSONUtils.trimString(queries.get("saveTuple").toString()));
statement.setInt(1, requestID);
statement.setString(2, "String");
statement.setString(3, entry.getKey());
statement.setString(4, entry.getValue());
statement.executeUpdate();
}
break;
case MediaType.MULTIPART_FORM_DATA:
for (Map.Entry<String, String> entry : state.getStringTuples().entrySet()) {
// Saves the string tuples
statement = conn.prepareStatement(JSONUtils.trimString(queries.get("saveTuple").toString()));
statement.setInt(1, requestID);
statement.setString(2, "String");
statement.setString(3, entry.getKey());
statement.setString(4, entry.getValue());
statement.executeUpdate();
}
for (Map.Entry<String, String> entry : state.getFileTuples().entrySet()) {
// Saves the file tuples
statement = conn.prepareStatement(JSONUtils.trimString(queries.get("saveTuple").toString()));
statement.setInt(1, requestID);
statement.setString(2, "File");
statement.setString(3, entry.getKey());
statement.setString(4, entry.getValue());
statement.executeUpdate();
}
break;
}
}
} }
} catch (SQLException e) {
e.printStackTrace();
}
}).start();
// Appends this history item to the HistoryTab
Services.homeWindowController.addHistoryItem(state);
}
public synchronized List<DashboardState> getHistory() {
List<DashboardState> history = new ArrayList<>();
try {
// Loads the requests from the last x number of days, x being stored in Settings.showHistoryRange
statement = conn.prepareStatement(JSONUtils.trimString(queries.get("selectRecentRequests").toString()));
String historyStartDate = LocalDate.now().minusDays(Settings.showHistoryRange).toString();
statement.setString(1, historyStartDate);
ResultSet resultSet = statement.executeQuery();
DashboardState state;
while (resultSet.next()) {
state = new DashboardState();
try {
state.setTarget(resultSet.getString("Target"));
} catch (MalformedURLException e) {
e.printStackTrace();
}
int requestID = resultSet.getInt("ID");
state.setHeaders(getRequestHeaders(requestID));
state.setHttpMethod(resultSet.getString("Type"));
if (state.getHttpMethod().equals("POST") || state.getHttpMethod().equals("PUT")) {
// Retrieves request body ContentType for querying corresponding table
statement = conn.prepareStatement(JSONUtils.trimString(queries.get("selectRequestContentType").toString()));
statement.setInt(1, requestID);
ResultSet RS = statement.executeQuery();
String contentType = "";
if (RS.next())
contentType = RS.getString("ContentType");
state.setContentType(contentType);
// Retrieves body from corresponding table
switch (contentType) {
case MediaType.TEXT_PLAIN:
case MediaType.APPLICATION_JSON:
case MediaType.APPLICATION_XML:
case MediaType.TEXT_HTML:
case MediaType.APPLICATION_OCTET_STREAM:
statement = conn.prepareStatement(JSONUtils.trimString(queries.get("selectRequestBody").toString()));
statement.setInt(1, requestID);
RS = statement.executeQuery();
if (RS.next())
state.setBody(resultSet.getString("Body"));
break;
case MediaType.APPLICATION_FORM_URLENCODED:
state.setStringTuples(getTuples(requestID, "String"));
break;
case MediaType.MULTIPART_FORM_DATA:
state.setStringTuples(getTuples(requestID, "String"));
state.setFileTuples(getTuples(requestID, "Files"));
break;
}
}
history.add(state);
} }
} catch (SQLException e) { } catch (SQLException e) {
e.printStackTrace(); e.printStackTrace();
} }
return history;
}
private HashMap<String, String> getRequestHeaders(int requestID) {
HashMap<String, String> headers = new HashMap<>();
try {
PreparedStatement statement =
conn.prepareStatement(JSONUtils.trimString(queries.get("selectRequestHeaders").toString()));
statement.setInt(1, requestID);
ResultSet RS = statement.executeQuery();
String key, value;
while (RS.next()) {
key = RS.getString("Key");
value = RS.getString("Value");
headers.put(key, value);
}
} catch (SQLException e) {
e.printStackTrace();
}
return headers;
}
/**
*
* @param requestID Database ID of the request whose tuples are needed.
* @param type Type of tuples needed ('String' or 'File')
* @return tuples - Map of tuples of corresponding type
*/
private HashMap<String, String> getTuples(int requestID, String type) {
if (!type.equals("String") || !type.equals("File"))
return null;
HashMap<String, String> tuples = new HashMap<>();
try {
PreparedStatement statement =
conn.prepareStatement(JSONUtils.trimString(queries.get("selectTuples").toString()));
statement.setInt(1, requestID);
statement.setString(2, type);
ResultSet RS = statement.executeQuery();
String key, value;
while (RS.next()) {
key = RS.getString("Key");
value = RS.getString("Value");
tuples.put(key, value);
}
} catch (SQLException e) {
e.printStackTrace();
}
return tuples;
} }
} }

View file

@ -31,4 +31,5 @@ public class Settings {
public static int connectionReadTimeOut = 30000; public static int connectionReadTimeOut = 30000;
public static String theme = "Adreana"; public static String theme = "Adreana";
public static long showHistoryRange = 7;
} }

View file

@ -98,6 +98,7 @@
.scroll-pane .viewport, .scroll-pane .viewport,
.scroll-pane .scroll-bar:vertical { .scroll-pane .scroll-bar:vertical {
-fx-background-color: #3d3d3d; -fx-background-color: #3d3d3d;
-fx-background-insets: 0px;
} }
/* Tab attributes */ /* Tab attributes */
@ -141,11 +142,19 @@
-fx-background-color: #808080; -fx-background-color: #808080;
} }
.split-pane, .split-pane {
-fx-background-color: #505050;
-fx-padding: 0px;
}
.split-pane .split-pane-divider { .split-pane .split-pane-divider {
-fx-background-color: #505050; -fx-background-color: #505050;
} }
.split-pane:horizontal .split-pane-divider {
-fx-padding: 0px;
}
#keyField, #valueField, #filePathField { #keyField, #valueField, #filePathField {
-fx-prompt-text-fill: #919191; -fx-prompt-text-fill: #919191;
-fx-background-color: #303030; -fx-background-color: #303030;
@ -170,6 +179,22 @@
-fx-background-color: #a2a2a2; -fx-background-color: #a2a2a2;
} }
/* History tab */
#historyPane, #historyTab {
-fx-background-color: #404040;
}
#historySearchField {
-fx-background-color: #505050;
-fx-text-fill: white;
}
/* History item */
#historyItemBox {
-fx-background-color: #353535;
}
/* SnackBar */
.jfx-snackbar-content { .jfx-snackbar-content {
-fx-background-color: black; -fx-background-color: black;
} }

View file

@ -16,26 +16,37 @@
~ limitations under the License. ~ limitations under the License.
--> -->
<?import com.jfoenix.controls.*?> <?import com.jfoenix.controls.JFXButton?>
<?import javafx.geometry.*?> <?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?> <?import javafx.scene.control.ComboBox?>
<?import javafx.scene.image.*?> <?import javafx.scene.control.Label?>
<?import javafx.scene.layout.*?> <?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.text.*?> <?import javafx.scene.control.SplitPane?>
<StackPane fx:id="dashboard" stylesheets="@../../css/Adreana.css" xmlns="http://javafx.com/javafx/8.0.111" <?import javafx.scene.control.Tab?>
xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.rohitawate.restaurant.homewindow.DashboardController"> <?import javafx.scene.control.TabPane?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.control.Tooltip?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<StackPane fx:id="dashboard" stylesheets="@../../css/Adreana.css" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.rohitawate.restaurant.homewindow.DashboardController">
<children> <children>
<VBox prefHeight="200.0" prefWidth="100.0"> <VBox prefHeight="200.0" prefWidth="100.0">
<children> <children>
<HBox alignment="CENTER" maxHeight="100.0" VBox.vgrow="ALWAYS"> <HBox alignment="CENTER" maxHeight="100.0" VBox.vgrow="ALWAYS">
<children> <children>
<ImageView fitHeight="80.0" fitWidth="80.0" pickOnBounds="true" preserveRatio="true" <ImageView fitHeight="80.0" fitWidth="80.0" pickOnBounds="true" preserveRatio="true" HBox.hgrow="ALWAYS">
HBox.hgrow="ALWAYS">
<image> <image>
<Image url="@../../assets/LogoWithoutText.png"/> <Image url="@../../assets/LogoWithoutText.png" />
</image> </image>
<HBox.margin> <HBox.margin>
<Insets right="20.0"/> <Insets right="20.0" />
</HBox.margin> </HBox.margin>
</ImageView> </ImageView>
<TextField fx:id="addressField" maxWidth="800.0" promptText="URL" HBox.hgrow="ALWAYS"> <TextField fx:id="addressField" maxWidth="800.0" promptText="URL" HBox.hgrow="ALWAYS">
@ -46,13 +57,12 @@
<Font size="18.0" /> <Font size="18.0" />
</font> </font>
</TextField> </TextField>
<ComboBox fx:id="httpMethodBox" prefHeight="39.0" prefWidth="150.0" HBox.hgrow="ALWAYS"> <ComboBox fx:id="httpMethodBox" prefHeight="33.0" prefWidth="150.0" HBox.hgrow="ALWAYS">
<HBox.margin> <HBox.margin>
<Insets right="20.0" /> <Insets right="20.0" />
</HBox.margin> </HBox.margin>
</ComboBox> </ComboBox>
<JFXButton fx:id="sendButton" buttonType="RAISED" defaultButton="true" onAction="#sendRequest" <JFXButton fx:id="sendButton" buttonType="RAISED" defaultButton="true" minWidth="110.0" onAction="#sendRequest" prefHeight="39.0" ripplerFill="WHITE" text=" SEND" textAlignment="CENTER" textFill="WHITE" HBox.hgrow="ALWAYS">
prefHeight="39.0" prefWidth="100.0" text=" SEND" textFill="WHITE" HBox.hgrow="ALWAYS">
<padding> <padding>
<Insets bottom="5.0" left="15.0" right="15.0" top="5.0" /> <Insets bottom="5.0" left="15.0" right="15.0" top="5.0" />
</padding> </padding>
@ -65,7 +75,7 @@
<graphic> <graphic>
<ImageView fitHeight="15.0" fitWidth="15.0" pickOnBounds="true" preserveRatio="true"> <ImageView fitHeight="15.0" fitWidth="15.0" pickOnBounds="true" preserveRatio="true">
<image> <image>
<Image url="@../../assets/Send.png"/> <Image url="@../../assets/Send.png" />
</image> </image>
</ImageView> </ImageView>
</graphic> </graphic>
@ -76,210 +86,177 @@
<items> <items>
<AnchorPane maxHeight="250.0"> <AnchorPane maxHeight="250.0">
<children> <children>
<TabPane fx:id="requestOptionsTab" maxHeight="200.0" minHeight="200.0" <TabPane fx:id="requestOptionsTab" maxHeight="200.0" minHeight="200.0" tabClosingPolicy="UNAVAILABLE" tabMinWidth="150.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
tabClosingPolicy="UNAVAILABLE" tabMinWidth="150.0" AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0"
AnchorPane.topAnchor="0.0">
<tabs> <tabs>
<Tab fx:id="paramsTab" text="PARAMS"> <Tab fx:id="paramsTab" text="PARAMS">
<content> <content>
<VBox> <VBox>
<children> <children>
<HBox alignment="CENTER" maxHeight="0.0" spacing="20.0" <HBox alignment="CENTER" maxHeight="0.0" spacing="20.0" VBox.vgrow="ALWAYS">
VBox.vgrow="ALWAYS">
<VBox.margin> <VBox.margin>
<Insets/> <Insets />
</VBox.margin> </VBox.margin>
<children> <children>
<JFXButton fx:id="newParamButton" onAction="#addParamField" <JFXButton fx:id="newParamButton" onAction="#addParamField" text=" NEW PARAM" textFill="WHITE" HBox.hgrow="ALWAYS">
text=" NEW PARAM" textFill="WHITE"
HBox.hgrow="ALWAYS">
<graphic> <graphic>
<ImageView fitHeight="15.0" fitWidth="15.0" <ImageView fitHeight="15.0" fitWidth="15.0" pickOnBounds="true" preserveRatio="true">
pickOnBounds="true"
preserveRatio="true">
<image> <image>
<Image url="@../../assets/Plus.png"/> <Image url="@../../assets/Plus.png" />
</image> </image>
</ImageView> </ImageView>
</graphic> </graphic>
<HBox.margin> <HBox.margin>
<Insets/> <Insets />
</HBox.margin> </HBox.margin>
</JFXButton> </JFXButton>
<JFXButton fx:id="appendParamsButton" <JFXButton fx:id="appendParamsButton" onAction="#appendParams" ripplerFill="WHITE" text=" APPEND PARAMS" textFill="WHITE" HBox.hgrow="ALWAYS">
onAction="#appendParams" ripplerFill="WHITE"
text=" APPEND PARAMS" textFill="WHITE"
HBox.hgrow="ALWAYS">
<graphic> <graphic>
<ImageView fitHeight="15.0" fitWidth="15.0" <ImageView fitHeight="15.0" fitWidth="15.0" pickOnBounds="true" preserveRatio="true">
pickOnBounds="true"
preserveRatio="true">
<image> <image>
<Image url="@../../assets/CheckMark.png"/> <Image url="@../../assets/CheckMark.png" />
</image> </image>
</ImageView> </ImageView>
</graphic> </graphic>
<HBox.margin> <HBox.margin>
<Insets/> <Insets />
</HBox.margin> </HBox.margin>
</JFXButton> </JFXButton>
</children> </children>
<padding> <padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0"/> <Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
</padding> </padding>
</HBox> </HBox>
<ScrollPane fitToHeight="true" fitToWidth="true" hbarPolicy="NEVER" <ScrollPane fitToHeight="true" fitToWidth="true" hbarPolicy="NEVER" VBox.vgrow="ALWAYS">
VBox.vgrow="ALWAYS">
<content> <content>
<VBox fx:id="paramsBox" alignment="TOP_CENTER"/> <VBox fx:id="paramsBox" alignment="TOP_CENTER" />
</content> </content>
</ScrollPane> </ScrollPane>
</children> </children>
</VBox> </VBox>
</content> </content>
</Tab> </Tab>
<Tab fx:id="authTab" text="AUTHORIZATION"/> <Tab fx:id="authTab" text="AUTHORIZATION" />
<Tab fx:id="headersTab" text="HEADERS"/> <Tab fx:id="headersTab" text="HEADERS" />
<Tab fx:id="bodyTab" text="BODY"/> <Tab fx:id="bodyTab" text="BODY" />
</tabs> </tabs>
</TabPane> </TabPane>
</children> </children>
</AnchorPane> </AnchorPane>
<AnchorPane> <AnchorPane>
<children> <children>
<VBox fx:id="responseBox" alignment="CENTER" AnchorPane.bottomAnchor="0.0" <VBox fx:id="responseBox" alignment="CENTER" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children> <children>
<HBox fx:id="responseDetails" alignment="CENTER_RIGHT" maxHeight="50.0" <HBox fx:id="responseDetails" alignment="CENTER_RIGHT" maxHeight="50.0" minHeight="50.0" VBox.vgrow="ALWAYS">
minHeight="50.0" VBox.vgrow="ALWAYS">
<children> <children>
<HBox alignment="CENTER_LEFT" HBox.hgrow="ALWAYS"> <HBox alignment="CENTER_LEFT" HBox.hgrow="ALWAYS">
<children> <children>
<Label fx:id="statusCode" text="404" textFill="WHITE" <Label fx:id="statusCode" text="404" textFill="WHITE" HBox.hgrow="ALWAYS">
HBox.hgrow="ALWAYS">
<font> <font>
<Font name="System Bold" size="35.0"/> <Font name="System Bold" size="35.0" />
</font> </font>
<HBox.margin> <HBox.margin>
<Insets right="10.0"/> <Insets right="10.0" />
</HBox.margin> </HBox.margin>
</Label> </Label>
<Label fx:id="statusCodeDescription" text="Not Found" <Label fx:id="statusCodeDescription" text="Not Found" textFill="WHITE" HBox.hgrow="ALWAYS">
textFill="WHITE" HBox.hgrow="ALWAYS">
<font> <font>
<Font size="30.0"/> <Font size="30.0" />
</font> </font>
</Label> </Label>
</children> </children>
</HBox> </HBox>
<Label fx:id="responseTime" text="151 ms" textFill="WHITE" <Label fx:id="responseTime" text="151 ms" textFill="WHITE" HBox.hgrow="ALWAYS">
HBox.hgrow="ALWAYS">
<HBox.margin> <HBox.margin>
<Insets right="30.0"/> <Insets right="30.0" />
</HBox.margin> </HBox.margin>
<font> <font>
<Font name="Liberation Mono" size="20.0"/> <Font name="Liberation Mono" size="20.0" />
</font> </font>
</Label> </Label>
<Label fx:id="responseSize" layoutX="1187.0" layoutY="23.0" text="1998 B" <Label fx:id="responseSize" layoutX="1187.0" layoutY="23.0" text="1998 B" textFill="WHITE" HBox.hgrow="ALWAYS">
textFill="WHITE" HBox.hgrow="ALWAYS">
<font> <font>
<Font name="Liberation Mono" size="20.0"/> <Font name="Liberation Mono" size="20.0" />
</font> </font>
<HBox.margin> <HBox.margin>
<Insets right="30.0"/> <Insets right="30.0" />
</HBox.margin> </HBox.margin>
</Label> </Label>
<JFXButton fx:id="clearResponseAreaButton" buttonType="RAISED" <JFXButton fx:id="clearResponseAreaButton" buttonType="RAISED" onAction="#clearResponseArea" ripplerFill="WHITE" text=" CLEAR" textFill="WHITE" HBox.hgrow="ALWAYS">
onAction="#clearResponseArea" ripplerFill="WHITE" text=" CLEAR"
textFill="WHITE" HBox.hgrow="ALWAYS">
<graphic> <graphic>
<ImageView fitHeight="15.0" fitWidth="15.0" pickOnBounds="true" <ImageView fitHeight="15.0" fitWidth="15.0" pickOnBounds="true" preserveRatio="true">
preserveRatio="true">
<image> <image>
<Image url="@../../assets/CrossMark.png"/> <Image url="@../../assets/CrossMark.png" />
</image> </image>
</ImageView> </ImageView>
</graphic> </graphic>
<tooltip> <tooltip>
<Tooltip autoHide="true" <Tooltip autoHide="true" text="Clears this bar and the response body below." />
text="Clears this bar and the response body below."/>
</tooltip> </tooltip>
</JFXButton> </JFXButton>
</children> </children>
<padding> <padding>
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0"/> <Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
</padding> </padding>
</HBox> </HBox>
<StackPane VBox.vgrow="ALWAYS"> <StackPane VBox.vgrow="ALWAYS">
<children> <children>
<TextArea fx:id="responseArea" editable="false" minHeight="300.0" <TextArea fx:id="responseArea" editable="false" minHeight="300.0" wrapText="true" />
wrapText="true"/>
<VBox fx:id="loadingLayer" alignment="CENTER" visible="false"> <VBox fx:id="loadingLayer" alignment="CENTER" visible="false">
<children> <children>
<HBox alignment="CENTER"> <HBox alignment="CENTER">
<children> <children>
<ImageView fitHeight="150.0" fitWidth="100.0" <ImageView fitHeight="150.0" fitWidth="100.0" pickOnBounds="true" preserveRatio="true">
pickOnBounds="true" preserveRatio="true">
<image> <image>
<Image url="@../../assets/LoadingCircle_WhiteOnOrange.gif"/> <Image url="@../../assets/LoadingCircle_WhiteOnOrange.gif" />
</image> </image>
</ImageView> </ImageView>
<Label text="LOADING" textFill="WHITE"> <Label text="LOADING" textFill="WHITE">
<font> <font>
<Font size="70.0"/> <Font size="70.0" />
</font> </font>
</Label> </Label>
</children> </children>
</HBox> </HBox>
<JFXButton fx:id="cancelButton" text=" CANCEL" textFill="WHITE" <JFXButton fx:id="cancelButton" text=" CANCEL" textFill="WHITE" VBox.vgrow="ALWAYS">
VBox.vgrow="ALWAYS">
<graphic> <graphic>
<ImageView fitHeight="15.0" fitWidth="15.0" <ImageView fitHeight="15.0" fitWidth="15.0" pickOnBounds="true" preserveRatio="true">
pickOnBounds="true" preserveRatio="true">
<image> <image>
<Image url="@../../assets/CrossMark.png"/> <Image url="@../../assets/CrossMark.png" />
</image> </image>
</ImageView> </ImageView>
</graphic> </graphic>
</JFXButton> </JFXButton>
</children> </children>
</VBox> </VBox>
<VBox fx:id="promptLayer" alignment="CENTER" prefHeight="200.0" <VBox fx:id="promptLayer" alignment="CENTER" prefHeight="200.0" prefWidth="100.0" visible="false">
prefWidth="100.0" visible="false">
<children> <children>
<Label text="Enter an address, select a method and hit send. 🚀" <Label text="Enter an address, select a method and hit send. 🚀" textFill="WHITE">
textFill="WHITE">
<font> <font>
<Font size="32.0"/> <Font size="32.0" />
</font> </font>
</Label> </Label>
<Label text="It's not rocket science." textFill="#9e9e9e"> <Label text="It's not rocket science." textFill="#9e9e9e">
<font> <font>
<Font name="System Italic" size="27.0"/> <Font name="System Italic" size="27.0" />
</font> </font>
</Label> </Label>
</children> </children>
</VBox> </VBox>
<VBox fx:id="errorLayer" alignment="CENTER" layoutX="10.0" layoutY="10.0" <VBox fx:id="errorLayer" alignment="CENTER" layoutX="10.0" layoutY="10.0" prefHeight="200.0" prefWidth="100.0" visible="false">
prefHeight="200.0" prefWidth="100.0" visible="false">
<children> <children>
<ImageView fitHeight="100.0" fitWidth="100.0" opacity="0.75" <ImageView fitHeight="100.0" fitWidth="100.0" opacity="0.75" pickOnBounds="true" preserveRatio="true">
pickOnBounds="true" preserveRatio="true">
<image> <image>
<Image url="@../../assets/Explosion.png"/> <Image url="@../../assets/Explosion.png" />
</image> </image>
</ImageView> </ImageView>
<Label fx:id="errorTitle" text="Error title" textFill="WHITE"> <Label fx:id="errorTitle" text="Error title" textFill="WHITE">
<font> <font>
<Font name="System Bold" size="32.0"/> <Font name="System Bold" size="32.0" />
</font> </font>
</Label> </Label>
<Label fx:id="errorDetails" text="Error details" <Label fx:id="errorDetails" text="Error details" textAlignment="CENTER" textFill="#c3c3c3">
textAlignment="CENTER" textFill="#c3c3c3">
<font> <font>
<Font size="22.0"/> <Font size="22.0" />
</font> </font>
</Label> </Label>
</children> </children>

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.text.Font?>
<HBox fx:id="historyItemBox" alignment="CENTER_LEFT" maxHeight="40.0" minHeight="40.0" minWidth="300.0" spacing="20.0" stylesheets="@../../css/Adreana.css" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.rohitawate.restaurant.homewindow.HistoryItemController">
<children>
<StackPane maxWidth="80.0" minWidth="80.0" HBox.hgrow="ALWAYS">
<children>
<Label fx:id="requestType" textFill="ORANGERED" textOverrun="WORD_ELLIPSIS">
<font>
<Font name="System Bold" size="18.0" />
</font>
</Label>
</children>
</StackPane>
<Label fx:id="address" textFill="WHITE" HBox.hgrow="ALWAYS">
<font>
<Font size="15.0" />
</font>
</Label>
</children>
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>
</HBox>

View file

@ -16,9 +16,54 @@
~ limitations under the License. ~ limitations under the License.
--> -->
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TabPane?> <?import javafx.scene.control.TabPane?>
<TabPane fx:id="homeWindowTabPane" prefHeight="760.0" prefWidth="1280.0" stylesheets="@../../css/Adreana.css" <?import javafx.scene.control.TextField?>
tabClosingPolicy="ALL_TABS" tabMaxHeight="35.0" tabMaxWidth="200.0" tabMinHeight="35.0" tabMinWidth="200.0" <?import javafx.scene.layout.StackPane?>
xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" <?import javafx.scene.layout.VBox?>
fx:controller="com.rohitawate.restaurant.homewindow.HomeWindowController"/> <?import javafx.scene.text.Font?>
<SplitPane dividerPositions="0.3" stylesheets="@../../css/Adreana.css" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.rohitawate.restaurant.homewindow.HomeWindowController">
<items>
<VBox fx:id="historyPane" alignment="TOP_CENTER" maxWidth="500.0" minWidth="500.0" spacing="10.0" SplitPane.resizableWithParent="false">
<children>
<TextField fx:id="historySearchField" promptText="SEARCH" VBox.vgrow="ALWAYS">
<VBox.margin>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</VBox.margin>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding></TextField>
<ScrollPane fitToHeight="true" fitToWidth="true" VBox.vgrow="ALWAYS">
<content>
<StackPane>
<children>
<VBox fx:id="historyTab" alignment="TOP_CENTER" spacing="5.0">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" />
</padding>
</VBox>
<StackPane fx:id="historyPromptLayer">
<children>
<Label text="YOUR REQUESTS HISTORY WILL APPEAR HERE" textAlignment="CENTER" textFill="#575757" wrapText="true">
<font>
<Font size="25.0" />
</font>
</Label>
</children>
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
</padding>
</StackPane>
</children>
</StackPane>
</content>
</ScrollPane>
</children>
</VBox>
<TabPane fx:id="homeWindowTabPane" prefHeight="760.0" prefWidth="1280.0" tabClosingPolicy="ALL_TABS" tabMaxHeight="30.0" tabMaxWidth="200.0" tabMinHeight="30.0" tabMinWidth="200.0" SplitPane.resizableWithParent="false" />
</items>
</SplitPane>

View file

@ -1,7 +1,17 @@
{ {
"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(ID INTEGER, Key TEXT NOT NULL, Value TEXT NOT NULL, FOREIGN KEY(ID) REFERENCES Requests(ID))", "createHeadersTable": "CREATE TABLE IF NOT EXISTS Headers(RequestID INTEGER, Key TEXT NOT NULL, Value TEXT NOT NULL, FOREIGN KEY(RequestID) REFERENCES Requests(ID))",
"selectAllRequests": "SELECT * FROM Requests, Headers", "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))",
"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))",
"saveRequest": "INSERT INTO Requests(Type, Target, Date) VALUES(?, ?, ?)", "saveRequest": "INSERT INTO Requests(Type, Target, Date) VALUES(?, ?, ?)",
"saveHeader": "INSERT INTO Headers(ID, Key, Value) VALUES(?, ?, ?)" "saveHeader": "INSERT INTO Headers(RequestID, Key, Value) VALUES(?, ?, ?)",
"saveRequestContentPair": "INSERT INTO RequestContentMap(ID, ContentType) VALUES(?, ?)",
"saveBody": "INSERT INTO Bodies(RequestID, Body) VALUES(?, ?)",
"saveTuple": "INSERT INTO Tuples(RequestID, TupleType, Key, Value) VALUES(?, ?, ?, ?)",
"selectRecentRequests": "SELECT * FROM Requests WHERE Requests.Date > ?",
"selectRequestHeaders": "SELECT * FROM Headers WHERE RequestID == ?",
"selectRequestContentType": "SELECT ContentType FROM RequestContentMap WHERE RequestID == ?",
"selectRequestBody": "SELECT Body FROM Bodies WHERE RequestID == ?",
"selectTuples": "SELECT * FROM Tuples WHERE RequestID == ? AND TupleType == ?"
} }