diff options
Diffstat (limited to 'qpid/tools/src/java/qpid-qmf2-tools/bin/qpid-web/web/qpid/scripts/qpid.js')
-rw-r--r-- | qpid/tools/src/java/qpid-qmf2-tools/bin/qpid-web/web/qpid/scripts/qpid.js | 745 |
1 files changed, 745 insertions, 0 deletions
diff --git a/qpid/tools/src/java/qpid-qmf2-tools/bin/qpid-web/web/qpid/scripts/qpid.js b/qpid/tools/src/java/qpid-qmf2-tools/bin/qpid-web/web/qpid/scripts/qpid.js new file mode 100644 index 0000000000..f1ad4711f2 --- /dev/null +++ b/qpid/tools/src/java/qpid-qmf2-tools/bin/qpid-web/web/qpid/scripts/qpid.js @@ -0,0 +1,745 @@ +/** + * http://www.apache.org/licenses/LICENSE-2.0 + * + * This library provides a JavaScript implementation of the QMF2 API (and TODO qpid::messaging - some code is in place + * for Connection as necessary to implement the QMF2 API the rest will follow in due course. + * + * This implementation of the QMF2 API relies on the Qpid REST API as a back end server and the QMF2 API methods + * are basically proxies by the Qpid REST API to a real QMF2 Console implemented on the back end. Note that this + * implementation uses AJAX/REST over pure HTTP which results in some inefficiencies, in particular the mechanism + * used to retrieve QMF2 Events (getWorkItem()) uses the AJAX long-polling pattern. It should be possible to + * provide an alternative implementation using WebSockets, the main two reasons that this hasn't been done are. + * 1) The author lacks familiarity with WebSockets.... + * 2) WebSockets have much poorer cross-browser support, though to be fair that could be mitigated using a + * WebSocket JavaScript library that could fall back to using HTTP if browser/server support was unavaiable. + * + * This library also includes a utility package providing a number of useful classes and helper functions. These + * aren't strictly part of qpid/qmf JavaScript but stringify() and randomUUID() are used by qpid.js so including + * the util package in this library avoids adding yet another dependency. + * + * It has dependencies on the following: + * jquery.js (> 1.5) + * + * author Fraser Adams + */ + +//------------------------------------------------------------------------------------------------------------------- + +// Create a new namespace for the util "package". +var util = {}; + +/** + * This debug method lists the properties of the specified JavaScript object. + * @param obj the object that we wish to list the properties for. + " @return a string containing the list of the object's properties pretty printed. + */ +util.displayProperties = function(obj) { + var result = obj + "\n\n"; + var count = 0; + for (var i in obj) { + result += i + ", "; + if (count % 4 == 3) result += "\n"; + count++; + } + result = result.substring(0, result.lastIndexOf(",")); + return result; +}; + +/** + * Stringify an Object into JSON. Uses JSON.stringify if present and if not it uses a quick and dirty serialiser. + * @param obj the object that we wish to stringify into JSON. + * @return the JSON representation of the specified object. + */ +util.stringify = function(obj) { + var fromObject = function(obj) { + if (Object.prototype.toString.apply(obj) === '[object Array]') { + var string = ""; + var length = obj.length; + for (var i = 0; i < length; i++) { + string += fromObject(obj[i]); + if (i < length - 1) { + string += ","; + } + } + + string = "[" + string + "]"; + return string; + } else if (typeof obj == "object") { // Check if the value part is an ObjectId and serialise appropriately + if (!obj) { + return "null"; + } + + var string = ""; + for (var i in obj) { + if (obj.hasOwnProperty(i)) { + if (string != "") { + string += ","; + } + string += '"' + i + '":' + fromObject(obj[i]); + } + } + + string = "{" + string + "}"; + return string; + } else if (typeof obj == "string") { + return '"' + obj + '"'; + } else { + return obj.toString(); + } + }; + + if (obj == null) { + return ""; + } if (window.JSON && JSON.stringify && typeof JSON.stringify == "function") { + return JSON.stringify(obj); + } else { + var string = fromObject(obj); + return string; + } +}; + +/** + * Compact rfc4122v4 UUID from http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript + * @return an rfc4122v4 UUID. + */ +util.randomUUID = function() { + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) { + var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); + return v.toString(16); + }); +}; + +/** + * This is a JavaScript port of the "fill count" example from http://en.wikipedia.org/wiki/Circular_buffer + * @param size maximum size of the RingBuffer that we want to construct, when items are added beyond this size + * they will wrap around. + */ +util.RingBuffer = function(size) { + var _size = size; + var _start = 0; + var _count = 0; + var _elems = new Array(size); + + /** + * @return the maximum size of the RingBuffer. + */ + this.capacity = function() { + return _size; + }; + + /** + * @return the number of items currently stored in the RingBuffer. + */ + this.size = function() { + return _count; + }; + + /** + * @return true if the buffer is full otherwise return false. + */ + this.isFull = function() { + return _count == _size; + }; + + /** + * @return true if the buffer is empty otherwise return false. + */ + this.isEmpty = function() { + return _count == 0; + }; + + /** + * Add an item to the end of the buffer, overwriting oldest element if buffer is full. + * A client can choose to avoid the overwrite by checking IsFull(). + * @param the item that we wish to add to the end of the ring buffer. + */ + this.put = function(item) { + var end = (_start + _count) % _size; + _elems[end] = item; + if (_count == _size) { + _start = (_start + 1) % _size; // full, overwrite + } else { + ++_count; + } + }; + + /** + * Read and remove oldest item from buffer. N.B. clients must ensure !isEmpty() first. + * @return the oldest item from the ring buffer. + */ + this.take = function() { + var item = _elems[_start]; + _start = (_start + 1) % _size; + --_count; + return item; + }; + + /** + * Read the item at the specified (circular) index non-destructively e.g. index 0 is the first item held in + * the ring buffer index size() - 1 is the last item (which is the equivalent of getLast()). + * @return the specified ring buffer item. + */ + this.get = function(index) { + index = (_start + index) % _size; + return _elems[index]; + }; + + /** + * @return the last item, or null if no entries are present. + */ + this.getLast = function() { + if (_count == 0) { + return null; + } else { + return this.get(_count - 1); + } + }; +}; + + +//------------------------------------------------------------------------------------------------------------------- +/** + * This package is a proxy to the Qpid REST API, which is itself a proxy to a real Qpid API. + * It attempts to mimic the Qpid Messaging API where possible (given the asynchronous constraints of JavaScript). + */ + +// Create a new namespace for the qpid "package". +var qpid = {}; + +/** + * This factory class will attempt to create a Connection object and creates an opaque handle to it internally + * which will be used in the URI of subsequent Qpid/QMF calls to the Qpid REST API. + * + * @param url an AMQP 0.10 URL, an extended AMQP 0-10 URL, a Broker URL or a Java Connection URL. + * @param opts a String containing the options encoded using the same form as the C++ qpid::messaging Connection class. + * @return a Connection object which is a proxy to the Qpid REST API, which is itself a proxy to a real Qpid API. + */ +qpid.ConnectionFactory = function(url, connectionOptions) { + + /** + * This class is a proxy to the Qpid REST API PUT method for creating Connections. + * @param url an AMQP 0.10 URL, an extended AMQP 0-10 URL, a Broker URL or a Java Connection URL. + * @param opts a String containing the options encoded using the same form as the C++ qpid::messaging Connection. + */ + var Connection = function(url, connectionOptions) { + var _disableEvents = false; + var _available = false; + var _url = url; + var _handle = util.randomUUID(); + var _defaultCallback = function(connection) {}; + var _callback = _defaultCallback; + var _failureCallback = _defaultCallback; + + /** + * The success callback method for the AJAX call invoked by putConnection(). This method marks the + * Connection as available and calls the callback method registered by the call to open(). + */ + var putConnectionSucceeded = function() { + _available = true; + _callback(this); + }; + + /** + * The failure callback method for the AJAX call invoked by putConnection(). This method calls the callback + * method registered by the call to open(). + */ + var putConnectionFailed = function(xhr) { + _available = false; + _failureCallback(this); + }; + + /** + * Create a Connection object on the REST API via an asynchronous HTTP PUT method. + */ + var putConnection = function() { + var data = {url: _url}; + if (connectionOptions != null && connectionOptions != "") { + data.connectionOptions = connectionOptions; + } + + if (_disableEvents) { + data.disableEvents = true; + } + + // Serialise the data Object into a JSON String. + data = util.stringify(data); + + $.ajax({ + type: "PUT", + url: "../qpid/connection/" + _handle, + cache: false, + contentType: "application/json", + data: data, + timeout: 10000, + success: putConnectionSucceeded, + error: putConnectionFailed + }); + }; + + /** + * Remove a Connection object on the REST API via a synchronous HTTP DELETE method. + */ + var deleteConnection = function() { + //console.log("**** calling qpid.Connection deleteConnection() ****"); + /** + * Explicitly retrieve the XmlHttpRequest and send the DELETE via a low-level synchronous call because + * JQuery 1.8 has deprecated the async: false setting on $.ajax. There's some debate about this - see + * http://bugs.jquery.com/ticket/11013 the gist is that synchronous requests can prevent the rest of + * the $.ajax plumbing and dependencies from being fixed. In an ideal world asynchronous requests + * are almost always preferable, however in mobile Safari (at least) asynchronous calls won't get + * fired from onpagehide handlers, which messes up some useful garbage collection when navigating away. + * TODO I wonder how delete onpageunload is going to work if I want to use JSONP...... + */ + var xhr = $.ajaxSettings.xhr(); // At least we can get the XMLHttpRequest in a platform neutral way. + xhr.open("DELETE", "../qpid/connection/" + _handle, false); // Low level synchronous DELETE. + xhr.send(null); + }; + + /** + * Calling this method results in a QMF2 Console being created that can only perform synchronous calls such + * as getObjects() and can't do asynchronous things like receive Agent updates and QMF2 Events. + * + * This method must be called besfore addConnection() in order to take effect. + */ + this.disableEvents = function() { + _disableEvents = true; + } + + /** + * Open the Connection object for use. + * @param successCallback a handler to be called when open() has successfully established the Qpid Connection. + * we include the callback method because JavaScript networking is fundamentally asynchronous so open() + * won't block as it would with other languages, the callback provides a way to defer execution of subsequent + * code until the Connection is established. + * @param failureCallback a handler to be called when open() has failed to established the Qpid Connection. + * Note that this will only be called if creating the connection is impossible, that is to say an exception + * got thrown by the REST API PUT mothod. If the broker is simply down the Connection proxy will get created + * on the server side and the successCallback will be called. + */ + this.open = function(successCallback, failureCallback) { + if (successCallback != null) { + _callback = successCallback; + } + + if (failureCallback != null) { + _failureCallback = failureCallback; + } + + _available = false; + putConnection(); + }; + + /** + * Close the Connection object. + */ + this.close = function() { + deleteConnection(); + _available = false; + }; + + /** + * Identify whether the Connection is open for use. + * @return true if the Connection is open, otherwise returns false. + */ + this.isAvailable = function() { + return _available; + }; + + /** + * Retrieve the Connection's "handle" which is used as part of its URI on the REST API. + * @return a String containing the handle UUID. + */ + this.toString = function() { + return _handle; + }; + }; + + /** + * Factory method used to construct a new Connection object. + * @return a new Connection object. + */ + this.createConnection = function() { + return new Connection(url, connectionOptions); + }; +}; + +//------------------------------------------------------------------------------------------------------------------- + +// Create a new namespace for the qmf "package". +var qmf = {}; +qmf.REFRESH_PERIOD = 10000; + +/** + * This class is a proxy to the QMF REST API, which is itself a proxy to a real QMF2 Console. + * It attempts to mimic the QMF2 API where possible however there are a few deviations because of the entirely + * asynchronous nature of JavaScript and AJAX. + * + * Constructor that provides defaults for name and domain and takes a Notifier/Listener. + * @param onEvent a QMFEventListener. + */ +qmf.Console = function(onEvent) { + var _disableEvents = false; + var _connection = null; + var _url = "../qpid/connection/"; + var _qmfEventListenerXHR; // Retain JQuery XHR object for QmfEventListener so we can abort request if needed. + + /** + * Send and "AGENT_DELETED" WorkItem for qpidd to the registered Event Listener. + */ + var sendBrokerDisconnectedEvent = function() { + var agent = {_vendor: "apache.org", _product: "qpidd", _instance: "1234", + _name: "apache.org:qpidd:1234", _epoch: 1, _heartbeat_interval: 10}; + onEvent({_type: "AGENT_DELETED", _params: {agent: agent}}); + }; + + /** + * Send and "AGENT_DELETED" WorkItem for qpid.restapi to the registered Event Listener. + */ + var sendRestApiDisconnectedEvent = function() { + var agent = {_vendor: "apache.org", _product: "qpid.restapi", _instance: "1234", + _name: "apache.org:qpid.restapi:1234", _epoch: 1, _heartbeat_interval: 10}; + onEvent({_type: "AGENT_DELETED", _params: {agent: agent}}); + }; + + /** + * Retrieve QMF2 WorkItems via the QMF2 REST API, note that this call may block (on the server). + */ + var dispatchEvents = function() { + if (_connection != null) { + _qmfEventListenerXHR = $.ajax({ + url: _url + "/console/nextWorkItem", + cache: false, + dataType: "json", + timeout: 3*qmf.REFRESH_PERIOD, + success: handleDispatchEventsSuccess, + error: handleDispatchEventsFailure + }); + } + }; + + /** + * Success callback method for dispatchEvents. When WorkItems are available they are delivered to the attached + * eventListener onEvent callback and dispatchEvents is called again to wait for the next WorkItem. + * @param data the QMF2 WorkItem data. + */ + var handleDispatchEventsSuccess = function(data) { + if (_connection != null) { + onEvent(data); + dispatchEvents(); + } + }; + + /** + * Failure callback method for dispatchEvents. This method sends "AGENT_DELETED" WorkItems to the registered + * Event Listener and attempts to re-establish a connection to the REST API and via the the broker. + * @param xhr the jQuery XHR object. + */ + var handleDispatchEventsFailure = function(xhr) { + //console.log("handleDispatchEventsFailure " + xhr.status + " " + xhr.statusText); + if (xhr.status == 0 || xhr.status == 12029) { // For some reason IE7 sends 12029?? + if (xhr.statusText == "timeout") { // If AJAX calls have timed out it's likely due to a failed broker. + sendBrokerDisconnectedEvent(); + } else { + sendRestApiDisconnectedEvent(); // If the status is 0 for another reason the server is probabbly down. + } + } else if (xhr.status == 404) { + // HTTP Not Found. This is most likely to mean that the Console has timed out and been garbage collected + // on the REST API Server so we simply attempt to re-open the Qpid Connection. + if (_connection != null && _connection.open) { + _connection.open(); + } + } else if (xhr.status == 500) { + // HTTP Internal Error. Sent by the REST API Server when it knows that the broker has disconnected. + sendBrokerDisconnectedEvent(); + } + + // If the failure wasn't caused by an abort we retry after a timeout. + if (xhr.statusText != "abort" && _connection != null) { + setTimeout(dispatchEvents, qmf.REFRESH_PERIOD); + } + }; + + /** + * Helper method to allow us to get the data from a specified resource from the REST API in the /console/ sub-path + * Most of the core QMF2 API mehods can make use of this. + * @param resourceName the name of the resource on the REST Server. This is the part of the resource after + * the connection e.g. "/console/objects/" + className for the getObjects() call. + * @param handler the callback handler if the AJAX GET is successful. + * @return the jQuery XHR Object. + */ + var getResource = function(resourceName, handler) { + return $.ajax({ + url: _url + resourceName, + cache: false, + dataType: "json", + timeout: 3*qmf.REFRESH_PERIOD, + success: handler + }); + }; + + // ******** QmfConsoleData Methods that will be attached to QmfData Objects via makeConsoleData()********** + + /** + * Invoke the named method using the supplied inArgs, the response occurs asynchronously and triggers the + * named handler method, the outArgs are sent as JSON to the data parameter of the handler method. + * Note that this method is intended to be attached to a QmfData JavaScript object. It will use the ObjectId + * of the QmfData object to determine the URL resource to POST the data to. + */ + var invokeMethod = function(name, inArgs, handler) { + //console.log("calling invokeMethod: " + name + ", oid: " + this._object_id); + var defaultHandler = function(data) {}; + + var postFailed = function(xhr) { + var error = xhr.responseText; + if (xhr.status != 500) { + error = (xhr.status == 0) ? "POST failed to return correctly." : xhr.statusText; + } + + handler({"error_text" : error}); + }; + + inArgs = (inArgs == null || typeof inArgs == "string") ? inArgs : util.stringify(inArgs); + handler = (handler == null) ? defaultHandler : handler; + + var data = (inArgs == null) ? '{"_method_name":"' + name + '"}' : + '{"_method_name":"' + name + '","_arguments":' + inArgs + '}'; + + $.ajax({ + type: "POST", + url: _url + "/object/" + this._object_id, + cache: false, + headers : {"cache-control": "no-cache"}, // Curtails iOS6 overly aggressive (incorrect!) caching. + contentType: "application/json", + data: data, + timeout: 10000, + success: handler, + error: postFailed + }); + }; + + /** + * Request that the Agent updates the value of this object's contents. + * @param handler a callback method to handle the asynchronously delivered object refresh. + * One slight quirk of the JavaScript implementation is that the state update occurs asynchronously which will + * affect code that tries to use the state immediately after a call to object.refresh(). The optional handler + * parameter allows client code to defer its code to a callback that gets triggered after the state update. + */ + var refresh = function(handler) { + var self = this; // So we use the correct this in the update method.... + + var update = function(data) { + // Save these timestamps from the original object as they are not correctly populated by the ManagementAgent. + var savedCreateTime = self._create_ts; + var savedDeleteTime = self._delete_ts; + + if (handler == null) { + // If no handler is supplied we update the state of the object itself. + + // Replace all of the current properties with the ones from the JSON response object. + for (var i in data) { + self[i] = data[i] + } + + // Restore the correct timestamps. + self._create_ts = savedCreateTime; + self._delete_ts = savedDeleteTime; + } else { + // If a handler is supplied we pass the JSON object returned from the query having set the correct + // timestamps and turned back into a QmfConsoleData. + // Restore the correct timestamps. + data._create_ts = savedCreateTime; + data._delete_ts = savedDeleteTime; + data.invokeMethod = self.invokeMethod; + data.refresh = self.refresh; + handler(data); + } + }; + + return getResource("/object/" + this._object_id, update); + } + + // ******************************************** Public Methods ******************************************** + + /** + * Calling this method results in a QMF2 Console being created that can only perform synchronous calls such + * as getObjects() and can't do asynchronous things like receive Agent updates and QMF2 Events. + * Note that "asynchronous" here relates to the underlying QMF Console created on the Server, as this is a + * JavaScript API things like getObjects() results still arrive asynchronously. + * + * This method must be called besfore addConnection() in order to take effect. + */ + this.disableEvents = function() { + _disableEvents = true; + } + + /** + * Connect the console to the AMQP cloud. + * + * @param connection a JavaScript qpid.Connection object. Alternatively if the handle string to the Connection + * object on the server is known this string can be supplied instead. This is most likely to be the case where + * the default QMF Console on the REST API Server is used e.g. by doing _console.addConnection("default"); + * @param failureCallback a handler to be called when addConnection() has failed to established the Qpid Connection. + * Note that this will *only* be called if creating the connection is impossible, that is to say an exception + * got thrown by the REST API PUT mothod. If the broker is simply down the Connection proxy will get created + * on the server side and the successCallback will be called. + */ + this.addConnection = function(connection, failureCallback) { + _connection = connection; + _url = _url + _connection.toString(); + + if (_connection.open) { + if (_disableEvents) { + _connection.disableEvents(); + _connection.open(null, failureCallback); + } else { + _connection.open(dispatchEvents, failureCallback); + } + } else { // Use this case if the connection that is passed in is just a string "handle" to the connection + dispatchEvents(); + } + }; + + /** + * Remove the AMQP connection from the console. Un-does the addConnection() operation. Note that because this + * API implementation is really a proxy removeConnection() just aborts any AJAX calls for the console. + * + * @param connection a JavaScript qpid.Connection object + */ + this.removeConnection = function(connection) { + if (_connection == connection) { + _connection = null; + + if (_qmfEventListenerXHR) { + _qmfEventListenerXHR.abort(); + } + } + }; + + /** + * Release the Console's resources. + */ + this.destroy = function() { + //console.log("Console.destroy()"); + this.removeConnection(_connection); + }; + + /** + * Perform a query for QmfData objects. In a difference to the specified QMF2 API rather than returning a list + * (possibly empty) of matching objects this JavaScript version triggers a callback, the data parameter of + * which contains the list of matching objects. Usage example: + * _console.getObjects("broker", function(data) {_objects.broker = data;}); + * + * @param className the class name QMF Management Objects that we wish to retrieve. + * TODO packageName and agentName. + * @param handler a handler to be called when getObjects() has successfully retrieved the specified objects. + * we include the callback method because JavaScript networking is fundamentally asynchronous so getObjects() + * won't block as it would with other languages, the callback provides a way to defer execution of subsequent + * code until the objects have been returned. + * @return returns the jQuery XHR object. In particular the reson for returning this is that it is a "Deferred" + * object, what this means is that is can be used to wait until the results from several getObjects() calls + * have returned before executing an overall callback. + */ + this.getObjects = function(className, handler) { + // TODO allow options to specify Package name and Agent name. + return getResource("/console/objects/" + className, handler); + }; + + /** + * The items returned by getObjects are really pure QmfData (really QmfManaged) Objects, they are data Objects + * with no methods. This is normally OK because most applications simply want to retrieve the properties/stats + * but there are occasions where some of the QmfConsoleData methods are useful. Rather than add the methods + * universally this method enables them to be added to specific QmfData instances. This approach is more + * efficient as the QmfData objects are created by the browser deserialising JSON and it seems a bit wasteful + * to iterate through turning the JSON data into full QmfConsoleData Objects when the methods are rarely used. + * @param data the QmfData object that we want to turn into a QmfConsoleData. + */ + this.makeConsoleData = function(data) { + data.invokeMethod = invokeMethod; + data.refresh = refresh; + }; + + /** + * Get the AMQP address this Console is listening to. + * + * @param handler a callback method to handle the asynchronously delivered response. + * the data passed to the handler contains the console's replyTo address. Note that there are actually two, + * there's a synchronous one which is the return address for synchronous request/response type invocations and + * there's an asynchronous address with a ".async" suffix which is the return address for asynchronous invocations. + * @return returns the jQuery XHR object. See getObjects() documentation for more details on why. + * + * _console.getAddress(function(data) {console.log(data)}); // Example usage. + */ + this.getAddress = function(handler) { + return getResource("/console/address", handler); + }; + + /** + * Gets a list of all known Agents. + * + * @param handler a callback method to handle the asynchronously delivered response. + * the data passed to the handler contains a list of all known Agents as a JSON array each item in the array is + * a QMF Agent object in JSON form. + * @return returns the jQuery XHR object. See getObjects() documentation for more details on why. + * + * _console.getAgents(function(data) {console.log(data)}); // Example usage. + */ + this.getAgents = function(handler) { + return getResource("/console/agents", handler); + }; + + /** + * Gets the named Agent, if known. + * + * @param agentName the name of the Agent to be returned. + * @param handler a callback method to handle the asynchronously delivered response. + * the data passed to the handler is the QMF Agent object in JSON form. + * @return returns the jQuery XHR object. See getObjects() documentation for more details on why. + * + * _console.getAgent("qpidd", function(data) {console.log(data)}); // Example usage. + */ + this.getAgent = function(agentName, handler) { + return getResource("/console/agent/" + agentName, handler); + }; + + /** + * In this implementation findAgent() is a synonym for getAgent(). + */ + this.findAgent = function(agentName, handler) { + return getResource("/console/agent/" + agentName, handler); + }; + + /** + * Gets a list of all known Packages. + * @param handler a callback method to handle the asynchronously delivered response. + * the data passed to the handler is a list of all available packages as a JSON array. + * @return returns the jQuery XHR object. See getObjects() documentation for more details on why. + * + * _console.getPackages(function(data) {console.log(data)}); // Example usage. + */ + this.getPackages = function(handler) { + // TODO handle getPackages() for specified Agent. + return getResource("/console/packages", handler); + }; + + /** + * Gets a List of SchemaClassId for all available Schema. + * @param handler a callback method to handle the asynchronously delivered response. + * the data passed to the handler is a list of all available classes as a JSON array of SchemaClassId. + * @return returns the jQuery XHR object. See getObjects() documentation for more details on why. + * + * _console.getClasses(function(data) {console.log(data)}); // Example usage. + */ + this.getClasses = function(handler) { + // TODO handle getClasses() for specified Agent + return getResource("/console/classes", handler); + }; + + /** + * TODO + * getSchema() - should be easy as the REST API supports it. + * createSubscription() - harder as the REST API doesn't yet support it. + * refreshSubscription() - harder as the REST API doesn't yet support it. + * cancelSubscription() - harder as the REST API doesn't yet support it. + */ +}; + |