From 4f6762436cdce53480d76afcf563b7ce6e320eb2 Mon Sep 17 00:00:00 2001 From: Frank Celler Date: Sun, 16 Sep 2012 17:25:57 +0200 Subject: [PATCH] added first go at user actions --- Doxygen/arango-html.doxy | 2 +- arangod/Documentation/user-manual.dox | 99 ++++++++++++++++++++++ js/actions/system/api-system.js | 5 +- js/common/bootstrap/js-print.h | 2 +- js/server/modules/org/arangodb/actions.js | 54 +++++++++++- js/server/modules/org/arangodb/examples.js | 10 ++- 6 files changed, 162 insertions(+), 10 deletions(-) diff --git a/Doxygen/arango-html.doxy b/Doxygen/arango-html.doxy index 23544d86eb..3552f2c06d 100644 --- a/Doxygen/arango-html.doxy +++ b/Doxygen/arango-html.doxy @@ -1550,7 +1550,7 @@ HIDE_UNDOC_RELATIONS = YES # toolkit from AT&T and Lucent Bell Labs. The other options in this section # have no effect if this option is set to NO (the default) -HAVE_DOT = YES +HAVE_DOT = NO # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is # allowed to run in parallel. When set to 0 (the default) doxygen will diff --git a/arangod/Documentation/user-manual.dox b/arangod/Documentation/user-manual.dox index 53ce920e07..9884003f1d 100644 --- a/arangod/Documentation/user-manual.dox +++ b/arangod/Documentation/user-manual.dox @@ -42,6 +42,7 @@ ///
  • @ref ShellEdge
  • ///
  • @ref SimpleQueries
  • ///
  • @ref Aql
  • +///
  • @ref UserManualActions
  • /// /// @latexonly\appendix@endlatexonly ///
  • @ref CommandLine
  • @@ -56,6 +57,7 @@ /// @copydetails ShellEdgeTOC /// @copydetails SimpleQueriesTOC /// @copydetails AqlTOC +/// @copydetails UserManualActionsTOC /// @copydetails CommandLineTOC /// @endif //////////////////////////////////////////////////////////////////////////////// @@ -336,6 +338,103 @@ /// JSON output format. //////////////////////////////////////////////////////////////////////////////// +// ----------------------------------------------------------------------------- +// --SECTION-- USER MANUAL ACTIONS +// ----------------------------------------------------------------------------- + +//////////////////////////////////////////////////////////////////////////////// +/// @page UserManualActionsTOC +/// +/// +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +/// @page UserManualActions Arango Actions +/// +/// Please note, that user Actions in ArangoDB are still preliminary and details +/// are subject to change. +/// +/// @EMBEDTOC{UserManualActionsTOC} +/// +/// @section UserManualActionsIntro Introduction to User Actions +//////////////////////////////////////////////////////////////// +/// +/// In same ways the ArangoDB server behaves like a Web server. It responses to +/// HTTP request by delivering JSON objects in general. The idea is that +/// sometimes it is better to store parts of the business logic within +/// AnrangoDB. For instance, if you want to apply some statistics to large +/// data-set and you cannot easily express this as query, then you can define a +/// action instead of transferring the whole data-set to the client and do the +/// computation there. Actions are also useful if you want to restrict and +/// filter data according to some complex permission system. +/// +/// The ArangoDB server deliver all kinds of information, JSON being only one +/// possible format. You can also generate HTML or images. However, a Web server +/// is normally better suited for the task as it also implements various caching +/// strategies, language selection, compression and so on. Having said that, +/// there are still situations where it might be suitable to use the ArangoDB to +/// deliver HTML pages - static or dynamic. An simple example is the built-in +/// administration interface. You can access it using any modern browser and +/// there is no need for a separate Apache or IIS. +/// +/// In the following sections we will introduce you to actions within ArangoDB +/// and how to define them. The examples start with delivering static HTML pages +/// - even if this is not the primary use-case for actions. The later sections +/// will then show you, how to code some pieces of your business logic and +/// return JSON objects. +/// +/// The interface is loosely modelled after the JavaScript classes for HTTP +/// request and responses found in node.js and the middleware/routing aspects +/// of connect.js and express.js. +/// +/// Note that unlike node.js, ArangoDB is multi-threaded and there is no easy +/// way to share state between queries inside the JavaScript engine. If such +/// state information is required, you need to use the database itself. +/// +/// @section UserManualActionsHelloWorld A Hello World +/// +/// The client API or browser sends a HTTP request to the ArangoDB server and +/// the server returns a HTTP response to the client. A HTTP requests consists +/// of a method, normally @LIT{GET} or @LIT{POST} when using a browser, and a +/// request path like "/hello/world". For a real Web server there are a zillion +/// of other thing to consider, we will ignore this for the moment. The HTTP +/// response contains a content type, describing how to interpret the returned +/// data, and the data itself. +/// +/// In the following example, we want to define action in ArangoDB, so that the +/// server returns the HTML document +/// +/// @code +/// +/// +/// Hallo World +/// +/// +/// @endcode +/// +/// if asked @LIT{GET /hello/world}. +/// +/// The server needs to know what function to call or what document to deliver +/// if it receives a request. This is called routing. All the routing information +/// of ArangoDB is stored in a collection @LIT{_routing}. Each entry in this +/// collections describes how to deal with a particular request path. +/// +/// For the above example, add the following document to the @{_routing} +/// collection: +/// +/// @code +/// arangosh> db._routing.save({ +/// ........> path : "/hello/world", +/// ........> callback: { +/// ........> type: "static", +/// ........> contentType: "text/html", +/// ........> body: "Hello" }}); +/// @endcode +/// +/// // ----------------------------------------------------------------------------- // --SECTION-- END-OF-FILE // ----------------------------------------------------------------------------- diff --git a/js/actions/system/api-system.js b/js/actions/system/api-system.js index a010996761..2b5aa47f32 100644 --- a/js/actions/system/api-system.js +++ b/js/actions/system/api-system.js @@ -72,13 +72,12 @@ function Routing (req, res) { console.error("trying callback #%d / %d: %s # %s", current, callbacks.length, typeof callback, callback); - current++; - if (callback == null) { actions.resultNotImplemented(req, res, "not implemented '" + req.suffix.join("/") + "'"); } else { - callback(req, res, next, options); + req.prefix = callback.path; + callback.func(req, res, next, options); } } diff --git a/js/common/bootstrap/js-print.h b/js/common/bootstrap/js-print.h index a33904850c..0ff184d7f1 100644 --- a/js/common/bootstrap/js-print.h +++ b/js/common/bootstrap/js-print.h @@ -54,7 +54,7 @@ static string JS_common_bootstrap_print = "////////////////////////////////////////////////////////////////////////////////\n" "/// @brief prints objects to standard output\n" "///\n" - "/// @FUN{print(@FA{arg1}, @FA{arg2}, @FA{arg3}, ...)}\n" + "/// @FUN{internal.printShell(@FA{arg1}, @FA{arg2}, @FA{arg3}, ...)}\n" "///\n" "/// Only available in shell mode.\n" "///\n" diff --git a/js/server/modules/org/arangodb/actions.js b/js/server/modules/org/arangodb/actions.js index 41074e3e38..46df00735f 100644 --- a/js/server/modules/org/arangodb/actions.js +++ b/js/server/modules/org/arangodb/actions.js @@ -56,12 +56,49 @@ var RoutingCache = {}; /// @{ //////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// @brief looks up a callback for static data +//////////////////////////////////////////////////////////////////////////////// + +function LookupCallbackStatic (callback) { + var type; + var body; + + type = callback.type || "text/plain"; + body = callback.body || ""; + + return function (req, res) { + res.responseCode = exports.HTTP_OK; + res.contentType = type; + res.body = body; + }; +} + //////////////////////////////////////////////////////////////////////////////// /// @brief looks up a callback //////////////////////////////////////////////////////////////////////////////// function LookupCallback (callback) { - if (typeof callback === "string") { + var type; + + if (typeof callback === 'object') { + if ('type' in callback) { + type = callback.type; + + if (type === "static") { + return LookupCallbackStatic(callback); + } + else { + console.error("unknown callback type '%s'", type); + return undefined; + } + } + else { + console.error("missing callback type in '%s'", JSON.stringify(callback)); + return undefined; + } + } + else if (typeof callback === "string") { var components; var module; var fn; @@ -86,7 +123,6 @@ function LookupCallback (callback) { } if (fn in module) { - console.error("lookup %s: %s", typeof module[fn], module[fn]); return module[fn]; } @@ -540,11 +576,21 @@ function Routing (method, path) { result = []; for (i = 0; i < topdown.length; ++i) { - result = result.concat(topdown[i].callback); + var td = topdown[i]; + var callback = td.callback; + + for (j = 0; j < callback.length; ++j) { + result.push({ func : callback[j], path : td.path }); + } } for (i = 0; i < bottomup.length; ++i) { - result = result.concat(bottomup[i].callback); + var bu = bottomup[i]; + var callback = bu.callback; + + for (j = 0; j < callback.length; ++j) { + result.push({ func : callback[j], path : bu.path }); + } } return result; diff --git a/js/server/modules/org/arangodb/examples.js b/js/server/modules/org/arangodb/examples.js index 31a536551b..f32d3ab397 100644 --- a/js/server/modules/org/arangodb/examples.js +++ b/js/server/modules/org/arangodb/examples.js @@ -1,7 +1,15 @@ var console = require("console"); +var actions = require("org/arangodb/actions"); exports.logRequest = function (req, res, next, options) { console.log("received request: %s", JSON.stringify(req)); + console.log("error: ", next); next(); console.log("produced response: %s", JSON.stringify(res)); -} +}; + +exports.echoRequest = function (req, res) { + res.responseCode = actions.HTTP_OK; + res.contentType = "application/json"; + res.body = JSON.stringify(req); +}; \ No newline at end of file