Fix issues with detecting duplicate ComposerStates while saving history

This commit is contained in:
Rohit Awate 2018-08-18 22:27:11 +05:30
parent 53a42f8de5
commit 6eb4595f91
No known key found for this signature in database
GPG key ID: 1051D7B79CF2EE25
8 changed files with 120 additions and 96 deletions

View file

@ -100,6 +100,7 @@ public class DashboardController implements Initializable {
private IntegerProperty paramsCountProperty; private IntegerProperty paramsCountProperty;
private Visualizer visualizer; private Visualizer visualizer;
private ResponseHeadersViewer responseHeadersViewer; private ResponseHeadersViewer responseHeadersViewer;
private SyncManager syncManager;
private GETRequest getRequest; private GETRequest getRequest;
private DataRequest dataRequest; private DataRequest dataRequest;
@ -293,7 +294,7 @@ public class DashboardController implements Initializable {
cancelButton.setOnAction(e -> requestManager.cancel()); cancelButton.setOnAction(e -> requestManager.cancel());
requestManager.addHandlers(this::whileRunning, this::onSucceeded, this::onFailed, this::onCancelled); requestManager.addHandlers(this::whileRunning, this::onSucceeded, this::onFailed, this::onCancelled);
requestManager.start(); requestManager.start();
SyncManager.saveState(getState().composer); syncManager.saveState(getState().composer);
} catch (MalformedURLException MURLE) { } catch (MalformedURLException MURLE) {
showLayer(ResponseLayer.PROMPT); showLayer(ResponseLayer.PROMPT);
snackbar.show("Invalid address. Please verify and try again.", 3000); snackbar.show("Invalid address. Please verify and try again.", 3000);
@ -614,6 +615,10 @@ public class DashboardController implements Initializable {
} }
} }
public void setSyncManager(SyncManager syncManager) {
this.syncManager = syncManager;
}
public ComposerTab getVisibleComposerTab() { public ComposerTab getVisibleComposerTab() {
int visibleTab = requestOptionsTab.getSelectionModel().getSelectedIndex(); int visibleTab = requestOptionsTab.getSelectionModel().getSelectedIndex();
switch (visibleTab) { switch (visibleTab) {
@ -628,7 +633,6 @@ public class DashboardController implements Initializable {
} }
} }
private ResponseTab getVisibleResponseTab() { private ResponseTab getVisibleResponseTab() {
int visibleTab = responseTabPane.getSelectionModel().getSelectedIndex(); int visibleTab = responseTabPane.getSelectionModel().getSelectedIndex();
switch (visibleTab) { switch (visibleTab) {

View file

@ -129,7 +129,7 @@ public class HeaderTabController implements Initializable {
/** /**
* Return a list of the state of all the fields in the Headers tab. * Return a list of the state of all the fields in the Headers tab.
*/ */
public ArrayList<FieldState> getFieldStates() { public List<FieldState> getFieldStates() {
ArrayList<FieldState> states = new ArrayList<>(); ArrayList<FieldState> states = new ArrayList<>();
for (StringKeyValueFieldController controller : controllers) { for (StringKeyValueFieldController controller : controllers) {

View file

@ -29,12 +29,12 @@ import java.util.List;
import java.util.function.Consumer; import java.util.function.Consumer;
public class HistoryPaneController extends SearchablePaneController<ComposerState> { public class HistoryPaneController extends SearchablePaneController<ComposerState> {
private List<Consumer<ComposerState>> stateClickHandler = new LinkedList<>(); private List<Consumer<ComposerState>> stateClickHandler = new LinkedList<>();
private SyncManager syncManager;
@Override @Override
protected List<ComposerState> loadInitialEntries() { protected List<ComposerState> loadInitialEntries() {
return SyncManager.getHistory(); return syncManager.getHistory();
} }
protected SearchEntry<ComposerState> createEntryFromState(ComposerState state) throws IOException { protected SearchEntry<ComposerState> createEntryFromState(ComposerState state) throws IOException {
@ -62,4 +62,8 @@ public class HistoryPaneController extends SearchablePaneController<ComposerStat
public void addItemClickHandler(Consumer<ComposerState> handler) { public void addItemClickHandler(Consumer<ComposerState> handler) {
stateClickHandler.add(handler); stateClickHandler.add(handler);
} }
public void setSyncManager(SyncManager syncManager) {
this.syncManager = syncManager;
}
} }

View file

@ -61,21 +61,24 @@ public class HomeWindowController implements Initializable {
private HistoryPaneController historyController; private HistoryPaneController historyController;
private DashboardController dashboard; private DashboardController dashboard;
private StringProperty addressProperty; private StringProperty addressProperty;
private SyncManager syncManager;
@Override @Override
public void initialize(URL location, ResourceBundle resources) { public void initialize(URL location, ResourceBundle resources) {
new SyncManager(this); syncManager = new SyncManager(this);
try { try {
FXMLLoader historyLoader = new FXMLLoader(getClass().getResource("/fxml/homewindow/HistoryPane.fxml")); FXMLLoader historyLoader = new FXMLLoader(getClass().getResource("/fxml/homewindow/HistoryPane.fxml"));
Parent historyFXML = historyLoader.load(); Parent historyFXML = historyLoader.load();
splitPane.getItems().add(0, historyFXML); splitPane.getItems().add(0, historyFXML);
historyController = historyLoader.getController(); historyController = historyLoader.getController();
historyController.setSyncManager(syncManager);
historyController.addItemClickHandler(this::addTab); historyController.addItemClickHandler(this::addTab);
FXMLLoader dashboardLoader = new FXMLLoader(getClass().getResource("/fxml/homewindow/Dashboard.fxml")); FXMLLoader dashboardLoader = new FXMLLoader(getClass().getResource("/fxml/homewindow/Dashboard.fxml"));
Parent dashboardFXML = dashboardLoader.load(); Parent dashboardFXML = dashboardLoader.load();
dashboard = dashboardLoader.getController(); dashboard = dashboardLoader.getController();
dashboard.setSyncManager(syncManager);
dashboardContainer.getChildren().add(dashboardFXML); dashboardContainer.getChildren().add(dashboardFXML);
addressProperty = dashboard.addressField.textProperty(); addressProperty = dashboard.addressField.textProperty();
} catch (IOException e) { } catch (IOException e) {

View file

@ -57,12 +57,16 @@ public class ComposerState {
ComposerState state = (ComposerState) o; ComposerState state = (ComposerState) o;
if (!target.equals(state.target)) return false; if (!target.equals(state.target)) return false;
if (!httpMethod.equals(state.httpMethod)) return false; if (!httpMethod.equals(state.httpMethod)) return false;
if (!params.equals(state.params)) return false;
if (!headers.equals(state.headers)) return false;
if (state.httpMethod.equals(HTTPConstants.GET)
|| state.httpMethod.equals(HTTPConstants.DELETE)) return true;
if (!contentType.equals(state.contentType)) return false; if (!contentType.equals(state.contentType)) return false;
if (!rawBody.equals(state.rawBody)) return false; if (!rawBody.equals(state.rawBody)) return false;
if (!rawBodyBoxValue.equals(state.rawBodyBoxValue)) return false; if (!rawBodyBoxValue.equals(state.rawBodyBoxValue)) return false;
if (!binaryFilePath.equals(state.binaryFilePath)) return false; if (!binaryFilePath.equals(state.binaryFilePath)) return false;
if (!params.equals(state.params)) return false;
if (!headers.equals(state.headers)) return false;
if (!urlStringTuples.equals(state.urlStringTuples)) return false; if (!urlStringTuples.equals(state.urlStringTuples)) return false;
if (!formStringTuples.equals(state.formStringTuples)) return false; if (!formStringTuples.equals(state.formStringTuples)) return false;
if (!formFileTuples.equals(state.formFileTuples)) return false; if (!formFileTuples.equals(state.formFileTuples)) return false;

View file

@ -11,14 +11,21 @@ public interface DataManager {
/** /**
* Saves the state of the Composer when the request was made. * Saves the state of the Composer when the request was made.
*/ */
void saveState(ComposerState newState); void saveState(ComposerState newState) throws Exception;
/** /**
* Fetches all the states of the Composer when the previous requests were made. * Fetches all the states of the Composer when the previous requests were made.
* *
* @return A list of the states. * @return A list of the states.
*/ */
List<ComposerState> getHistory(); List<ComposerState> getHistory() throws Exception;
/**
* Returns the state of the Composer when the last request was made.
* If this DataManager is the primary fetching source, SyncManager uses
* calls this method before attempting to save a new state.
*/
ComposerState getLastAdded();
/** /**
* Returns the identifier for the DataManager. Preferably, use the source as the identifier. * Returns the identifier for the DataManager. Preferably, use the source as the identifier.

View file

@ -91,55 +91,46 @@ class SQLiteManager implements DataManager {
* @param newState - The state of the Dashboard while making the request. * @param newState - The state of the Dashboard while making the request.
*/ */
@Override @Override
public synchronized void saveState(ComposerState newState) { public synchronized void saveState(ComposerState newState) throws SQLException {
ComposerState lastState = getLastRequest(); statement = conn.prepareStatement(Queries.saveRequest);
if (newState.equals(lastState))
return;
try { statement.setString(1, newState.httpMethod);
statement = statement.setString(2, newState.target);
conn.prepareStatement(Queries.saveRequest); statement.setString(3, LocalDate.now().toString());
statement.executeUpdate();
statement.setString(1, newState.httpMethod); // Get latest RequestID to insert into Headers table
statement.setString(2, newState.target); statement = conn.prepareStatement("SELECT MAX(ID) AS MaxID FROM Requests");
statement.setString(3, LocalDate.now().toString());
ResultSet RS = statement.executeQuery();
int requestID = -1;
if (RS.next())
requestID = RS.getInt("MaxID");
saveTuple(newState.headers, "Header", requestID);
saveTuple(newState.params, "Param", requestID);
if (!(newState.httpMethod.equals(HTTPConstants.GET) || newState.httpMethod.equals(HTTPConstants.DELETE))) {
// Maps the request to its ContentType for faster retrieval
statement = conn.prepareStatement(Queries.saveRequestContentPair);
statement.setInt(1, requestID);
statement.setString(2, newState.contentType);
statement.executeUpdate(); statement.executeUpdate();
// Get latest RequestID to insert into Headers table statement = conn.prepareStatement(Queries.saveBody);
statement = conn.prepareStatement("SELECT MAX(ID) AS MaxID FROM Requests"); statement.setInt(1, requestID);
statement.setString(2, newState.rawBody);
statement.setString(3, newState.rawBodyBoxValue);
statement.executeUpdate();
ResultSet RS = statement.executeQuery(); statement = conn.prepareStatement(Queries.saveFilePath);
int requestID = -1; statement.setInt(1, requestID);
if (RS.next()) statement.setString(2, newState.binaryFilePath);
requestID = RS.getInt("MaxID"); statement.executeUpdate();
saveTuple(newState.headers, "Header", requestID); saveTuple(newState.urlStringTuples, "URLString", requestID);
saveTuple(newState.params, "Param", requestID); saveTuple(newState.formStringTuples, "FormString", requestID);
saveTuple(newState.formFileTuples, "File", requestID);
if (!(newState.httpMethod.equals(HTTPConstants.GET) || newState.httpMethod.equals(HTTPConstants.DELETE))) {
// Maps the request to its ContentType for faster retrieval
statement = conn.prepareStatement(Queries.saveRequestContentPair);
statement.setInt(1, requestID);
statement.setString(2, newState.contentType);
statement.executeUpdate();
statement = conn.prepareStatement(Queries.saveBody);
statement.setInt(1, requestID);
statement.setString(2, newState.rawBody);
statement.setString(3, newState.rawBodyBoxValue);
statement.executeUpdate();
statement = conn.prepareStatement(Queries.saveFilePath);
statement.setInt(1, requestID);
statement.setString(2, newState.binaryFilePath);
statement.executeUpdate();
saveTuple(newState.urlStringTuples, "URLString", requestID);
saveTuple(newState.formStringTuples, "FormString", requestID);
saveTuple(newState.formFileTuples, "File", requestID);
}
} catch (SQLException e) {
LoggingService.logWarning("Database error.", e, LocalDateTime.now());
} }
} }
@ -147,49 +138,45 @@ class SQLiteManager implements DataManager {
* Returns a list of all the recent requests. * Returns a list of all the recent requests.
*/ */
@Override @Override
public synchronized List<ComposerState> getHistory() { public synchronized List<ComposerState> getHistory() throws SQLException {
List<ComposerState> history = new ArrayList<>(); List<ComposerState> history = new ArrayList<>();
try { // Loads the requests from the last x number of days, x being Settings.showHistoryRange
// Loads the requests from the last x number of days, x being Settings.showHistoryRange statement = conn.prepareStatement(Queries.selectRecentRequests);
statement = conn.prepareStatement(Queries.selectRecentRequests); String historyStartDate = LocalDate.now().minusDays(Settings.showHistoryRange).toString();
String historyStartDate = LocalDate.now().minusDays(Settings.showHistoryRange).toString(); statement.setString(1, historyStartDate);
statement.setString(1, historyStartDate);
ResultSet resultSet = statement.executeQuery(); ResultSet resultSet = statement.executeQuery();
ComposerState state; ComposerState state;
while (resultSet.next()) { while (resultSet.next()) {
state = new ComposerState(); state = new ComposerState();
state.target = resultSet.getString("Target"); state.target = resultSet.getString("Target");
int requestID = resultSet.getInt("ID"); int requestID = resultSet.getInt("ID");
state.headers = getTuples(requestID, "Header"); state.headers = getTuples(requestID, "Header");
state.params = getTuples(requestID, "Param"); state.params = getTuples(requestID, "Param");
state.httpMethod = resultSet.getString("Type"); state.httpMethod = resultSet.getString("Type");
if (!(state.httpMethod.equals(HTTPConstants.GET) || state.httpMethod.equals(HTTPConstants.DELETE))) { if (!(state.httpMethod.equals(HTTPConstants.GET) || state.httpMethod.equals(HTTPConstants.DELETE))) {
// Retrieves request body ContentType for querying corresponding table // Retrieves request body ContentType for querying corresponding table
state.contentType = getRequestContentType(requestID); state.contentType = getRequestContentType(requestID);
Pair<String, String> rawBodyAndType = getRequestBody(requestID); Pair<String, String> rawBodyAndType = getRequestBody(requestID);
if (rawBodyAndType != null) { if (rawBodyAndType != null) {
state.rawBody = rawBodyAndType.getKey(); state.rawBody = rawBodyAndType.getKey();
state.rawBodyBoxValue = rawBodyAndType.getValue(); state.rawBodyBoxValue = rawBodyAndType.getValue();
}
state.binaryFilePath = getFilePath(requestID);
state.urlStringTuples = getTuples(requestID, "URLString");
state.formStringTuples = getTuples(requestID, "FormString");
state.formFileTuples = getTuples(requestID, "File");
} }
history.add(state); state.binaryFilePath = getFilePath(requestID);
state.urlStringTuples = getTuples(requestID, "URLString");
state.formStringTuples = getTuples(requestID, "FormString");
state.formFileTuples = getTuples(requestID, "File");
} }
} catch (SQLException e) {
LoggingService.logWarning("Database error.", e, LocalDateTime.now()); history.add(state);
} }
return history; return history;
@ -239,7 +226,8 @@ class SQLiteManager implements DataManager {
return fieldStates; return fieldStates;
} }
private ComposerState getLastRequest() { @Override
public ComposerState getLastAdded() {
ComposerState lastRequest = new ComposerState(); ComposerState lastRequest = new ComposerState();
try { try {
statement = conn.prepareStatement(Queries.selectMostRecentRequest); statement = conn.prepareStatement(Queries.selectMostRecentRequest);

View file

@ -41,7 +41,10 @@ public class SyncManager {
/** /**
* Asynchronously saves the new state by invoking all the registered DataManagers. * Asynchronously saves the new state by invoking all the registered DataManagers.
*/ */
public static void saveState(ComposerState newState) { public void saveState(ComposerState newState) {
if (newState.equals(managers.get(Settings.fetchSource).getLastAdded()))
return;
historySaver.newState = newState; historySaver.newState = newState;
executor.execute(historySaver); executor.execute(historySaver);
@ -53,20 +56,27 @@ public class SyncManager {
* *
* @return A list of all the requests * @return A list of all the requests
*/ */
public static List<ComposerState> getHistory() { public List<ComposerState> getHistory() {
if (managers.get(Settings.fetchSource) == null) { List<ComposerState> history = null;
LoggingService.logSevere("No such source found: " + Settings.fetchSource, null, LocalDateTime.now()); try {
return managers.get("SQLite").getHistory(); if (managers.get(Settings.fetchSource) == null) {
} else { LoggingService.logSevere("No such source found: " + Settings.fetchSource, null, LocalDateTime.now());
return managers.get(Settings.fetchSource).getHistory(); history = managers.get("SQLite").getHistory();
} else {
history = managers.get(Settings.fetchSource).getHistory();
}
} catch (Exception e) {
LoggingService.logSevere("History could not be fetched.", e, LocalDateTime.now());
} }
return history;
} }
/** /**
* Registers a new DataManager to be used for syncing Everest's data * Registers a new DataManager to be used for syncing Everest's data
* at various sources. * at various sources.
*/ */
public static void registerManager(DataManager newManager) throws DuplicateException { public void registerManager(DataManager newManager) throws DuplicateException {
if (managers.containsKey(newManager.getIdentifier())) if (managers.containsKey(newManager.getIdentifier()))
throw new DuplicateException( throw new DuplicateException(
"Duplicate DataManager: Manager with identifier \'" + newManager.getIdentifier() + "\' already exists."); "Duplicate DataManager: Manager with identifier \'" + newManager.getIdentifier() + "\' already exists.");
@ -79,8 +89,12 @@ public class SyncManager {
@Override @Override
public void run() { public void run() {
for (DataManager manager : managers.values()) try {
manager.saveState(newState); for (DataManager manager : managers.values())
manager.saveState(newState);
} catch (Exception e) {
LoggingService.logSevere("Could not save history.", e, LocalDateTime.now());
}
} }
} }
} }