1
0
Fork 0

The new modules arangoApp and routing now are able to create an app including a context. Also the routing information is created in the routing file. Now has to be included into the routing table

This commit is contained in:
Michael Hackstein 2015-01-19 15:08:24 +01:00
parent 8121702af9
commit 620243fc18
4 changed files with 429 additions and 1622 deletions

View File

@ -1357,8 +1357,8 @@ function require (path) {
Module.prototype.createAppModule = function (app) { Module.prototype.createAppModule = function (app) {
'use strict'; 'use strict';
var libpath = fs.join(this._root, this._path); var libpath = fs.join(app._root, app._path);
if (this._manifest.hasOwnProperty("lib")) { if (app._manifest.hasOwnProperty("lib")) {
libpath = fs.join(libpath, app._manifest.lib); libpath = fs.join(libpath, app._manifest.lib);
} }
var pkg = new Package("application-package", var pkg = new Package("application-package",

View File

@ -108,7 +108,6 @@
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
var ArangoApp = function (config) { var ArangoApp = function (config) {
var collectionPrefix = this._mount.substr(1).replace(/-/g, "_").replace(/\//g, "_") + "_";
this._id = config.id; // ??? this._id = config.id; // ???
this._manifest = config.manifest; this._manifest = config.manifest;
this._name = config.manifest.name; this._name = config.manifest.name;
@ -120,11 +119,7 @@ var ArangoApp = function (config) {
this._isSystem = config.isSystem || false; this._isSystem = config.isSystem || false;
this._isDevelopment = config.isDevelopment || false; this._isDevelopment = config.isDevelopment || false;
this._exports = {}; this._exports = {};
this._collectionPrefix = this._mount.substr(1).replace(/-/g, "_").replace(/\//g, "_") + "_";
// converts the mount point into the default prefix
this._collectionPrefix = collectionPrefix;
this._context = new AppContext(this); this._context = new AppContext(this);
}; };
@ -188,7 +183,12 @@ var ArangoApp = function (config) {
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
ArangoApp.prototype.loadAppScript = function (filename, options) { ArangoApp.prototype.loadAppScript = function (filename, options) {
var appContext = _.merge(options.context || {}, this._context); var appContext;
if (options !== undefined && options.hasOwnProperty("appContext")) {
appContext = _.extend(options.appContext, this._context);
} else {
appContext = _.extend({}, this._context);
}
options = options || {}; options = options || {};

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*jshint strict: false */ /*jshint strict: false */
/*global module, require, exports */ /*global require, exports */
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief Foxx routing /// @brief Foxx routing
@ -27,381 +27,419 @@
/// @author Dr. Frank Celler /// @author Dr. Frank Celler
/// @author Copyright 2013, triAGENS GmbH, Cologne, Germany /// @author Copyright 2013, triAGENS GmbH, Cologne, Germany
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
(function() {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- private variables // --SECTION-- Imports
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////// var arangodb = require("org/arangodb");
/// @brief development mounts var preprocess = require("org/arangodb/foxx/preprocessor").preprocess;
//////////////////////////////////////////////////////////////////////////////// var _ = require("underscore");
var fs = require("fs");
var frontendDevelopmentMode = require("internal").frontendDevelopmentMode;
var console = require("console");
var DEVELOPMENTMOUNTS = null; // -----------------------------------------------------------------------------
// --SECTION-- private functions
// -----------------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
/// @brief mounted apps /// @brief excludes certain files
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
var excludeFile = function (name) {
var parts = name.split('/');
var MOUNTED_APPS = {}; if (parts.length > 0) {
var last = parts[parts.length - 1];
// ----------------------------------------------------------------------------- // exclude all files starting with .
// --SECTION-- private functions if (last[0] === '.') {
// ----------------------------------------------------------------------------- return true;
}
////////////////////////////////////////////////////////////////////////////////
/// @brief computes the routes of an app
////////////////////////////////////////////////////////////////////////////////
function routingAalApp (app, mount, options) {
"use strict";
MOUNTED_APPS[mount] = app;
var i, prefix;
if (mount === "") {
mount = "/";
}
else {
mount = arangodb.normalizeURL(mount);
}
if (mount[0] !== "/") {
console.errorLines(
"Cannot mount Foxx application: '%s'. Mount '%s' has to be absolute", app._name, mount);
return;
}
// compute the collection prefix
if (options.collectionPrefix === undefined) {
prefix = prefixFromMount(mount);
}
else {
prefix = options.collectionPrefix;
}
var defaultDocument = "index.html";
if (app._manifest.hasOwnProperty("defaultDocument")) {
defaultDocument = app._manifest.defaultDocument;
}
// setup the routes
var routes = {
urlPrefix: mount,
routes: [],
middleware: [],
context: {},
models: {},
foxx: true,
appContext: {
name: app._name, // app name
version: app._version, // app version
appId: app._id, // app identifier
mount: mount, // global mount
options: options, // options
collectionPrefix: prefix // collection prefix
} }
return false;
}; };
var p = mount; ////////////////////////////////////////////////////////////////////////////////
/// @brief builds one asset of an app
////////////////////////////////////////////////////////////////////////////////
if (p !== "/") { var buildAssetContent = function(app, assets, basePath) {
p = mount + "/"; var i, j, m;
}
if ((p + defaultDocument) !== p) { var reSub = /(.*)\/\*\*$/;
// only add redirection if src and target are not the same var reAll = /(.*)\/\*$/;
routes.routes.push({
"url" : { match: "/" }, var files = [];
"action" : {
"do" : "org/arangodb/actions/redirectRequest", for (j = 0; j < assets.length; ++j) {
"options" : { var asset = assets[j];
"permanently" : (app._id.substr(0,4) !== 'dev'), var match = reSub.exec(asset);
"destination" : defaultDocument,
"relative" : true if (match !== null) {
m = fs.listTree(fs.join(basePath, match[1]));
// files are sorted in file-system order.
// this makes the order non-portable
// we'll be sorting the files now using JS sort
// so the order is more consistent across multiple platforms
m.sort();
for (i = 0; i < m.length; ++i) {
var filename = fs.join(basePath, match[1], m[i]);
if (! excludeFile(m[i])) {
if (fs.isFile(filename)) {
files.push(filename);
}
}
} }
} }
}); else {
} match = reAll.exec(asset);
// template for app context if (match !== null) {
var devel = false; throw new Error("Not implemented");
var root; }
else {
if (app._manifest.isSystem) { if (! excludeFile(asset)) {
root = module.systemAppPath(); files.push(fs.join(basePath, asset));
}
else if (app._id.substr(0,4) === "dev:") {
devel = true;
root = module.devAppPath();
}
else {
root = module.appPath();
}
var appContextTempl = app.createAppContext();
appContextTempl.mount = mount; // global mount
appContextTempl.options = options;
appContextTempl.configuration = app._options.configuration;
appContextTempl.collectionPrefix = prefix; // collection prefix
appContextTempl.basePath = fs.join(root, app._path);
appContextTempl.baseUrl = '/_db/' + encodeURIComponent(arangodb.db._name()) + mount;
appContextTempl.isDevelopment = devel;
appContextTempl.isProduction = ! devel;
appContextTempl.manifest = app._manifest;
extendContext(appContextTempl, app, root);
var appContext;
var file;
// mount all exports
if (app._manifest.hasOwnProperty("exports")) {
var exps = app._manifest.exports;
var result, context;
for (i in exps) {
if (exps.hasOwnProperty(i)) {
file = exps[i];
result = {};
context = { exports: result };
appContext = _.extend({}, appContextTempl);
appContext.prefix = "/";
app.loadAppScript(appContext, file, { context: context });
app._exports[i] = result;
}
}
}
// mount all controllers
var controllers = app._manifest.controllers;
try {
for (i in controllers) {
if (controllers.hasOwnProperty(i)) {
file = controllers[i];
// set up a context for the application start function
appContext = _.extend({}, appContextTempl);
appContext.prefix = arangodb.normalizeURL("/" + i); // app mount
appContext.routingInfo = {};
appContext.foxxes = [];
app.loadAppScript(appContext, file, { transform: transformScript(file) });
// .............................................................................
// routingInfo
// .............................................................................
var foxxes = appContext.foxxes;
var u;
for (u = 0; u < foxxes.length; ++u) {
var foxx = foxxes[u];
var ri = foxx.routingInfo;
var rm = [ "routes", "middleware" ];
var route;
var j;
var k;
_.extend(routes.models, foxx.models);
p = ri.urlPrefix;
for (k = 0; k < rm.length; ++k) {
var key = rm[k];
if (ri.hasOwnProperty(key)) {
var rt = ri[key];
for (j = 0; j < rt.length; ++j) {
route = rt[j];
if (route.hasOwnProperty("url")) {
route.url.match = arangodb.normalizeURL(p + "/" + route.url.match);
}
route.context = i;
routes[key].push(route);
}
}
} }
} }
} }
} }
// install all files and assets var content = "";
installAssets(app, routes);
// remember mount point for (i = 0; i < files.length; ++i) {
MOUNTED_APPS[mount] = app;
// and return all routes
return routes;
}
catch (err) {
delete MOUNTED_APPS[mount];
console.errorLines(
"Cannot compute Foxx application routes: %s", String(err.stack || err));
throw err;
}
return null;
}
// -----------------------------------------------------------------------------
// --SECTION-- public functions
// -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the app routes
////////////////////////////////////////////////////////////////////////////////
exports.appRoutes = function () {
"use strict";
var aal = getStorage();
var find = aal.byExample({ type: "mount", active: true });
var routes = [];
// Variables needed in loop
var doc, appId, mount, options, app, r;
while (find.hasNext()) {
doc = find.next();
appId = doc.app;
mount = doc.mount;
options = doc.options || {};
app = createApp(appId, options);
if (app !== undefined) {
try { try {
var c = fs.read(files[i]);
r = routingAalApp(app, mount, options); content += c + "\n";
if (r === null) {
throw new Error("Cannot compute the routing table for Foxx application '"
+ app._id + "', check the log file for errors!");
}
routes.push(r);
if (! developmentMode) {
console.debug("Mounted Foxx application '%s' on '%s'", appId, mount);
}
} }
catch (err) { catch (err) {
console.error("Cannot mount Foxx application '%s': %s", appId, String(err.stack || err)); console.error("Cannot read asset '%s'", files[i]);
} }
} }
}
return routes; return content;
}; };
////////////////////////////////////////////////////////////////////////////////
/// @brief installs an asset for an app
////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////// var buildFileAsset = function(app, path, basePath, asset) {
/// @brief returns the development routes var content = buildAssetContent(app, asset.files, basePath);
//////////////////////////////////////////////////////////////////////////////// var type;
exports.developmentRoutes = function () { // .............................................................................
"use strict"; // content-type detection
// .............................................................................
var mounts = []; // contentType explicitly specified for asset
var routes = []; if (asset.hasOwnProperty("contentType") && asset.contentType !== '') {
type = asset.contentType;
}
var root = module.devAppPath(); // path contains a dot, derive content type from path
var files = fs.list(root); else if (path.match(/\.[a-zA-Z0-9]+$/)) {
var j; type = arangodb.guessContentType(path);
}
// Variables required in loop // path does not contain a dot,
var m, mf, appId, mount, options, app, r; // derive content type from included asset names
else if (asset.files.length > 0) {
type = arangodb.guessContentType(asset.files[0]);
}
for (j = 0; j < files.length; ++j) { // use built-in defaulti content-type
m = fs.join(root, files[j], "manifest.json"); else {
mf = validateManifestFile(m); type = arangodb.guessContentType("");
if (mf !== undefined) { }
appId = "dev:" + mf.name + ":" + files[j]; // .............................................................................
mount = "/dev/" + files[j]; // return content
options = { // .............................................................................
collectionPrefix : prefixFromMount(mount)
return { contentType: type, body: content };
};
////////////////////////////////////////////////////////////////////////////////
/// @brief generates development asset action
////////////////////////////////////////////////////////////////////////////////
var buildDevelopmentAssetRoute = function(app, path, basePath, asset) {
return {
url: { match: path },
action: {
callback: function (req, res) {
var c = buildFileAsset(app, path, basePath, asset);
res.contentType = c.contentType;
res.body = c.body;
}
}
};
};
////////////////////////////////////////////////////////////////////////////////
/// @brief generates asset action
////////////////////////////////////////////////////////////////////////////////
var buildAssetRoute = function (app, path, basePath, asset) {
var c = buildFileAsset(app, path, basePath, asset);
return {
url: { match: path },
content: { contentType: c.contentType, body: c.body }
};
};
////////////////////////////////////////////////////////////////////////////////
/// @brief installs the assets of an app
////////////////////////////////////////////////////////////////////////////////
var installAssets = function (app, routes) {
var path;
var desc = app._manifest;
if (! desc) {
throw new Error("Invalid application manifest");
}
var normalized;
var route;
if (desc.hasOwnProperty('assets')) {
for (path in desc.assets) {
if (desc.assets.hasOwnProperty(path)) {
var asset = desc.assets[path];
var basePath = fs.join(app._root, app._path);
if (asset.hasOwnProperty('basePath')) {
basePath = asset.basePath;
}
normalized = arangodb.normalizeURL("/" + path);
if (asset.hasOwnProperty('files')) {
if (frontendDevelopmentMode) {
route = buildDevelopmentAssetRoute(app, normalized, basePath, asset);
}
else {
route = buildAssetRoute(app, normalized, basePath, asset);
}
routes.routes.push(route);
}
}
}
}
if (desc.hasOwnProperty('files')) {
for (path in desc.files) {
if (desc.files.hasOwnProperty(path)) {
var directory = desc.files[path];
normalized = arangodb.normalizeURL("/" + path);
route = {
url: { match: normalized + "/*" },
action: {
"do": "org/arangodb/actions/pathHandler",
"options": {
root: app._root,
path: fs.join(app._path, directory)
}
}
};
routes.routes.push(route);
}
}
}
};
////////////////////////////////////////////////////////////////////////////////
/// @brief returns the transform script
////////////////////////////////////////////////////////////////////////////////
function transformScript (file) {
"use strict";
if (/\.coffee$/.test(file)) {
return function (content) {
return preprocess(content, "coffee");
}; };
app = createApp(appId, options, mf.name, m); }
if (app !== undefined) {
try {
setupApp(app, mount, options.collectionPrefix);
} catch (err) {
console.errorLines(
"Setup of App '%s' with manifest '%s' failed: %s", mf.name, m, String(err));
continue;
}
try { return preprocess;
r = routingAalApp(app, mount, options); }
} catch (err) {
console.errorLines(
"Unable to properly route the App '%s': %s", mf.name, String(err.stack || err) // -----------------------------------------------------------------------------
); // --SECTION-- public functions
continue; // -----------------------------------------------------------------------------
////////////////////////////////////////////////////////////////////////////////
/// @brief computes the routes of an app
////////////////////////////////////////////////////////////////////////////////
var routeApp = function (app) {
var i;
var mount = app._mount;
var defaultDocument = app._manifest.defaultDocument; // TODO by default "index.html"
// setup the routes
var routes = {
urlPrefix: mount,
routes: [],
middleware: [],
context: {},
models: {},
foxx: true,
appContext: {
app: app,
module: app._module // TODO
}
};
var p = mount;
var devel = app._isDevelopment;
if ((p + defaultDocument) !== p) {
// only add redirection if src and target are not the same
routes.routes.push({
"url" : { match: "/" },
"action" : {
"do" : "org/arangodb/actions/redirectRequest",
"options" : {
"permanently" : ! devel,
"destination" : defaultDocument,
"relative" : true
}
} }
if (r === null) { });
console.errorLines("Cannot compute the routing table for Foxx application '%s'" , app._id); }
continue;
var tmpContext, file;
var result, context;
// mount all exports
if (app._manifest.hasOwnProperty("exports")) {
var exps = app._manifest.exports;
for (i in exps) {
if (exps.hasOwnProperty(i)) {
file = exps[i];
result = {};
// TODO ?
context = { exports: result };
tmpContext = {prefix: "/"};
app.loadAppScript(file, { context: context, appContext: tmpContext });
app._exports[i] = result;
} }
routes.push(r);
var desc = {
_id: "dev/" + app._id,
_key: app._id,
type: "mount",
app: app._id,
name: app._name,
description: app._manifest.description,
repository: app._manifest.repository,
license: app._manifest.license,
author: app._manifest.author,
mount: mount,
active: true,
collectionPrefix: options.collectionPrefix,
isSystem: app._manifest.isSystem || false,
options: options
};
mounts.push(desc);
} }
} }
}
DEVELOPMENTMOUNTS = mounts; // mount all controllers
var controllers = app._manifest.controllers;
return routes; try {
}; for (i in controllers) {
if (controllers.hasOwnProperty(i)) {
file = controllers[i];
//////////////////////////////////////////////////////////////////////////////// // TODO ????
/// @brief returns the development mounts // set up a context for the application start function
/// tmpContext = {
/// Must be called after developmentRoutes. prefix: arangodb.normalizeURL("/" + i), // app mount
//////////////////////////////////////////////////////////////////////////////// routingInfo: {},
foxxes: []
};
exports.developmentMounts = function () { app.loadAppScript(file, {
"use strict"; transform: transformScript(file),
appContext: tmpContext
});
if (DEVELOPMENTMOUNTS === null) { // .............................................................................
exports.developmentRoutes(); // routingInfo
} // .............................................................................
return DEVELOPMENTMOUNTS; var foxxes = tmpContext.foxxes;
}; var u;
for (u = 0; u < foxxes.length; ++u) {
var foxx = foxxes[u];
var ri = foxx.routingInfo;
var rm = [ "routes", "middleware" ];
var route;
var j;
var k;
_.extend(routes.models, foxx.models);
p = ri.urlPrefix;
for (k = 0; k < rm.length; ++k) {
var key = rm[k];
if (ri.hasOwnProperty(key)) {
var rt = ri[key];
for (j = 0; j < rt.length; ++j) {
route = rt[j];
if (route.hasOwnProperty("url")) {
route.url.match = arangodb.normalizeURL(p + "/" + route.url.match);
}
route.context = i;
routes[key].push(route);
}
}
}
}
}
}
// install all files and assets
installAssets(app, routes);
// and return all routes
return routes;
}
catch (err) {
console.errorLines(
"Cannot compute Foxx application routes: %s", String(err.stack || err));
throw err;
}
return null;
};
// -----------------------------------------------------------------------------
// --SECTION-- Exports
// -----------------------------------------------------------------------------
exports.routeApp = routeApp;
}());
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// --SECTION-- END-OF-FILE // --SECTION-- END-OF-FILE
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------