Mega-refactoring and docs
diff --git a/asterix-examples/src/main/resources/black-cherry/cherry.tpl b/asterix-examples/src/main/resources/black-cherry/cherry.tpl
index af3220f..a3dc994 100755
--- a/asterix-examples/src/main/resources/black-cherry/cherry.tpl
+++ b/asterix-examples/src/main/resources/black-cherry/cherry.tpl
@@ -141,14 +141,8 @@
<input type="checkbox" value="Submit Asynchronously" name="async" id="asbox" />
Submit asynchronously?
</li>
-
- <!-- Div to hold error messages -->
- <li class="list-group-item">
- <div id="explore-report-message"></div>
- </li>
</ul>
</div>
-
</div>
<div class="container well" id="review-well" style="display:none;">
@@ -185,6 +179,9 @@
</div>
</div>
</div>
+
+ <!-- Div to hold success/error messages -->
+ <div id="report-message"></div>
</div>
<div class="col-md-7">
diff --git a/asterix-examples/src/main/resources/black-cherry/run_black_cherry.py b/asterix-examples/src/main/resources/black-cherry/run_black_cherry.py
index 62eb85c..f461934 100755
--- a/asterix-examples/src/main/resources/black-cherry/run_black_cherry.py
+++ b/asterix-examples/src/main/resources/black-cherry/run_black_cherry.py
@@ -36,7 +36,6 @@
def run_asterix_query_result():
return (build_response("query/result", dict(request.query)))
-
@route('/ddl')
def run_asterix_ddl():
return (build_response("ddl", dict(request.query)))
diff --git a/asterix-examples/src/main/resources/black-cherry/static/img/mobile_green2.png b/asterix-examples/src/main/resources/black-cherry/static/img/mobile_green2.png
index 9efc3f6..d590461 100755
--- a/asterix-examples/src/main/resources/black-cherry/static/img/mobile_green2.png
+++ b/asterix-examples/src/main/resources/black-cherry/static/img/mobile_green2.png
Binary files differ
diff --git a/asterix-examples/src/main/resources/black-cherry/static/js/asterix-sdk-stable.js b/asterix-examples/src/main/resources/black-cherry/static/js/asterix-sdk-stable.js
index cd467ce..fc93875 100755
--- a/asterix-examples/src/main/resources/black-cherry/static/js/asterix-sdk-stable.js
+++ b/asterix-examples/src/main/resources/black-cherry/static/js/asterix-sdk-stable.js
@@ -13,19 +13,43 @@
* same basic functionality.
*/
function AsterixDBConnection(configuration) {
+ // Initialize AsterixDBConnection properties
this._properties = {};
+
+ // Set dataverse as null for now, this needs to be set by the user.
this._properties["dataverse"] = "";
+
+ // By default, we will wait for calls to the REST API to complete. The query method
+ // sends a different setting when executed asynchronously. Calls that do not specify a mode
+ // will be executed synchronously.
this._properties["mode"] = "synchronous";
- this._properties["error"] = function (e) {
- // TODO Needs to be more informative
- //alert(e);
- }
+
+ // These are the default error behaviors for Asterix and ajax errors, respectively.
+ // They can be overridden by calling initializing your AsterixDBConnection like so:
+ // adb = new AsterixDBConnection({
+ // "error" : function(data) {
+ // // override here...
+ // });
+ // and similarly for ajax_error, just pass in the configuration as a json object.
+ this._properties["error"] = function(data) {
+ alert("Asterix REST API Error:\n" + data["error-code"][0] + "\n" + data["error-code"][1]);
+ };
+
+ this._properties["ajax_error"] = function(message) {
+ alert("[Ajax Error]\n" + message);
+ };
- // This is a demo setup related fix. Enabled by proxy to Asterix REST API.
- this._properties["endpoint_root"] = "/";
+ // This is the default path to the local Asterix REST API. Can be overwritten for remote configurations
+ // or for demo setup purposes (such as with a proxy handler with Python or PHP.
+ this._properties["endpoint_root"] = "http://localhost:19002/";
- var configuration = arguments || {};
-
+ // If we have passed in a configuration, we will update the internal properties
+ // using that configuration. You can do things such as include a new endpoint_root,
+ // a new error function, a new dataverse, etc. You can even store extra info.
+ //
+ // NOTE Long-term, this should have more strict limits.
+ var configuration = configuration || {};
+
for (var key in configuration) {
this._properties[key] = configuration[key];
}
@@ -199,31 +223,93 @@
* http://asterix.ics.uci.edu/documentation/api.html
*
* This is treated as an internal method for making the actual call to the API.
-* TODO Fix jquery dependency
*/
AsterixDBConnection.prototype._api = function(json, onSuccess, endpoint) {
+
+ // The success function is called if the response is successful and returns data,
+ // or is just OK.
var success_fn = onSuccess;
+
+ // This is the error function. Called if something breaks either on the Asterix side
+ // or in the Ajax call.
var error_fn = this._properties["error"];
+ var ajax_error_fn = this._properties["ajax_error"];
+
+ // This is the target endpoint from the REST api, called as a string.
var endpoint_url = this._properties["endpoint_root"] + endpoint;
- $.ajax({
- type: 'GET',
- url: endpoint_url,
- data : json,
- dataType: "json",
- success: function(data) {
- success_fn(data);
- },
- error: function(xhr, status, error) {
- error_fn(error);
+ // This SDK does not rely on jQuery, but utilizes its Ajax capabilities when present.
+ if (window.jQuery) {
+ $.ajax({
+
+ // The Asterix API does not accept post requests.
+ type : 'GET',
+
+ // This is the endpoint url provided by combining the default
+ // or reconfigured endpoint root along with the appropriate api endpoint
+ // such as "query" or "update".
+ url : endpoint_url,
+
+ // This is the data in the format specified on the API documentation.
+ data : json,
+
+ // We send out the json datatype to make sure our data is parsed correctly.
+ dataType : "json",
+
+ // The success option calls a function on success, which in this case means
+ // something was returned from the API. However, this does not mean the call succeeded
+ // on the REST API side, it just means we got something back. This also contains the
+ // error return codes, which need to be handled before we call th success function.
+ success : function(data) {
+
+ // Check Asterix Response for errors
+ // See http://asterix.ics.uci.edu/documentation/api.html#ErrorCodes
+ if (data["error-code"]) {
+ error_fn(data);
+
+ // Otherwise, run our provided success function
+ } else {
+ success_fn(data);
+ }
+ },
+
+ // This is the function that gets called if there is an ajax-related (non-Asterix)
+ // error. Network errors, empty response bodies, syntax errors, and a number of others
+ // can pop up.
+ error : function(data) {
+ // Some of the Asterix API endpoints return empty responses on success.
+ // However, the ajax function treats these as errors while reporting a
+ // 200 OK code with no payload. So we will check for that, otherwise
+ // alert of an error. An example response is as follows:
+ // {"readyState":4,"responseText":"","status":200,"statusText":"OK"}
+ if (data["status"] == 200 && data["responseText"] == "") {
+ success_fn(data);
+ } else {
+ thi
+ alert("[Ajax Error]\n" + JSON.stringify(data));
+ }
+ }
+ });
+ } else {
+ alert("no jquery");
+ var xmlhttp;
+ }
+ // XML Http Request
+ /*var xmlhttp;
+
+ xmlhttp = new XMLHttpRequest();
+ xmlhttp.onreadystatechange = function(){
+ if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
+ alert(xmlhttp.responseText);
+ //callback(xmlhttp.responseText);
}
- });
+ }
+ xmlhttp.open("GET", endpoint_url, true);
+ xmlhttp.send();*/
return this;
};
-// TODO Better documentation below here.
-
// Asterix Expressions - Base
function AExpression () {
diff --git a/asterix-examples/src/main/resources/black-cherry/static/js/cherry.js b/asterix-examples/src/main/resources/black-cherry/static/js/cherry.js
index fb83406..2d42251 100755
--- a/asterix-examples/src/main/resources/black-cherry/static/js/cherry.js
+++ b/asterix-examples/src/main/resources/black-cherry/static/js/cherry.js
@@ -1,7 +1,40 @@
$(function() {
- // Connection to AsterixDB - Just one needed!
- A = new AsterixDBConnection().dataverse("twitter");
+ // Initialize connection to AsterixDB. Just one connection is needed and contains
+ // logic for connecting to each API endpoint. This object A is reused throughout the
+ // code but does not store information about any individual API call.
+ A = new AsterixDBConnection({
+
+ // We will be using the twitter dataverse, which we can configure either like this
+ // or by following our AsterixDBConnection with a dataverse call, like so:
+ // A = new AsterixDBConnection().dataverse("twitter");
+ "dataverse" : "twitter",
+
+ // Due to the setup of this demo using the Bottle server, it is necessary to change the
+ // default endpoint of API calls. The proxy server handles the call to http://localhost:19002
+ // for us, and we reconfigure this connection to connect to the proxy server.
+ "endpoint_root" : "/",
+
+ // Finally, we want to make our error function nicer so that we show errors with a call to the
+ // reportUserMessage function. Update the "error" property to do that.
+ "error" : function(data) {
+ // For an error, data will look like this:
+ // {
+ // "error-code" : [error-number, error-text]
+ // "stacktrace" : ...stack trace...
+ // "summary" : ...summary of error...
+ // }
+ // We will report this as an Asterix REST API Error, an error code, and a reason message.
+ // Note the method signature: reportUserMessage(message, isPositiveMessage, target). We will provide
+ // an error message to display, a positivity value (false in this case, errors are bad), and a
+ // target html element in which to report the message.
+ var showErrorMessage = "Asterix Error #" + data["error-code"][0] + ": " + data["error-code"][1];
+ var isPositive = false;
+ var showReportAt = "report-message";
+
+ reportUserMessage(showErrorMessage, isPositive, showReportAt);
+ }
+ });
// Following this is some stuff specific to the Black Cherry demo
// This is not necessary for working with AsterixDB
@@ -29,7 +62,7 @@
$("#clear-button").button().click(function () {
mapWidgetResetMap();
- $('#explore-report-message').html('');
+ $('#report-message').html('');
$('#query-preview-window').html('');
$("#metatweetzone").html('');
});
@@ -187,10 +220,10 @@
var kwterm = $("#keyword-textbox").val();
if (kwterm == "") {
- addFailureBlock("Please enter a search term!", "explore-report-message");
+ reportUserMessage("Please enter a search term!", false, "report-message");
} else {
- $("#explore-report-message").html('');
+ $("#report-message").html('');
$("#submit-button").attr("disabled", true);
var startdp = $("#start-date").datepicker("getDate");
@@ -258,7 +291,6 @@
});
});
-
function buildAQLQueryFromForm(parameters) {
var bounds = {
@@ -295,7 +327,6 @@
return aql;
}
-
/**
* getAllDataverseTweetbooks
*
@@ -339,7 +370,6 @@
}
-
/** Asynchronous Query Management **/
/**
@@ -353,7 +383,6 @@
}
}
-
/**
* Returns current time interval to check for asynchronous query readiness
* @returns {number} milliseconds between asychronous query checks
@@ -363,7 +392,6 @@
return seconds * 1000;
}
-
/**
* Retrieves status of an asynchronous query, using an opaque result handle from API
* @param {Object} handle, an object previously returned from an async call
@@ -388,7 +416,6 @@
);
}
-
/**
* On-success callback after async API query
* @param {object} res, a result object containing an opaque result handle to Asterix
@@ -464,7 +491,6 @@
$("#submit-button").attr("disabled", false);
}
-
/**
* returns a json object with keys: weight, latSW, lngSW, latNE, lngNE
*
@@ -486,7 +512,6 @@
.replace("int64", '"int64"');
}
-
/**
* A spatial data cleaning and mapping call
* @param {Object} res, a result object from a cherry geospatial query
@@ -666,7 +691,6 @@
return drillDown;
}
-
function onDrillDownAtLocation(tO) {
var tweetId = tO["tweetEntryId"];
@@ -719,6 +743,7 @@
$("#modal-save-tweet-panel").show();
// Now, when adding a comment on an available tweet to a tweetbook
+ $('#save-comment-tweetbook-modal').unbind('click');
$("#save-comment-tweetbook-modal").on('click', function(e) {
// Stuff to save about new comment
@@ -728,12 +753,12 @@
// Make sure content is entered, and then save this comment.
if ($("#modal-body-add-note").val() == "") {
-
- addFailureBlock("Please enter a comment.", "modal-body-message-holder");
+
+ reportUserMessage("Please enter a comment about the tweet", false, "report-message");
} else if ($("#modal-body-add-to").val() == "") {
- addFailureBlock("Please enter a tweetbook.", "modal-body-message-holder");
+ reportUserMessage("Please enter a tweetbook.", false, "report-message");
} else {
@@ -749,21 +774,24 @@
"comment-text" : save_metacomment_target_comment
}
);
- A.update(toInsert.val(), function () {});
+
+ A.update(toInsert.val(), function () {
+ var successMessage = "Saved comment on <b>Tweet #" + tweetId +
+ "</b> in dataset <b>" + save_metacomment_target_tweetbook + "</b>.";
+ reportUserMessage(successMessage, true, "report-message");
- var successMessage = "Saved comment on <b>Tweet #" + tweetId +
- "</b> in dataset <b>" + save_metacomment_target_tweetbook + "</b>.";
- addSuccessBlock(successMessage, "modal-body-message-holder");
-
- $("#modal-body-add-to").val('');
- $("#modal-body-add-note").val('');
- $("#modal-body-message-holder").html('');
+ $("#modal-body-add-to").val('');
+ $("#modal-body-add-note").val('');
+ $('#save-comment-tweetbook-modal').unbind('click');
+
+ // Close modal
+ $('#drilldown_modal').modal('hide');
+ });
}
});
}
}
-
/**
* Adds a new tweetbook entry to the menu and creates a dataset of type TweetbookEntry.
*/
@@ -782,7 +810,6 @@
}
}
-
function onDropTweetBook(tweetbook_title) {
// AQL Call
@@ -802,7 +829,6 @@
}
}
-
function addTweetBookDropdownItem(tweetbook) {
// Add placeholder for this tweetbook
$('<div/>')
@@ -829,7 +855,6 @@
);
}
-
function onPlotTweetbook(tweetbook) {
// Clear map for this one
@@ -856,7 +881,6 @@
A.query(plotTweetQuery.val(), onTweetbookQuerySuccessPlot);
}
-
function onTweetbookQuerySuccessPlot (res) {
var records = res["results"];
@@ -913,7 +937,6 @@
});
}
-
function existsTweetbook(tweetbook) {
if (parseInt($.inArray(tweetbook, review_mode_tweetbooks)) == -1) {
return false;
@@ -922,7 +945,6 @@
}
}
-
function onCleanPlotTweetbook(records) {
var toPlot = [];
@@ -946,7 +968,6 @@
return toPlot;
}
-
function onCleanTweetbookDrilldown (rec) {
var drilldown_cleaned = [];
@@ -968,7 +989,6 @@
return drilldown_cleaned;
}
-
function onClickTweetbookMapMarker(tweet_arr) {
// Clear existing display
$.each(tweet_arr, function (t, valueT) {
@@ -981,7 +1001,6 @@
/** Toggling Review and Explore Modes **/
-
/**
* Explore mode: Initial map creation and screen alignment
*/
@@ -997,7 +1016,6 @@
$('#right-col').height(explore_column_height + "px");
}
-
/**
* Launching explore mode: clear windows/variables, show correct sidebar
*/
@@ -1009,14 +1027,12 @@
$('#review-active').removeClass('active');
$('#review-well').hide();
-
$('#explore-active').addClass('active');
$('#explore-well').show();
$("#clear-button").trigger("click");
}
-
/**
* Launching review mode: clear windows/variables, show correct sidebar
*/
@@ -1034,7 +1050,6 @@
$("#clear-button").trigger("click");
}
-
/**
* Lauching about mode: hides all windows, shows row containing about info
*/
@@ -1063,34 +1078,29 @@
$('#' + iconId).on('click', onClick);
}
-
/**
-* Creates a success message and attaches it to a div with provided ID.
+* Creates a message and attaches it to data management area.
* @param {String} message, a message to post
-* @param {String} appendTarget, a target div to which to append the alert
+* @param {Boolean} isPositiveMessage, whether or not this is a positive message.
+* @param {String} target, the target div to attach this message.
*/
-function addSuccessBlock(message, appendTarget) {
- $('#' + appendTarget).html('');
- $('<div/>')
- .attr("class", "alert alert-success")
- .html('<button type="button" class="close" data-dismiss="alert">×</button>' + message)
- .appendTo('#' + appendTarget);
-}
-
-/**
-* Creates a failure mesage and attaches it to a div with provided id.
-* @param {String} message, a message to post
-* @param {String} target, a target div to append the message
-*/
-function addFailureBlock(message, target) {
+function reportUserMessage(message, isPositiveMessage, target) {
+ // Clear out any existing messages
$('#' + target).html('');
+
+ // Select appropriate alert-type
+ var alertType = "alert-success";
+ if (!isPositiveMessage) {
+ alertType = "alert-danger";
+ }
+
+ // Append the appropriate message
$('<div/>')
- .attr("class", "alert alert-danger")
+ .attr("class", "alert " + alertType)
.html('<button type="button" class="close" data-dismiss="alert">×</button>' + message)
.appendTo('#' + target);
}
-
/**
* mapWidgetResetMap
*