Added Response Visualizer
This commit is contained in:
parent
35a21f0df0
commit
09ea27f699
7 changed files with 181 additions and 14 deletions
|
@ -40,6 +40,7 @@ import javafx.event.ActionEvent;
|
|||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.CacheHint;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.input.KeyCode;
|
||||
|
@ -55,6 +56,7 @@ import java.net.MalformedURLException;
|
|||
import java.net.URL;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class DashboardController implements Initializable {
|
||||
@FXML
|
||||
|
@ -78,6 +80,10 @@ public class DashboardController implements Initializable {
|
|||
TabPane requestOptionsTab;
|
||||
@FXML
|
||||
Tab paramsTab, authTab, headersTab, bodyTab;
|
||||
@FXML
|
||||
private Tab visualizerTab;
|
||||
@FXML
|
||||
private ScrollPane visualizer;
|
||||
|
||||
private JFXSnackbar snackbar;
|
||||
private final String[] httpMethods = {"GET", "POST", "PUT", "DELETE", "PATCH"};
|
||||
|
@ -87,6 +93,7 @@ public class DashboardController implements Initializable {
|
|||
private HeaderTabController headerTabController;
|
||||
private BodyTabController bodyTabController;
|
||||
private IntegerProperty paramsCountProperty;
|
||||
private Accordion accordion;
|
||||
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle rb) {
|
||||
|
@ -138,6 +145,8 @@ public class DashboardController implements Initializable {
|
|||
|
||||
errorTitle.setText("Oops... That's embarrassing!");
|
||||
errorDetails.setText("Something went wrong. Try to make another request.\nRestart Everest if that doesn't work.");
|
||||
|
||||
setupVisualizer();
|
||||
}
|
||||
|
||||
@FXML
|
||||
|
@ -320,11 +329,15 @@ public class DashboardController implements Initializable {
|
|||
// Selects only the part preceding the ';', skipping the character encoding
|
||||
type = type.split(";")[0];
|
||||
|
||||
visualizerTab.setDisable(true);
|
||||
switch (type.toLowerCase()) {
|
||||
case "application/json":
|
||||
responseType.setText("JSON");
|
||||
JsonNode node = EverestUtilities.mapper.readTree(responseBody);
|
||||
responseArea.setText(EverestUtilities.mapper.writeValueAsString(node));
|
||||
accordion.getPanes().clear();
|
||||
visualizerTab.setDisable(false);
|
||||
populateVisualizer(accordion, "root", node);
|
||||
break;
|
||||
case "application/xml":
|
||||
responseType.setText("XML");
|
||||
|
@ -351,6 +364,84 @@ public class DashboardController implements Initializable {
|
|||
}
|
||||
}
|
||||
|
||||
private void setupVisualizer() {
|
||||
accordion = new Accordion();
|
||||
accordion.setCache(true);
|
||||
accordion.setCacheHint(CacheHint.SPEED);
|
||||
visualizer.setContent(accordion);
|
||||
}
|
||||
|
||||
private void populateVisualizer(Accordion rootAccordion, String rootName, JsonNode root) {
|
||||
JsonNode currentNode;
|
||||
VBox container = new VBox();
|
||||
container.setStyle("-fx-padding: 3px 30px");
|
||||
Label valueLabel;
|
||||
TitledPane pane = new TitledPane(rootName, container);
|
||||
Tooltip valueTooltip;
|
||||
|
||||
if (root.isArray()) {
|
||||
Iterator<JsonNode> iterator = root.elements();
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
currentNode = iterator.next();
|
||||
|
||||
if (currentNode.isValueNode()) {
|
||||
valueLabel = new Label(currentNode.toString());
|
||||
valueLabel.getStyleClass().add("visualizerValueLabel");
|
||||
valueLabel.setWrapText(true);
|
||||
valueTooltip = new Tooltip(currentNode.toString());
|
||||
valueLabel.setTooltip(valueTooltip);
|
||||
|
||||
container.getChildren().add(valueLabel);
|
||||
} else if (currentNode.isObject()) {
|
||||
Accordion arrayAccordion = new Accordion();
|
||||
container.getChildren().add(arrayAccordion);
|
||||
populateVisualizer(arrayAccordion, "", currentNode);
|
||||
}
|
||||
}
|
||||
rootAccordion.getPanes().add(pane);
|
||||
} else {
|
||||
Iterator<Entry<String, JsonNode>> iterator = root.fields();
|
||||
Entry<String, JsonNode> currentEntry;
|
||||
HBox valueContainer;
|
||||
Label keyLabel;
|
||||
Tooltip keyTooltip;
|
||||
|
||||
while (iterator.hasNext()) {
|
||||
currentEntry = iterator.next();
|
||||
currentNode = currentEntry.getValue();
|
||||
|
||||
if (currentNode.isValueNode()) {
|
||||
keyLabel = new Label(currentEntry.getKey() + ": ");
|
||||
keyLabel.setStyle("-fx-font-weight: bold");
|
||||
keyLabel.getStyleClass().add("visualizerKeyLabel");
|
||||
keyTooltip = new Tooltip(currentEntry.getKey());
|
||||
keyLabel.setTooltip(keyTooltip);
|
||||
|
||||
valueLabel = new Label(currentNode.toString());
|
||||
valueLabel.getStyleClass().add("visualizerValueLabel");
|
||||
valueLabel.setWrapText(true);
|
||||
valueTooltip = new Tooltip(currentNode.toString());
|
||||
valueLabel.setTooltip(valueTooltip);
|
||||
|
||||
valueContainer = new HBox(keyLabel, valueLabel);
|
||||
container.getChildren().add(valueContainer);
|
||||
} else if (currentNode.isArray() || currentNode.isObject()) {
|
||||
Accordion arrayAccordion = new Accordion();
|
||||
container.getChildren().add(arrayAccordion);
|
||||
populateVisualizer(arrayAccordion, currentEntry.getKey(), currentNode);
|
||||
}
|
||||
}
|
||||
rootAccordion.getPanes().add(pane);
|
||||
}
|
||||
|
||||
if (!rootName.equals("root")) {
|
||||
pane.getStyleClass().add("nonRootTitledPane"); // Special CSS class to set padding for non-root panes only
|
||||
} else {
|
||||
rootAccordion.setExpandedPane(pane);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyDashboardSettings() {
|
||||
String responseAreaCSS = "-fx-font-family: " + Settings.responseAreaFont + ";" +
|
||||
"-fx-font-size: " + Settings.responseAreaFontSize;
|
||||
|
@ -482,11 +573,11 @@ public class DashboardController implements Initializable {
|
|||
httpMethodBox.getSelectionModel().select(dashboardState.getHttpMethod());
|
||||
|
||||
if (dashboardState.getHeaders() != null)
|
||||
for (Map.Entry entry : dashboardState.getHeaders().entrySet())
|
||||
for (Entry entry : dashboardState.getHeaders().entrySet())
|
||||
headerTabController.addHeader(entry.getKey().toString(), entry.getValue().toString());
|
||||
|
||||
if (dashboardState.getParams() != null)
|
||||
for (Map.Entry entry : dashboardState.getParams().entrySet())
|
||||
for (Entry entry : dashboardState.getParams().entrySet())
|
||||
addParamField(entry.getKey().toString(), entry.getValue().toString());
|
||||
|
||||
if (!(httpMethodBox.getValue().equals("GET") || httpMethodBox.getValue().equals("DELETE")))
|
||||
|
|
|
@ -260,7 +260,7 @@ public class HomeWindowController implements Initializable {
|
|||
objectStream.writeObject(dashboardStates);
|
||||
objectStream.close();
|
||||
fileStream.close();
|
||||
Services.loggingService.logInfo("Application state saved successfully.", LocalDateTime.now());
|
||||
Services.loggingService.logInfo("Application state was saved successfully.", LocalDateTime.now());
|
||||
} catch (IOException e) {
|
||||
Services.loggingService.logSevere("Failed to save the application's state.", e, LocalDateTime.now());
|
||||
}
|
||||
|
@ -338,7 +338,7 @@ public class HomeWindowController implements Initializable {
|
|||
|
||||
for (HistoryItemController controller : historyItemControllers) {
|
||||
|
||||
int relativityIndex = 0;
|
||||
int relativityIndex = controller.getRelativityIndex(searchString);
|
||||
|
||||
// Split the string into words and get total relativity index as sum of individual indices.
|
||||
String words[] = searchString.split("\\s");
|
||||
|
|
|
@ -213,7 +213,7 @@ public class HistoryManager {
|
|||
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
|
||||
// Loads the requests from the last x number of days, x being Settings.showHistoryRange
|
||||
statement = conn.prepareStatement(EverestUtilities.trimString(queries.get("selectRecentRequests").toString()));
|
||||
String historyStartDate = LocalDate.now().minusDays(Settings.showHistoryRange).toString();
|
||||
statement.setString(1, historyStartDate);
|
||||
|
|
|
@ -31,5 +31,5 @@ public class Settings {
|
|||
public static int connectionReadTimeOut = 30000;
|
||||
|
||||
public static String theme = "Adreana";
|
||||
public static long showHistoryRange = 7;
|
||||
public static int showHistoryRange = 3;
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ public class SettingsLoader implements Runnable {
|
|||
Settings.connectionReadTimeOut = setIntegerSetting(Settings.connectionReadTimeOut, "connectionReadTimeOut");
|
||||
|
||||
Settings.theme = EverestUtilities.trimString(setStringSetting(Settings.theme, "theme"));
|
||||
Settings.showHistoryRange = setIntegerSetting(Settings.showHistoryRange, "showHistoryRange");
|
||||
} catch (IOException IOE) {
|
||||
Services.loggingService.logInfo("Settings file not found. Using defaults", LocalDateTime.now());
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
}
|
||||
|
||||
.text-area {
|
||||
-fx-font-family: "Liberation Mono";
|
||||
-fx-font-family: "Liberation Mono", monospace;
|
||||
-fx-text-fill: #a1a1a1;
|
||||
-fx-background-radius: 0;
|
||||
-fx-background-insets: 0;
|
||||
|
@ -118,7 +118,7 @@
|
|||
-fx-background-color: #505050;
|
||||
}
|
||||
|
||||
.tab-pane:top .tab-header-area {
|
||||
.tab-pane .tab-header-area {
|
||||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
|
@ -187,7 +187,6 @@
|
|||
-fx-background-color: #3d3d3d;
|
||||
}
|
||||
|
||||
|
||||
.scroll-pane .scroll-bar .thumb {
|
||||
-fx-background-color: #808080;
|
||||
}
|
||||
|
@ -290,6 +289,66 @@
|
|||
-fx-font-size: 15px;
|
||||
}
|
||||
|
||||
/* Response Visualizer */
|
||||
#responseTabPane:bottom .tab-header-area .tab-header-background,
|
||||
#responseTabPane:bottom .tab-header-area .headers-region .tab:bottom {
|
||||
-fx-background-color: #282828;
|
||||
}
|
||||
|
||||
#responseTabPane:bottom .tab-header-area .headers-region .tab:bottom .tab-container .tab-label .text {
|
||||
-fx-fill: white;
|
||||
}
|
||||
|
||||
#responseTabPane:bottom .tab-header-area .headers-region .tab:hover:bottom {
|
||||
-fx-background-color: purple;
|
||||
}
|
||||
|
||||
#responseTabPane:bottom .tab-header-area .headers-region .tab:selected:bottom {
|
||||
-fx-background-color: #3d3d3d;
|
||||
}
|
||||
|
||||
#responseTabPane .tab-content-area,
|
||||
#responseTabPane .scroll-pane .viewport {
|
||||
-fx-background-color: #2a2a2a;
|
||||
}
|
||||
|
||||
.nonRootTitledPane {
|
||||
-fx-padding: 3px 0px;
|
||||
}
|
||||
|
||||
.titled-pane .title {
|
||||
-fx-font-family: "Liberation Mono", monospace;
|
||||
-fx-background-color: #404040;
|
||||
}
|
||||
|
||||
.titled-pane .content {
|
||||
-fx-background-color: #282828;
|
||||
-fx-border-width: 0px;
|
||||
}
|
||||
|
||||
.titled-pane .content {
|
||||
-fx-font-family: "Liberation Mono", monospace;
|
||||
-fx-padding: 3px 0px;
|
||||
}
|
||||
|
||||
.visualizerKeyLabel {
|
||||
-fx-font-size: 18px;
|
||||
-fx-text-fill: #bababa;
|
||||
-fx-text-alignment: left;
|
||||
}
|
||||
|
||||
.visualizerValueLabel {
|
||||
-fx-font-size: 18px;
|
||||
-fx-text-fill: #959595;
|
||||
-fx-text-alignment: left;
|
||||
}
|
||||
|
||||
.titled-pane .title .text,
|
||||
.titled-pane .title .arrow-button .arrow {
|
||||
-fx-background-color: #dadada;
|
||||
-fx-fill: #dadada;
|
||||
}
|
||||
|
||||
/* SnackBar */
|
||||
.jfx-snackbar-content {
|
||||
-fx-background-color: black;
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<children>
|
||||
<VBox prefHeight="200.0" prefWidth="100.0">
|
||||
<children>
|
||||
<HBox alignment="CENTER" maxHeight="100.0" prefWidth="100.0" spacing="20.0" VBox.vgrow="ALWAYS">
|
||||
<HBox alignment="CENTER" maxHeight="100.0" minHeight="100.0" spacing="20.0" VBox.vgrow="ALWAYS">
|
||||
<children>
|
||||
<ImageView fitHeight="50.0" fitWidth="50.0" pickOnBounds="true" preserveRatio="true"
|
||||
HBox.hgrow="ALWAYS">
|
||||
|
@ -76,7 +76,7 @@
|
|||
</HBox>
|
||||
<SplitPane dividerPositions="0.1" orientation="VERTICAL" VBox.vgrow="ALWAYS">
|
||||
<items>
|
||||
<AnchorPane maxHeight="250.0">
|
||||
<AnchorPane maxHeight="300.0">
|
||||
<children>
|
||||
<TabPane fx:id="requestOptionsTab" minHeight="190.0" tabClosingPolicy="UNAVAILABLE"
|
||||
tabMinWidth="150.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0"
|
||||
|
@ -198,9 +198,25 @@
|
|||
<Insets bottom="15.0" left="15.0" right="15.0" top="15.0" />
|
||||
</padding>
|
||||
</HBox>
|
||||
<StackPane VBox.vgrow="ALWAYS">
|
||||
<StackPane VBox.vgrow="SOMETIMES">
|
||||
<children>
|
||||
<TextArea fx:id="responseArea" editable="false" wrapText="true"/>
|
||||
<TabPane fx:id="responseTabPane" side="BOTTOM" tabMinWidth="100.0">
|
||||
<tabs>
|
||||
<Tab closable="false" text="BODY">
|
||||
<content>
|
||||
<TextArea fx:id="responseArea" editable="false"
|
||||
wrapText="true"/>
|
||||
</content>
|
||||
</Tab>
|
||||
<Tab fx:id="visualizerTab" closable="false" text="VISUALIZER">
|
||||
<content>
|
||||
<ScrollPane fx:id="visualizer" fitToHeight="true"
|
||||
fitToWidth="true" hbarPolicy="NEVER"/>
|
||||
</content>
|
||||
</Tab>
|
||||
<Tab closable="false" text="HEADERS"/>
|
||||
</tabs>
|
||||
</TabPane>
|
||||
<VBox fx:id="loadingLayer" alignment="CENTER" visible="false">
|
||||
<children>
|
||||
<HBox alignment="CENTER">
|
||||
|
@ -228,7 +244,7 @@
|
|||
</JFXButton>
|
||||
</children>
|
||||
</VBox>
|
||||
<VBox fx:id="promptLayer" alignment="CENTER">
|
||||
<VBox fx:id="promptLayer" alignment="CENTER" visible="false">
|
||||
<children>
|
||||
<Label text="Enter an address, select a method and hit send."
|
||||
textFill="WHITE">
|
||||
|
|
Loading…
Reference in a new issue