1
0
Fork 0

Merge branch 'devel' of https://github.com/triAGENS/ArangoDB into devel

This commit is contained in:
a-brandt 2012-10-24 10:47:21 +02:00
commit ba5a98e613
3 changed files with 99 additions and 90 deletions

View File

@ -398,7 +398,7 @@
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
/// ///
/// In some ways the communication layer of the ArangoDB server behaves like a /// In some ways the communication layer of the ArangoDB server behaves like a
/// Web server. Unlike a Web server, it normally responses to HTTP requests by /// Web server. Unlike a Web server, it normally responds to HTTP requests by
/// delivering JSON objects. Remember, documents in the database are just JSON /// delivering JSON objects. Remember, documents in the database are just JSON
/// objects. So, most of the time the HTTP response will contain a JSON document /// objects. So, most of the time the HTTP response will contain a JSON document
/// from the database as body. You can extract the documents stored in the /// from the database as body. You can extract the documents stored in the
@ -436,7 +436,7 @@
/// The following sections will explain actions within ArangoDB and show how to /// The following sections will explain actions within ArangoDB and show how to
/// define them. The examples start with delivering static HTML pages - even if /// 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 /// 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 /// show you how to code some pieces of your business logic and return JSON
/// objects. /// objects.
/// ///
/// The interface is loosely modelled after the JavaScript classes for HTTP /// The interface is loosely modelled after the JavaScript classes for HTTP
@ -458,7 +458,7 @@
/// response contains a content type, describing how to interpret the returned /// response contains a content type, describing how to interpret the returned
/// data, and the data itself. /// data, and the data itself.
/// ///
/// In the following example, we want to define action in ArangoDB, so that the /// In the following example, we want to define an action in ArangoDB, so that the
/// server returns the HTML document /// server returns the HTML document
/// ///
/// @code /// @code
@ -576,7 +576,7 @@
/// @subsection UserManualActionsMatchesConstraint Constraint Match /// @subsection UserManualActionsMatchesConstraint Constraint Match
/// ///
/// A constraint match is similar to a parameterized match, but the parameters /// A constraint match is similar to a parameterized match, but the parameters
/// carries a constraint. /// can carry constraints.
/// ///
/// If the definition is /// If the definition is
/// ///
@ -753,7 +753,7 @@
/// @code /// @code
/// arangosh> db._routing.save({ /// arangosh> db._routing.save({
/// ........> url: "/hello/echo", /// ........> url: "/hello/echo",
/// ........> action: "org/arangodb/actions/echoRequest" }); /// ........> action: { controller: "org/arangodb/actions", do: "echoRequest" } });
/// @endcode /// @endcode
/// ///
/// Reload the routing and check /// Reload the routing and check
@ -874,7 +874,7 @@
/// @code /// @code
/// arangosh> db._routing.save({ /// arangosh> db._routing.save({
/// ........> url: "/echo", /// ........> url: "/echo",
/// ........> action: "org/arangodb/actions/echoRequest" }); /// ........> action: { controller: "org/arangodb/actions", do: "echoRequest" } });
/// @endcode /// @endcode
/// ///
/// Reload the routing and check /// Reload the routing and check
@ -907,29 +907,14 @@
/// } /// }
/// @endcode /// @endcode
/// ///
/// Note that /// You may also pass options to the called function:
///
/// @code
/// arangosh> db._routing.save({
/// ........> url: "/echo",
/// ........> action: "org/arangodb/actions/echoRequest" });
/// @endcode
///
/// is a short-cut for
///
/// @code
/// arangosh> db._routing.save({
/// ........> url: "/echo",
/// ........> action: { do: "org/arangodb/actions/echoRequest" } });
/// @endcode
///
/// The latter form allows you to pass options to the called function:
/// ///
/// @code /// @code
/// arangosh> db._routing.save({ /// arangosh> db._routing.save({
/// ........> url: "/echo", /// ........> url: "/echo",
/// ........> action: { /// ........> action: {
/// ........> do: "org/arangodb/actions/echoRequest", /// ........> controller: "org/arangodb/actions",
/// ........> do: "echoRequest",
/// ........> options: { "Hallo": "World" } } }); /// ........> options: { "Hallo": "World" } } });
/// @endcode /// @endcode
/// ///
@ -938,11 +923,10 @@
/// @code /// @code
/// { /// {
/// "request": { /// "request": {
/// "prefix": "/hello/echo-long",
/// ... /// ...
/// }, /// },
/// "options": { /// "options": {
/// "option": "my option1" /// "Hallo": "world"
/// } /// }
/// } /// }
/// @endcode /// @endcode
@ -960,7 +944,8 @@
/// arangosh> db._routing.save({ /// arangosh> db._routing.save({
/// ........> url: "/", /// ........> url: "/",
/// ........> action: { /// ........> action: {
/// ........> do: "org/arangodb/actions/redirectRequest", /// ........> controller: "org/arangodb/actions",
/// ........> do: "redirectRequest",
/// ........> options: { /// ........> options: {
/// ........> permanently: true, /// ........> permanently: true,
/// ........> destination: "http://somewhere.else/" } } }); /// ........> destination: "http://somewhere.else/" } } });
@ -1015,14 +1000,14 @@
/// }; /// };
/// @endcode /// @endcode
/// ///
/// This functions is available as @LIT{org/arangodb/actions/logRequest}. /// This function is available as @LIT{org/arangodb/actions/logRequest}.
/// You need to tell ArangoDB that it is should use a prefix match and /// You need to tell ArangoDB that it is should use a prefix match and
/// that the shortest match should win in this case: /// that the shortest match should win in this case:
/// ///
/// @code /// @code
/// arangosh> db._routing.save({ /// arangosh> db._routing.save({
/// ........> middleware: [ /// ........> middleware: [
/// ........> { url: { match: "/*" }, action: "org/arangodb/actions/logRequest" } /// ........> { url: { match: "/*" }, action: { controller: "org/arangodb/actions", do: "logRequest" } }
/// ........> ] /// ........> ]
/// ........> }); /// ........> });
/// @endcode /// @endcode

View File

@ -65,6 +65,38 @@ var RoutingCache;
/// @{ /// @{
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// @brief function that's returned for non-implemented actions
////////////////////////////////////////////////////////////////////////////////
function notImplementedFunction (triggerRoute, message) {
message += "\nThis error is triggered by the following route " + JSON.stringify(triggerRoute);
console.error(message);
return function (req, res, options, next) {
res.responseCode = exports.HTTP_NOT_IMPLEMENTED;
res.contentType = "text/plain";
res.body = message;
};
}
////////////////////////////////////////////////////////////////////////////////
/// @brief function that's returned for actions that produce an error
////////////////////////////////////////////////////////////////////////////////
function errorFunction (triggerRoute, message) {
message += "\nThis error is triggered by the following route " + JSON.stringify(triggerRoute);
console.error(message);
return function (req, res, options, next) {
res.responseCode = exports.HTTP_SERVER_ERROR;
res.contentType = "text/plain";
res.body = message;
};
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief splits an URL into parts /// @brief splits an URL into parts
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
@ -181,14 +213,22 @@ function lookupCallbackStatic (content) {
/// @brief looks up a callback for an action /// @brief looks up a callback for an action
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
function lookupCallbackAction (action) { function lookupCallbackAction (route, action) {
var path; var path;
var name; var name;
var func; var func;
var module; var module;
var httpMethods = {
'get': exports.GET,
'head': exports.HEAD,
'put': exports.PUT,
'post': exports.POST,
'delete': exports.DELETE,
'patch': exports.PATCH
};
if (typeof action === 'string') { if (typeof action === 'string') {
return lookupCallbackAction({ prefixController: action }); return lookupCallbackAction(route, { prefixController: action });
} }
if (action.hasOwnProperty('do')) { if (action.hasOwnProperty('do')) {
@ -203,17 +243,15 @@ function lookupCallbackAction (action) {
func = module[name]; func = module[name];
} }
else { else {
console.error("cannot find action named '%s' in module '%s'", name, path.join("/")); func = notImplementedFunction(route, "Could not find action named '" + name + "' in module '" + path.join("/") + "'");
} }
} }
catch (err) { catch (err) {
console.error("cannot find action named '%s' in module '%s': %s", func = errorFunction(route, "An error occurred while loading action named '" + name + "' in module '" + path.join("/") + "': " + String(err));
name, path.join("/"), String(err));
return null;
} }
if (func === null || typeof func !== 'function') { if (func === null || typeof func !== 'function') {
return null; func = errorFunction(route, "Invalid definition for the action named '" + name + "' in module '" + path.join("/") + "'");
} }
return { return {
@ -229,32 +267,25 @@ function lookupCallbackAction (action) {
return { return {
controller: function (req, res, options, next) { controller: function (req, res, options, next) {
if (req.requestType === exports.GET && module.hasOwnProperty('get')) { // enum all HTTP methods
return module['get'](req, res, options, next); for (var m in httpMethods) {
if (! httpMethods.hasOwnProperty(m)) {
continue;
} }
if (req.requestType === exports.HEAD && module.hasOwnProperty('head')) { if (req.requestType == httpMethods[m] && module.hasOwnProperty(m)) {
return module['head'](req, res, options, next); func = module[m] ||
} errorFunction(route, "Invalid definition for " + m + " action in action controller module '" + action.controller + "'");
if (req.requestType === exports.PUT && module.hasOwnProperty('put')) { return func(req, res, options, next);
return module['put'](req, res, options, next);
} }
if (req.requestType === exports.POST && module.hasOwnProperty('post')) {
return module['post'](req, res, options, next);
}
if (req.requestType === exports.DELETE && module.hasOwnProperty('delete')) {
return module['delete'](req, res, options, next);
}
if (req.requestType === exports.PATCH && module.hasOwnProperty('patch')) {
return module['patch'](req, res, options, next);
} }
if (module.hasOwnProperty('do')) { if (module.hasOwnProperty('do')) {
return module['do'](req, res, options, next); func = module['do'] ||
errorFunction(route, "Invalid definition for do action in action controller module '" + action.controller + "'");
return func(req, res, options, next);
} }
next(); next();
@ -264,9 +295,7 @@ function lookupCallbackAction (action) {
}; };
} }
catch (err) { catch (err) {
console.error("cannot load action controller module '%s': %s", return errorFunction(route, "cannot load/execute action controller module '" + action.controller + ": " + String(err));
action.controller, String(err));
return null;
} }
} }
@ -286,37 +315,32 @@ function lookupCallbackAction (action) {
} }
} }
catch (err) { catch (err) {
console.error("cannot load prefix controller: %s", String(err)); return errorFunction(route, "cannot load prefix controller: " + String(err))(req, res, options, next);
next();
return;
} }
if (req.requestType === exports.GET && module.hasOwnProperty('get')) { try {
return module['get'](req, res, options, next); // enum all HTTP methods
for (var m in httpMethods) {
if (! httpMethods.hasOwnProperty(m)) {
continue;
} }
if (req.requestType === exports.HEAD && module.hasOwnProperty('head')) { if (req.requestType == httpMethods[m] && module.hasOwnProperty(m)) {
return module['head'](req, res, options, next); func = module[m] ||
} errorFunction(route, "Invalid definition for " + m + " action in prefix controller '" + action.prefixController + "'");
if (req.requestType === exports.PUT && module.hasOwnProperty('put')) { return func(req, res, options, next);
return module['put'](req, res, options, next);
} }
if (req.requestType === exports.POST && module.hasOwnProperty('post')) {
return module['post'](req, res, options, next);
}
if (req.requestType === exports.DELETE && module.hasOwnProperty('delete')) {
return module['delete'](req, res, options, next);
}
if (req.requestType === exports.PATCH && module.hasOwnProperty('patch')) {
return module['patch'](req, res, options, next);
} }
if (module.hasOwnProperty('do')) { if (module.hasOwnProperty('do')) {
return module['do'](req, res, options, next); func = module['do'] ||
errorFunction(route, "Invalid definition for do action in prefix controller '" + action.prefixController + "'");
return func(req, res, options, next);
}
}
catch (err) {
return errorFunction(route, "Cannot load/execute prefix controller '" + action.prefixController + "': " + String(err))(req, res, options, next);
} }
next(); next();
@ -340,7 +364,7 @@ function lookupCallback (route) {
result = lookupCallbackStatic(route.content); result = lookupCallbackStatic(route.content);
} }
else if (route.hasOwnProperty('action')) { else if (route.hasOwnProperty('action')) {
result = lookupCallbackAction(route.action); result = lookupCallbackAction(route, route.action);
} }
return result; return result;
@ -819,7 +843,7 @@ function reloadRouting () {
// ............................................................................. // .............................................................................
RoutingCache = {}; RoutingCache = {};
RoutingCache.flat = {};
RoutingCache.routes = {}; RoutingCache.routes = {};
RoutingCache.middleware = {}; RoutingCache.middleware = {};
@ -985,7 +1009,7 @@ function firstRouting (type, parts) {
url = "/" + parts.join("/"); url = "/" + parts.join("/");
} }
if (! RoutingCache.flat.hasOwnProperty(type)) { if (! RoutingCache.flat || ! RoutingCache.flat.hasOwnProperty(type)) {
return { return {
parts: parts, parts: parts,
position: -1, position: -1,

View File

@ -36,7 +36,7 @@ exports.head = function (req, res, next, options) {
exports.do = function (req, res, next, options) { exports.do = function (req, res, next, options) {
res.responseCode = actions.HTTP_OK; res.responseCode = actions.HTTP_OK;
res.contentType = "application/json; charset=utf-8"; res.contentType = "application/json; charset=utf-8";
res.body = JSON.stringify(req); res.body = JSON.stringify( { "request" : req, "options" : options });
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------