mirror of https://gitee.com/bigwinds/arangodb
Implement joi validation of query and path params. Fixes #936.
This commit is contained in:
parent
de7c995e4f
commit
5671bf1440
|
@ -61,7 +61,7 @@ var ALL_METHODS = [ "DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH" ]
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief function that's returned for non-implemented actions
|
/// @brief function that's returned for non-implemented actions
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
function notImplementedFunction (route, message) {
|
function notImplementedFunction (route, message) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
@ -259,7 +259,7 @@ function lookupCallbackActionCallbackString (route, action, context) {
|
||||||
catch (err) {
|
catch (err) {
|
||||||
func = errorFunction(
|
func = errorFunction(
|
||||||
route,
|
route,
|
||||||
"an error occurred while loading function '"
|
"an error occurred while loading function '"
|
||||||
+ action.callback + "': " + String(err.stack || err));
|
+ action.callback + "': " + String(err.stack || err));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -292,7 +292,7 @@ function lookupCallbackActionCallback (route, action, context) {
|
||||||
return {
|
return {
|
||||||
controller: errorFunction(
|
controller: errorFunction(
|
||||||
route,
|
route,
|
||||||
"an error occurred while loading function '"
|
"an error occurred while loading function '"
|
||||||
+ action.callback + "': unknown type '" + String(typeof action.callback) + "'"),
|
+ action.callback + "': unknown type '" + String(typeof action.callback) + "'"),
|
||||||
options: action.options || {},
|
options: action.options || {},
|
||||||
methods: action.methods || ALL_METHODS
|
methods: action.methods || ALL_METHODS
|
||||||
|
@ -331,22 +331,22 @@ function lookupCallbackActionDo (route, action) {
|
||||||
catch (err) {
|
catch (err) {
|
||||||
if (err.hasOwnProperty("moduleNotFound") && err.moduleNotFound) {
|
if (err.hasOwnProperty("moduleNotFound") && err.moduleNotFound) {
|
||||||
func = notImplementedFunction(
|
func = notImplementedFunction(
|
||||||
route,
|
route,
|
||||||
"an error occurred while loading action named '" + name
|
"an error occurred while loading action named '" + name
|
||||||
+ "' in module '" + joined + "': " + String(err.stack || err));
|
+ "' in module '" + joined + "': " + String(err.stack || err));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
func = errorFunction(
|
func = errorFunction(
|
||||||
route,
|
route,
|
||||||
"an error occurred while loading action named '" + name
|
"an error occurred while loading action named '" + name
|
||||||
+ "' in module '" + joined + "': " + String(err.stack || err));
|
+ "' in module '" + joined + "': " + String(err.stack || err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (func === null || typeof func !== 'function') {
|
if (func === null || typeof func !== 'function') {
|
||||||
func = errorFunction(
|
func = errorFunction(
|
||||||
route,
|
route,
|
||||||
"invalid definition for the action named '" + name
|
"invalid definition for the action named '" + name
|
||||||
+ "' in module '" + joined + "'");
|
+ "' in module '" + joined + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,7 +366,7 @@ function lookupCallbackActionController (route, action) {
|
||||||
|
|
||||||
var func;
|
var func;
|
||||||
var mdl;
|
var mdl;
|
||||||
var httpMethods = {
|
var httpMethods = {
|
||||||
'get': exports.GET,
|
'get': exports.GET,
|
||||||
'head': exports.HEAD,
|
'head': exports.HEAD,
|
||||||
'put': exports.PUT,
|
'put': exports.PUT,
|
||||||
|
@ -389,12 +389,12 @@ function lookupCallbackActionController (route, action) {
|
||||||
if (req.requestType === httpMethods[m] && mdl.hasOwnProperty(m)) {
|
if (req.requestType === httpMethods[m] && mdl.hasOwnProperty(m)) {
|
||||||
func = mdl[m]
|
func = mdl[m]
|
||||||
|| errorFunction(
|
|| errorFunction(
|
||||||
route,
|
route,
|
||||||
"invalid definition for " + m
|
"invalid definition for " + m
|
||||||
+ " action in action controller module '"
|
+ " action in action controller module '"
|
||||||
+ action.controller + "'");
|
+ action.controller + "'");
|
||||||
|
|
||||||
return func(req, res, options, next);
|
return func(req, res, options, next);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -418,14 +418,14 @@ function lookupCallbackActionController (route, action) {
|
||||||
catch (err) {
|
catch (err) {
|
||||||
if (err.hasOwnProperty("moduleNotFound") && err.moduleNotFound) {
|
if (err.hasOwnProperty("moduleNotFound") && err.moduleNotFound) {
|
||||||
return notImplementedFunction(
|
return notImplementedFunction(
|
||||||
route,
|
route,
|
||||||
"cannot load/execute action controller module '"
|
"cannot load/execute action controller module '"
|
||||||
+ action.controller + ": " + String(err.stack || err));
|
+ action.controller + ": " + String(err.stack || err));
|
||||||
}
|
}
|
||||||
|
|
||||||
return errorFunction(
|
return errorFunction(
|
||||||
route,
|
route,
|
||||||
"cannot load/execute action controller module '"
|
"cannot load/execute action controller module '"
|
||||||
+ action.controller + ": " + String(err.stack || err));
|
+ action.controller + ": " + String(err.stack || err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -438,7 +438,7 @@ function lookupCallbackActionPrefixController (route, action) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var prefixController = action.prefixController;
|
var prefixController = action.prefixController;
|
||||||
var httpMethods = {
|
var httpMethods = {
|
||||||
'get': exports.GET,
|
'get': exports.GET,
|
||||||
'head': exports.HEAD,
|
'head': exports.HEAD,
|
||||||
'put': exports.PUT,
|
'put': exports.PUT,
|
||||||
|
@ -496,11 +496,11 @@ function lookupCallbackActionPrefixController (route, action) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mdl.hasOwnProperty('do')) {
|
if (mdl.hasOwnProperty('do')) {
|
||||||
func = mdl['do']
|
func = mdl['do']
|
||||||
|| errorFunction(
|
|| errorFunction(
|
||||||
route,
|
route,
|
||||||
"Invalid definition for do action in prefix controller '"
|
"Invalid definition for do action in prefix controller '"
|
||||||
+ action.prefixController + "'");
|
+ action.prefixController + "'");
|
||||||
|
|
||||||
|
@ -509,7 +509,7 @@ function lookupCallbackActionPrefixController (route, action) {
|
||||||
}
|
}
|
||||||
catch (err2) {
|
catch (err2) {
|
||||||
return errorFunction(
|
return errorFunction(
|
||||||
route,
|
route,
|
||||||
"Cannot load/execute prefix controller '"
|
"Cannot load/execute prefix controller '"
|
||||||
+ action.prefixController + "': " + String(err2.stack || err2))(
|
+ action.prefixController + "': " + String(err2.stack || err2))(
|
||||||
req, res, options, next);
|
req, res, options, next);
|
||||||
|
@ -592,7 +592,7 @@ function lookupCallback (route, context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief intersect methods
|
/// @brief intersect methods
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
function intersectMethods (a, b) {
|
function intersectMethods (a, b) {
|
||||||
|
@ -611,7 +611,7 @@ function intersectMethods (a, b) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (j = 0; j < a.length; j++) {
|
for (j = 0; j < a.length; j++) {
|
||||||
var name = a[j].toUpperCase();
|
var name = a[j].toUpperCase();
|
||||||
|
|
||||||
if (d[name]) {
|
if (d[name]) {
|
||||||
results.push(name);
|
results.push(name);
|
||||||
|
@ -697,7 +697,7 @@ function defineRoutePart (route, subwhere, parts, pos, constraint, callback) {
|
||||||
for (i = 0; i < part.parameters.length; ++i) {
|
for (i = 0; i < part.parameters.length; ++i) {
|
||||||
p = part.parameters[i];
|
p = part.parameters[i];
|
||||||
subsub = { parameter: p, match: {} };
|
subsub = { parameter: p, match: {} };
|
||||||
subwhere.parameters.push(subsub);
|
subwhere.parameters.push(subsub);
|
||||||
|
|
||||||
if (constraint.hasOwnProperty(p)) {
|
if (constraint.hasOwnProperty(p)) {
|
||||||
subsub.constraint = constraint[p];
|
subsub.constraint = constraint[p];
|
||||||
|
@ -858,13 +858,13 @@ function flattenRouting (routes, path, urlParameters, depth, prefix) {
|
||||||
|
|
||||||
for (i = 0; i < sorted.length; ++i) {
|
for (i = 0; i < sorted.length; ++i) {
|
||||||
sorted[i] = {
|
sorted[i] = {
|
||||||
path: path,
|
path: path,
|
||||||
regexp: new RegExp("^" + path + "$"),
|
regexp: new RegExp("^" + path + "$"),
|
||||||
prefix: prefix,
|
prefix: prefix,
|
||||||
depth: depth,
|
depth: depth,
|
||||||
urlParameters: urlParameters,
|
urlParameters: urlParameters,
|
||||||
callback: sorted[i].callback,
|
callback: sorted[i].callback,
|
||||||
route: sorted[i].route
|
route: sorted[i].route
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -905,7 +905,7 @@ function routeRequest (req, res) {
|
||||||
|
|
||||||
execute = function () {
|
execute = function () {
|
||||||
if (action.route === undefined) {
|
if (action.route === undefined) {
|
||||||
exports.resultNotFound(req, res, arangodb.ERROR_HTTP_NOT_FOUND,
|
exports.resultNotFound(req, res, arangodb.ERROR_HTTP_NOT_FOUND,
|
||||||
"unknown path '" + path + "'");
|
"unknown path '" + path + "'");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -942,7 +942,7 @@ function routeRequest (req, res) {
|
||||||
|
|
||||||
if (func === null || typeof func !== 'function') {
|
if (func === null || typeof func !== 'function') {
|
||||||
func = exports.errorFunction(action.route,
|
func = exports.errorFunction(action.route,
|
||||||
'Invalid callback definition found for route '
|
'Invalid callback definition found for route '
|
||||||
+ JSON.stringify(action.route));
|
+ JSON.stringify(action.route));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1125,7 +1125,7 @@ function getJsonBody (req, res, code) {
|
||||||
/// @endDocuBlock
|
/// @endDocuBlock
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
function resultError (req, res, httpReturnCode, errorNum, errorMessage, headers, keyvals) {
|
function resultError (req, res, httpReturnCode, errorNum, errorMessage, headers, keyvals) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var i;
|
var i;
|
||||||
|
@ -1140,7 +1140,7 @@ function resultError (req, res, httpReturnCode, errorNum, errorMessage, headers,
|
||||||
errorMessage = errorNum;
|
errorMessage = errorNum;
|
||||||
errorNum = httpReturnCode;
|
errorNum = httpReturnCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errorMessage === undefined || errorMessage === null) {
|
if (errorMessage === undefined || errorMessage === null) {
|
||||||
msg = getErrorMessage(errorNum);
|
msg = getErrorMessage(errorNum);
|
||||||
}
|
}
|
||||||
|
@ -1162,11 +1162,11 @@ function resultError (req, res, httpReturnCode, errorNum, errorMessage, headers,
|
||||||
result.code = httpReturnCode;
|
result.code = httpReturnCode;
|
||||||
result.errorNum = errorNum;
|
result.errorNum = errorNum;
|
||||||
result.errorMessage = msg;
|
result.errorMessage = msg;
|
||||||
|
|
||||||
res.body = JSON.stringify(result);
|
res.body = JSON.stringify(result);
|
||||||
|
|
||||||
if (headers !== undefined && headers !== null) {
|
if (headers !== undefined && headers !== null) {
|
||||||
res.headers = headers;
|
res.headers = headers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1184,10 +1184,10 @@ function reloadRouting () {
|
||||||
// .............................................................................
|
// .............................................................................
|
||||||
// clear the routing cache
|
// clear the routing cache
|
||||||
// .............................................................................
|
// .............................................................................
|
||||||
|
|
||||||
// work with a local variable first
|
// work with a local variable first
|
||||||
var routingCache = { };
|
var routingCache = { };
|
||||||
|
|
||||||
routingCache.flat = {};
|
routingCache.flat = {};
|
||||||
routingCache.routes = {};
|
routingCache.routes = {};
|
||||||
routingCache.middleware = {};
|
routingCache.middleware = {};
|
||||||
|
@ -1250,7 +1250,7 @@ function reloadRouting () {
|
||||||
|
|
||||||
defineRoute(route, storage, url, callback);
|
defineRoute(route, storage, url, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
// .............................................................................
|
// .............................................................................
|
||||||
// analyses a new route
|
// analyses a new route
|
||||||
// .............................................................................
|
// .............................................................................
|
||||||
|
@ -1290,10 +1290,10 @@ function reloadRouting () {
|
||||||
for (i = 0; i < r.length; ++i) {
|
for (i = 0; i < r.length; ++i) {
|
||||||
var context = { appModule: appModule };
|
var context = { appModule: appModule };
|
||||||
|
|
||||||
installRoute(routingCache[key],
|
installRoute(routingCache[key],
|
||||||
urlPrefix,
|
urlPrefix,
|
||||||
modulePrefix,
|
modulePrefix,
|
||||||
context,
|
context,
|
||||||
r[i]);
|
r[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1341,7 +1341,7 @@ function reloadRouting () {
|
||||||
|
|
||||||
routingCache.flat[method] = b.concat(a);
|
routingCache.flat[method] = b.concat(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
// once we're done, we can set the complete routing cache for database
|
// once we're done, we can set the complete routing cache for database
|
||||||
// doing this here instead of above ensures we don't have a half-updated routing
|
// doing this here instead of above ensures we don't have a half-updated routing
|
||||||
// cache if this function fails somewhere in the middle
|
// cache if this function fails somewhere in the middle
|
||||||
|
@ -1405,11 +1405,11 @@ function firstRouting (type, parts) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var url = parts;
|
var url = parts;
|
||||||
|
|
||||||
if (undefined === RoutingCache[arangodb.db._name()]) {
|
if (undefined === RoutingCache[arangodb.db._name()]) {
|
||||||
reloadRouting();
|
reloadRouting();
|
||||||
}
|
}
|
||||||
|
|
||||||
var routingCache = RoutingCache[arangodb.db._name()];
|
var routingCache = RoutingCache[arangodb.db._name()];
|
||||||
|
|
||||||
if (typeof url === 'string') {
|
if (typeof url === 'string') {
|
||||||
|
@ -1452,7 +1452,7 @@ function firstRouting (type, parts) {
|
||||||
function badParameter (req, res, name) {
|
function badParameter (req, res, name) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
resultError(req, res, exports.HTTP_BAD, exports.HTTP_BAD,
|
resultError(req, res, exports.HTTP_BAD, exports.HTTP_BAD,
|
||||||
"invalid value for parameter '" + name + "'");
|
"invalid value for parameter '" + name + "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1469,24 +1469,24 @@ function badParameter (req, res, name) {
|
||||||
/// @endDocuBlock
|
/// @endDocuBlock
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
function resultOk (req, res, httpReturnCode, result, headers) {
|
function resultOk (req, res, httpReturnCode, result, headers) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
res.responseCode = httpReturnCode;
|
res.responseCode = httpReturnCode;
|
||||||
res.contentType = "application/json; charset=utf-8";
|
res.contentType = "application/json; charset=utf-8";
|
||||||
|
|
||||||
// add some default attributes to result
|
// add some default attributes to result
|
||||||
if (result === undefined) {
|
if (result === undefined) {
|
||||||
result = {};
|
result = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
result.error = false;
|
result.error = false;
|
||||||
result.code = httpReturnCode;
|
result.code = httpReturnCode;
|
||||||
|
|
||||||
res.body = JSON.stringify(result);
|
res.body = JSON.stringify(result);
|
||||||
|
|
||||||
if (headers !== undefined) {
|
if (headers !== undefined) {
|
||||||
res.headers = headers;
|
res.headers = headers;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1532,7 +1532,7 @@ function resultNotFound (req, res, code, msg, headers) {
|
||||||
function resultNotImplemented (req, res, msg, headers) {
|
function resultNotImplemented (req, res, msg, headers) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
resultError(req,
|
resultError(req,
|
||||||
res,
|
res,
|
||||||
exports.HTTP_NOT_IMPLEMENTED,
|
exports.HTTP_NOT_IMPLEMENTED,
|
||||||
arangodb.ERROR_NOT_IMPLEMENTED,
|
arangodb.ERROR_NOT_IMPLEMENTED,
|
||||||
|
@ -1556,11 +1556,11 @@ function resultUnsupported (req, res, headers) {
|
||||||
exports.HTTP_METHOD_NOT_ALLOWED,
|
exports.HTTP_METHOD_NOT_ALLOWED,
|
||||||
arangodb.ERROR_HTTP_METHOD_NOT_ALLOWED,
|
arangodb.ERROR_HTTP_METHOD_NOT_ALLOWED,
|
||||||
"Unsupported method",
|
"Unsupported method",
|
||||||
headers);
|
headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @brief internal function for handling redirects
|
/// @brief internal function for handling redirects
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
function handleRedirect (req, res, options, headers) {
|
function handleRedirect (req, res, options, headers) {
|
||||||
|
@ -1582,7 +1582,7 @@ function handleRedirect (req, res, options, headers) {
|
||||||
else {
|
else {
|
||||||
url += req.server.address + ":" + req.server.port;
|
url += req.server.address + ":" + req.server.port;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.relative) {
|
if (options.relative) {
|
||||||
var u = req.url;
|
var u = req.url;
|
||||||
|
|
||||||
|
@ -1637,7 +1637,7 @@ function resultPermanentRedirect (req, res, options, headers) {
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
/// @startDocuBlock actionsResultTemporaryRedirect
|
/// @startDocuBlock actionsResultTemporaryRedirect
|
||||||
///
|
///
|
||||||
/// `actions.resultTemporaryRedirect(req, res, options, headers)`
|
/// `actions.resultTemporaryRedirect(req, res, options, headers)`
|
||||||
///
|
///
|
||||||
|
@ -1649,7 +1649,7 @@ function resultTemporaryRedirect (req, res, options, headers) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
res.responseCode = exports.HTTP_TEMPORARY_REDIRECT;
|
res.responseCode = exports.HTTP_TEMPORARY_REDIRECT;
|
||||||
|
|
||||||
handleRedirect(req, res, options, headers);
|
handleRedirect(req, res, options, headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1693,7 +1693,7 @@ function resultCursor (req, res, cursor, code, options) {
|
||||||
|
|
||||||
if (hasNext) {
|
if (hasNext) {
|
||||||
cursor.persist();
|
cursor.persist();
|
||||||
cursorId = cursor.id();
|
cursorId = cursor.id();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
cursorId = null;
|
cursorId = null;
|
||||||
|
@ -1713,7 +1713,7 @@ function resultCursor (req, res, cursor, code, options) {
|
||||||
|
|
||||||
// do not use cursor after this
|
// do not use cursor after this
|
||||||
|
|
||||||
var result = {
|
var result = {
|
||||||
result : rows,
|
result : rows,
|
||||||
hasMore : hasNext
|
hasMore : hasNext
|
||||||
};
|
};
|
||||||
|
@ -1721,7 +1721,7 @@ function resultCursor (req, res, cursor, code, options) {
|
||||||
if (cursorId) {
|
if (cursorId) {
|
||||||
result.id = cursorId;
|
result.id = cursorId;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasCount) {
|
if (hasCount) {
|
||||||
result.count = count;
|
result.count = count;
|
||||||
}
|
}
|
||||||
|
@ -1798,8 +1798,8 @@ function indexNotFound (req, res, collection, index, headers) {
|
||||||
///
|
///
|
||||||
/// `actions.resultException(req, res, err, headers, verbose)`
|
/// `actions.resultException(req, res, err, headers, verbose)`
|
||||||
///
|
///
|
||||||
/// The function generates an error response. If @FA{verbose} is set to
|
/// The function generates an error response. If @FA{verbose} is set to
|
||||||
/// *true* or not specified (the default), then the error stack trace will
|
/// *true* or not specified (the default), then the error stack trace will
|
||||||
/// be included in the error message if available.
|
/// be included in the error message if available.
|
||||||
/// @endDocuBlock
|
/// @endDocuBlock
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -1827,44 +1827,44 @@ function resultException (req, res, err, headers, verbose) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err.errorMessage !== "" && ! verbose) {
|
if (err.errorMessage !== "" && ! verbose) {
|
||||||
msg = err.errorMessage;
|
msg = err.errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (num) {
|
switch (num) {
|
||||||
case arangodb.ERROR_INTERNAL:
|
case arangodb.ERROR_INTERNAL:
|
||||||
case arangodb.ERROR_OUT_OF_MEMORY:
|
case arangodb.ERROR_OUT_OF_MEMORY:
|
||||||
case arangodb.ERROR_GRAPH_TOO_MANY_ITERATIONS:
|
case arangodb.ERROR_GRAPH_TOO_MANY_ITERATIONS:
|
||||||
code = exports.HTTP_SERVER_ERROR;
|
code = exports.HTTP_SERVER_ERROR;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case arangodb.ERROR_FORBIDDEN:
|
case arangodb.ERROR_FORBIDDEN:
|
||||||
case arangodb.ERROR_ARANGO_USE_SYSTEM_DATABASE:
|
case arangodb.ERROR_ARANGO_USE_SYSTEM_DATABASE:
|
||||||
code = exports.HTTP_FORBIDDEN;
|
code = exports.HTTP_FORBIDDEN;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case arangodb.ERROR_ARANGO_COLLECTION_NOT_FOUND:
|
case arangodb.ERROR_ARANGO_COLLECTION_NOT_FOUND:
|
||||||
case arangodb.ERROR_ARANGO_DOCUMENT_NOT_FOUND:
|
case arangodb.ERROR_ARANGO_DOCUMENT_NOT_FOUND:
|
||||||
case arangodb.ERROR_ARANGO_DATABASE_NOT_FOUND:
|
case arangodb.ERROR_ARANGO_DATABASE_NOT_FOUND:
|
||||||
case arangodb.ERROR_ARANGO_ENDPOINT_NOT_FOUND:
|
case arangodb.ERROR_ARANGO_ENDPOINT_NOT_FOUND:
|
||||||
case arangodb.ERROR_ARANGO_NO_INDEX:
|
case arangodb.ERROR_ARANGO_NO_INDEX:
|
||||||
case arangodb.ERROR_ARANGO_INDEX_NOT_FOUND:
|
case arangodb.ERROR_ARANGO_INDEX_NOT_FOUND:
|
||||||
case arangodb.ERROR_CURSOR_NOT_FOUND:
|
case arangodb.ERROR_CURSOR_NOT_FOUND:
|
||||||
case arangodb.ERROR_USER_NOT_FOUND:
|
case arangodb.ERROR_USER_NOT_FOUND:
|
||||||
code = exports.HTTP_NOT_FOUND;
|
code = exports.HTTP_NOT_FOUND;
|
||||||
break;
|
|
||||||
|
|
||||||
case arangodb.ERROR_REQUEST_CANCELED:
|
|
||||||
code = exports.HTTP_REQUEST_TIMEOUT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case arangodb.ERROR_ARANGO_DUPLICATE_NAME:
|
|
||||||
case arangodb.ERROR_ARANGO_DUPLICATE_IDENTIFIER:
|
|
||||||
code = exports.HTTP_CONFLICT;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case arangodb.ERROR_CLUSTER_UNSUPPORTED:
|
case arangodb.ERROR_REQUEST_CANCELED:
|
||||||
|
code = exports.HTTP_REQUEST_TIMEOUT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case arangodb.ERROR_ARANGO_DUPLICATE_NAME:
|
||||||
|
case arangodb.ERROR_ARANGO_DUPLICATE_IDENTIFIER:
|
||||||
|
code = exports.HTTP_CONFLICT;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case arangodb.ERROR_CLUSTER_UNSUPPORTED:
|
||||||
code = exports.HTTP_NOT_IMPLEMENTED;
|
code = exports.HTTP_NOT_IMPLEMENTED;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (err instanceof TypeError) {
|
else if (err instanceof TypeError) {
|
||||||
|
@ -1875,7 +1875,7 @@ function resultException (req, res, err, headers, verbose) {
|
||||||
num = arangodb.ERROR_HTTP_SERVER_ERROR;
|
num = arangodb.ERROR_HTTP_SERVER_ERROR;
|
||||||
code = exports.HTTP_SERVER_ERROR;
|
code = exports.HTTP_SERVER_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
resultError(req, res, code, num, msg, headers);
|
resultError(req, res, code, num, msg, headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2052,12 +2052,12 @@ function addCookie (res, name, value, lifeTime, path, domain, secure, httpOnly)
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var cookie = {
|
var cookie = {
|
||||||
'name' : name,
|
'name' : name,
|
||||||
'value' : value
|
'value' : value
|
||||||
};
|
};
|
||||||
|
|
||||||
if (lifeTime !== undefined && lifeTime !== null) {
|
if (lifeTime !== undefined && lifeTime !== null) {
|
||||||
cookie.lifeTime = parseInt(lifeTime, 10);
|
cookie.lifeTime = parseInt(lifeTime, 10);
|
||||||
}
|
}
|
||||||
|
@ -2073,11 +2073,11 @@ function addCookie (res, name, value, lifeTime, path, domain, secure, httpOnly)
|
||||||
if (httpOnly !== undefined && httpOnly !== null) {
|
if (httpOnly !== undefined && httpOnly !== null) {
|
||||||
cookie.httpOnly = (httpOnly) ? true : false;
|
cookie.httpOnly = (httpOnly) ? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res.cookies === undefined || res.cookies === null) {
|
if (res.cookies === undefined || res.cookies === null) {
|
||||||
res.cookies = [];
|
res.cookies = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
res.cookies.push(cookie);
|
res.cookies.push(cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -156,8 +156,9 @@ extend(Controller.prototype, {
|
||||||
|
|
||||||
handleRequest: function (method, route, callback) {
|
handleRequest: function (method, route, callback) {
|
||||||
'use strict';
|
'use strict';
|
||||||
var newRoute = internal.constructRoute(method, route, callback, this),
|
var constraints = {queryParams: {}, urlParams: {}},
|
||||||
requestContext = new RequestContext(this.allRoutes, this.models, newRoute, this.rootElement),
|
newRoute = internal.constructRoute(method, route, callback, this, constraints),
|
||||||
|
requestContext = new RequestContext(this.allRoutes, this.models, newRoute, this.rootElement, constraints),
|
||||||
summary,
|
summary,
|
||||||
undocumentedBody;
|
undocumentedBody;
|
||||||
|
|
||||||
|
@ -482,7 +483,7 @@ extend(Controller.prototype, {
|
||||||
/// work. This includes sending a response to the user. This defaults to a function
|
/// work. This includes sending a response to the user. This defaults to a function
|
||||||
/// that sets the response to 401 and returns a JSON with *error* set to
|
/// that sets the response to 401 and returns a JSON with *error* set to
|
||||||
/// "Username or Password was wrong".
|
/// "Username or Password was wrong".
|
||||||
///
|
///
|
||||||
/// Both *onSuccess* and *onError* should take request and result as arguments.
|
/// Both *onSuccess* and *onError* should take request and result as arguments.
|
||||||
///
|
///
|
||||||
/// @EXAMPLES
|
/// @EXAMPLES
|
||||||
|
@ -510,7 +511,7 @@ extend(Controller.prototype, {
|
||||||
/// This works pretty similar to the logout function and adds a path to your
|
/// This works pretty similar to the logout function and adds a path to your
|
||||||
/// app for the logout functionality. You can customize it with a custom *onSuccess*
|
/// app for the logout functionality. You can customize it with a custom *onSuccess*
|
||||||
/// and *onError* function:
|
/// and *onError* function:
|
||||||
///
|
///
|
||||||
/// * *onSuccess* is a function that you can define to do something if the logout was
|
/// * *onSuccess* is a function that you can define to do something if the logout was
|
||||||
/// successful. This includes sending a response to the user. This defaults to a
|
/// successful. This includes sending a response to the user. This defaults to a
|
||||||
/// function that returns a JSON with *message* set to "logged out".
|
/// function that returns a JSON with *message* set to "logged out".
|
||||||
|
@ -518,7 +519,7 @@ extend(Controller.prototype, {
|
||||||
/// work. This includes sending a response to the user. This defaults to a function
|
/// work. This includes sending a response to the user. This defaults to a function
|
||||||
/// that sets the response to 401 and returns a JSON with *error* set to
|
/// that sets the response to 401 and returns a JSON with *error* set to
|
||||||
/// "No session was found".
|
/// "No session was found".
|
||||||
///
|
///
|
||||||
/// Both *onSuccess* and *onError* should take request and result as arguments.
|
/// Both *onSuccess* and *onError* should take request and result as arguments.
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
|
@ -555,9 +556,9 @@ extend(Controller.prototype, {
|
||||||
/// work. This includes sending a response to the user. This defaults to a function
|
/// work. This includes sending a response to the user. This defaults to a function
|
||||||
/// that sets the response to 401 and returns a JSON with *error* set to
|
/// that sets the response to 401 and returns a JSON with *error* set to
|
||||||
/// "Registration failed".
|
/// "Registration failed".
|
||||||
///
|
///
|
||||||
/// Both *onSuccess* and *onError* should take request and result as arguments.
|
/// Both *onSuccess* and *onError* should take request and result as arguments.
|
||||||
///
|
///
|
||||||
/// You can also set the fields containing the username and password via *usernameField*
|
/// You can also set the fields containing the username and password via *usernameField*
|
||||||
/// (defaults to *username*) and *passwordField* (defaults to *password*).
|
/// (defaults to *username*) and *passwordField* (defaults to *password*).
|
||||||
/// If you want to accept additional attributes for the user document, use the option
|
/// If you want to accept additional attributes for the user document, use the option
|
||||||
|
@ -608,7 +609,7 @@ extend(Controller.prototype, {
|
||||||
/// work. This includes sending a response to the user. This defaults to a function
|
/// work. This includes sending a response to the user. This defaults to a function
|
||||||
/// that sets the response to 401 and returns a JSON with *error* set to
|
/// that sets the response to 401 and returns a JSON with *error* set to
|
||||||
/// "No session was found".
|
/// "No session was found".
|
||||||
///
|
///
|
||||||
/// Both *onSuccess* and *onError* should take request and result as arguments.
|
/// Both *onSuccess* and *onError* should take request and result as arguments.
|
||||||
///
|
///
|
||||||
/// @EXAMPLES
|
/// @EXAMPLES
|
||||||
|
|
|
@ -28,7 +28,9 @@
|
||||||
/// @author Copyright 2013, triAGENS GmbH, Cologne, Germany
|
/// @author Copyright 2013, triAGENS GmbH, Cologne, Germany
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
var is = require("org/arangodb/is"),
|
var _ = require("underscore"),
|
||||||
|
is = require("org/arangodb/is"),
|
||||||
|
actions = require("org/arangodb/actions"),
|
||||||
constructUrlObject,
|
constructUrlObject,
|
||||||
constructNickname,
|
constructNickname,
|
||||||
constructRoute,
|
constructRoute,
|
||||||
|
@ -84,17 +86,37 @@ constructNickname = function (httpMethod, url) {
|
||||||
.toLowerCase();
|
.toLowerCase();
|
||||||
};
|
};
|
||||||
|
|
||||||
constructRoute = function (method, route, callback, controller) {
|
constructRoute = function (method, route, callback, controller, constraints) {
|
||||||
'use strict';
|
'use strict';
|
||||||
return {
|
return {
|
||||||
url: constructUrlObject(route, undefined, method),
|
url: constructUrlObject(route, undefined, method),
|
||||||
action: {
|
action: {
|
||||||
callback: function (req, res) {
|
callback: function (req, res) {
|
||||||
Object.keys(controller.injectors).forEach(function (key) {
|
if (constraints) {
|
||||||
if (Object.prototype.hasOwnProperty.call(controller.injected, key)) {
|
try {
|
||||||
|
_.each({
|
||||||
|
urlParameters: constraints.urlParams,
|
||||||
|
parameters: constraints.queryParams
|
||||||
|
}, function (paramConstraints, paramsPropertyName) {
|
||||||
|
var params = req[paramsPropertyName];
|
||||||
|
_.each(paramConstraints, function (constraint, paramName) {
|
||||||
|
var result = constraint.validate(params[paramName]);
|
||||||
|
params[paramName] = result.value;
|
||||||
|
if (result.error) {
|
||||||
|
result.error.message = 'Invalid value for "' + paramName + '": ' + result.error.message;
|
||||||
|
throw result.error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
actions.resultBad(req, res, actions.HTTP_BAD, err.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_.each(controller.injectors, function (injector, key) {
|
||||||
|
if (_.has(controller.injected, key)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var injector = controller.injectors[key];
|
|
||||||
if (typeof injector === 'function') {
|
if (typeof injector === 'function') {
|
||||||
controller.injected[key] = injector();
|
controller.injected[key] = injector();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -180,7 +180,7 @@ extend(SwaggerDocs.prototype, {
|
||||||
/// Used for documenting and constraining the routes.
|
/// Used for documenting and constraining the routes.
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
RequestContext = function (executionBuffer, models, route, rootElement) {
|
RequestContext = function (executionBuffer, models, route, rootElement, constraints) {
|
||||||
'use strict';
|
'use strict';
|
||||||
this.route = route;
|
this.route = route;
|
||||||
this.typeToRegex = {
|
this.typeToRegex = {
|
||||||
|
@ -189,6 +189,7 @@ RequestContext = function (executionBuffer, models, route, rootElement) {
|
||||||
"string": "/[^/]+/"
|
"string": "/[^/]+/"
|
||||||
};
|
};
|
||||||
this.rootElement = rootElement;
|
this.rootElement = rootElement;
|
||||||
|
this.constraints = constraints;
|
||||||
|
|
||||||
this.docs = new SwaggerDocs(this.route.docs, models);
|
this.docs = new SwaggerDocs(this.route.docs, models);
|
||||||
this.docs.addNickname(route.docs.httpMethod, route.url.match);
|
this.docs.addNickname(route.docs.httpMethod, route.url.match);
|
||||||
|
@ -202,11 +203,9 @@ extend(RequestContext.prototype, {
|
||||||
/// @startDocuBlock JSF_foxx_RequestContext_pathParam
|
/// @startDocuBlock JSF_foxx_RequestContext_pathParam
|
||||||
///
|
///
|
||||||
/// If you defined a route "/foxx/:id", you can constrain which format a path
|
/// If you defined a route "/foxx/:id", you can constrain which format a path
|
||||||
/// parameter (*/foxx/12*) can have by giving it a type. We currently support
|
/// parameter (*/foxx?a=12*) can have by giving it a *joi* type.
|
||||||
/// the following types:
|
|
||||||
///
|
///
|
||||||
/// * int
|
/// For more information on *joi* see [the official Joi documentation](https://github.com/spumko/joi).
|
||||||
/// * string
|
|
||||||
///
|
///
|
||||||
/// You can also provide a description of this parameter.
|
/// You can also provide a description of this parameter.
|
||||||
///
|
///
|
||||||
|
@ -216,8 +215,7 @@ extend(RequestContext.prototype, {
|
||||||
/// app.get("/foxx/:id", function {
|
/// app.get("/foxx/:id", function {
|
||||||
/// // Do something
|
/// // Do something
|
||||||
/// }).pathParam("id", {
|
/// }).pathParam("id", {
|
||||||
/// description: "Id of the Foxx",
|
/// type: joi.number().integer().required().description("Id of the Foxx")
|
||||||
/// type: "int"
|
|
||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
/// @endDocuBlock
|
/// @endDocuBlock
|
||||||
|
@ -226,14 +224,48 @@ extend(RequestContext.prototype, {
|
||||||
pathParam: function (paramName, attributes) {
|
pathParam: function (paramName, attributes) {
|
||||||
'use strict';
|
'use strict';
|
||||||
var url = this.route.url,
|
var url = this.route.url,
|
||||||
constraint = url.constraint || {};
|
urlConstraint = url.constraint || {},
|
||||||
|
type = attributes.type,
|
||||||
|
required = attributes.required,
|
||||||
|
description = attributes.description,
|
||||||
|
constraint = type,
|
||||||
|
regexType = type,
|
||||||
|
cfg;
|
||||||
|
|
||||||
constraint[paramName] = this.typeToRegex[attributes.type];
|
if (type && typeof type.describe === 'function') {
|
||||||
if (!constraint[paramName]) {
|
if (typeof required === 'boolean') {
|
||||||
throw new Error("Illegal attribute type: " + attributes.type);
|
constraint = required ? constraint.required() : constraint.optional();
|
||||||
|
}
|
||||||
|
if (typeof description === 'string') {
|
||||||
|
constraint = constraint.description(description);
|
||||||
|
}
|
||||||
|
this.constraints.urlParams[paramName] = constraint;
|
||||||
|
cfg = constraint.describe();
|
||||||
|
required = Boolean(cfg.flags && cfg.flags.presense === 'required');
|
||||||
|
description = cfg.description;
|
||||||
|
type = cfg.type;
|
||||||
|
if (
|
||||||
|
type === 'number' &&
|
||||||
|
_.isArray(cfg.rules) &&
|
||||||
|
_.some(cfg.rules, function (rule) {
|
||||||
|
return rule.name === 'integer';
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
type = 'integer';
|
||||||
|
}
|
||||||
|
if (_.has(this.typeToRegex, type)) {
|
||||||
|
regexType = type;
|
||||||
|
} else {
|
||||||
|
regexType = 'string';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.route.url = internal.constructUrlObject(url.match, constraint, url.methods[0]);
|
|
||||||
this.docs.addPathParam(paramName, attributes.description, attributes.type);
|
urlConstraint[paramName] = this.typeToRegex[regexType];
|
||||||
|
if (!urlConstraint[paramName]) {
|
||||||
|
throw new Error("Illegal attribute type: " + regexType);
|
||||||
|
}
|
||||||
|
this.route.url = internal.constructUrlObject(url.match, urlConstraint, url.methods[0]);
|
||||||
|
this.docs.addPathParam(paramName, description, type);
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -245,14 +277,12 @@ extend(RequestContext.prototype, {
|
||||||
/// Describe a query parameter:
|
/// Describe a query parameter:
|
||||||
///
|
///
|
||||||
/// If you defined a route "/foxx", you can constrain which format a query
|
/// If you defined a route "/foxx", you can constrain which format a query
|
||||||
/// parameter (*/foxx?a=12*) can have by giving it a type. We currently support
|
/// parameter (*/foxx?a=12*) can have by giving it a *joi* type.
|
||||||
/// the following types:
|
|
||||||
///
|
///
|
||||||
/// * int
|
/// For more information on *joi* see [the official Joi documentation](https://github.com/spumko/joi).
|
||||||
/// * string
|
|
||||||
///
|
///
|
||||||
/// You can also provide a description of this parameter, if it is required and
|
/// You can also provide a description of this parameter and
|
||||||
/// if you can provide the parameter multiple times.
|
/// whether you can provide the parameter multiple times.
|
||||||
///
|
///
|
||||||
/// @EXAMPLES
|
/// @EXAMPLES
|
||||||
///
|
///
|
||||||
|
@ -260,9 +290,7 @@ extend(RequestContext.prototype, {
|
||||||
/// app.get("/foxx", function {
|
/// app.get("/foxx", function {
|
||||||
/// // Do something
|
/// // Do something
|
||||||
/// }).queryParam("id", {
|
/// }).queryParam("id", {
|
||||||
/// description: "Id of the Foxx",
|
/// type: joi.number().integer().required().description("Id of the Foxx"),
|
||||||
/// type: "int",
|
|
||||||
/// required: true,
|
|
||||||
/// allowMultiple: false
|
/// allowMultiple: false
|
||||||
/// });
|
/// });
|
||||||
/// ```
|
/// ```
|
||||||
|
@ -271,11 +299,40 @@ extend(RequestContext.prototype, {
|
||||||
|
|
||||||
queryParam: function (paramName, attributes) {
|
queryParam: function (paramName, attributes) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
var type = attributes.type,
|
||||||
|
required = attributes.required,
|
||||||
|
description = attributes.description,
|
||||||
|
constraint = type,
|
||||||
|
cfg;
|
||||||
|
|
||||||
|
if (type && typeof type.describe === 'function') {
|
||||||
|
if (typeof required === 'boolean') {
|
||||||
|
constraint = required ? constraint.required() : constraint.optional();
|
||||||
|
}
|
||||||
|
if (typeof description === 'string') {
|
||||||
|
constraint = constraint.description(description);
|
||||||
|
}
|
||||||
|
this.constraints.queryParams[paramName] = constraint;
|
||||||
|
cfg = constraint.describe();
|
||||||
|
required = Boolean(cfg.flags && cfg.flags.presense === 'required');
|
||||||
|
description = cfg.description;
|
||||||
|
type = cfg.type;
|
||||||
|
if (
|
||||||
|
type === 'number' &&
|
||||||
|
_.isArray(cfg.rules) &&
|
||||||
|
_.some(cfg.rules, function (rule) {
|
||||||
|
return rule.name === 'integer';
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
type = 'integer';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.docs.addQueryParam(
|
this.docs.addQueryParam(
|
||||||
paramName,
|
paramName,
|
||||||
attributes.description,
|
description,
|
||||||
attributes.type,
|
type,
|
||||||
attributes.required,
|
required,
|
||||||
attributes.allowMultiple
|
attributes.allowMultiple
|
||||||
);
|
);
|
||||||
return this;
|
return this;
|
||||||
|
|
Loading…
Reference in New Issue