1
0
Fork 0

Added optional Foxx deps.

This commit is contained in:
Alan Plum 2015-08-11 13:07:33 +02:00
parent 3029891405
commit 1031a3d4e9
6 changed files with 77 additions and 35 deletions

View File

@ -133,6 +133,10 @@ v2.7.0 (XXXX-XX-XX)
* fixed handling of optional parameters in Foxx manifest configurations * fixed handling of optional parameters in Foxx manifest configurations
* added verbose Foxx manifest dependency format
* added optional Foxx dependencies
* updated chai to 3.0. * updated chai to 3.0.

View File

@ -73,7 +73,12 @@ A more complete example for a Manifest file:
"dependencies": { "dependencies": {
"sessions": "sessions@^1.0.0", "sessions": "sessions@^1.0.0",
"systemUsers": "users" "systemUsers": "users",
"mailer": {
"name": "mailer-postmark",
"version": "*",
"required": false
}
} }
} }
``` ```
@ -159,20 +164,26 @@ The **dependencies** object maps aliases to Foxx apps:
* The **value** is a dependency definition. * The **value** is a dependency definition.
The dependency definition can use any of the following formats: The dependency definition is an object with any of the following properties:
* **name** (optional): the name of the Foxx app this app depends on.
* **version** (Default: `"*"`): a [semver](http://semver.org) version or version range of the Foxx app this app depends on.
* **required** (Default: `true`): whether the dependency is required for this app to be usable or not.
Alternatively the dependency definition can be a string using any of the following formats:
* `*` will allow using any app to be used to meet the dependency. * `*` will allow using any app to be used to meet the dependency.
* `sessions` or `sessions@*` will match any app with the name `sessions` * `sessions` or `sessions:*` will match any app with the name `sessions`
(such as the *sessions* app in the Foxx application store). (such as the *sessions* app in the Foxx application store).
* `sessions@1.0.0` will match the version `1.0.0` of any app with the name `sessions`. * `sessions:1.0.0` will match the version `1.0.0` of any app with the name `sessions`.
Instead of using a specific version number, you can also use any expression supported by Instead of using a specific version number, you can also use any expression supported by
the [semver](https://github.com/npm/node-semver) module. the [semver](https://github.com/npm/node-semver) module.
Currently the dependency definitions are not enforced in ArangoDB Currently the dependency definition names and versions are not enforced in ArangoDB
but this may change in a future version. but this may change in a future version.
If an app declares any dependencies, If an app declares any required dependencies,
you need to fulfill its dependencies before it becomes active. you need to fulfill its dependencies before it becomes active.
In the meantime a fallback application will be mounted that responds to all In the meantime a fallback application will be mounted that responds to all
requests with a HTTP 500 status code indicating a server-side error. requests with a HTTP 500 status code indicating a server-side error.

View File

@ -80,7 +80,7 @@
hasUnconfiguredDependencies: function () { hasUnconfiguredDependencies: function () {
return _.any(this.get('deps'), function (dep) { return _.any(this.get('deps'), function (dep) {
return dep.current === undefined; return dep.current === undefined && dep.definition.required !== false;
}); });
}, },

View File

@ -350,24 +350,27 @@
var tableContent = _.map(this.model.get('deps'), function(obj, name) { var tableContent = _.map(this.model.get('deps'), function(obj, name) {
var currentValue = obj.current === undefined ? '' : String(obj.current); var currentValue = obj.current === undefined ? '' : String(obj.current);
var defaultValue = ''; var defaultValue = '';
var description = obj.definition; var description = obj.definition.name;
var checks = [ if (obj.definition.version !== '*') {
{ description += '@' + obj.definition.version;
}
var checks = [{
rule: Joi.string().optional().allow(''), rule: Joi.string().optional().allow(''),
msg: 'Has to be a string.' msg: 'Has to be a string.'
}, }];
{ if (obj.definition.required) {
checks.push({
rule: Joi.string().required(), rule: Joi.string().required(),
msg: 'This value is required.' msg: 'This value is required.'
});
} }
];
return window.modalView.createTextEntry( return window.modalView.createTextEntry(
'app_deps_' + name, 'app_deps_' + name,
obj.title, obj.title,
currentValue, currentValue,
description, description,
defaultValue, defaultValue,
true, obj.definition.required,
checks checks
); );
}); });

View File

@ -289,10 +289,11 @@ ArangoApp.prototype.updateDeps = function (deps) {
if (!expected[name]) { if (!expected[name]) {
invalid.push("Unexpected dependency " + name); invalid.push("Unexpected dependency " + name);
} }
this._options.dependencies[name] = mount; this._options.dependencies[name] = mount || undefined;
}, this); }, this);
_.each(this._options.dependencies, function (mount, name) { _.each(this._options.dependencies, function (mount, name) {
if (mount) {
Object.defineProperty(this._dependencies, name, { Object.defineProperty(this._dependencies, name, {
configurable: true, configurable: true,
enumerable: true, enumerable: true,
@ -300,6 +301,7 @@ ArangoApp.prototype.updateDeps = function (deps) {
return require("org/arangodb/foxx").requireApp(mount); return require("org/arangodb/foxx").requireApp(mount);
} }
}); });
}
}, this); }, this);
return invalid; return invalid;
@ -406,7 +408,7 @@ ArangoApp.prototype.needsConfiguration = function() {
return _.any(config, function (cfg) { return _.any(config, function (cfg) {
return cfg.current === undefined && cfg.required !== false; return cfg.current === undefined && cfg.required !== false;
}) || _.any(deps, function (dep) { }) || _.any(deps, function (dep) {
return dep.current === undefined; return dep.current === undefined && dep.definition.required !== false;
}); });
}; };

View File

@ -116,7 +116,15 @@ const manifestSchema = {
dependencies: ( dependencies: (
joi.object().optional() joi.object().optional()
.pattern(RE_EMPTY, joi.forbidden()) .pattern(RE_EMPTY, joi.forbidden())
.pattern(RE_NOT_EMPTY, joi.string().required()) .pattern(RE_NOT_EMPTY, joi.alternatives().try(
joi.string().required(),
joi.object().required()
.keys({
name: joi.string().default('*'),
version: joi.string().default('*'),
required: joi.boolean().default(true)
})
))
), ),
description: joi.string().allow('').default(''), description: joi.string().allow('').default(''),
engines: ( engines: (
@ -368,6 +376,26 @@ function checkManifest(filename, manifest) {
} }
}); });
if (validationErrors.length) {
throw new ArangoError({
errorNum: errors.ERROR_INVALID_APPLICATION_MANIFEST.code,
errorMessage: validationErrors.join('\n')
});
} else {
if (manifest.dependencies) {
Object.keys(manifest.dependencies).forEach(function (key) {
const dependency = manifest.dependencies[key];
if (typeof dependency === 'string') {
const tokens = dependency.split(':');
manifest.dependencies[key] = {
name: tokens[0] || '*',
version: tokens[1] || '*',
required: true
};
}
});
}
if (typeof manifest.controllers === 'string') { if (typeof manifest.controllers === 'string') {
manifest.controllers = {'/': manifest.controllers}; manifest.controllers = {'/': manifest.controllers};
} }
@ -375,12 +403,6 @@ function checkManifest(filename, manifest) {
if (typeof manifest.tests === 'string') { if (typeof manifest.tests === 'string') {
manifest.tests = [manifest.tests]; manifest.tests = [manifest.tests];
} }
if (validationErrors.length) {
throw new ArangoError({
errorNum: errors.ERROR_INVALID_APPLICATION_MANIFEST.code,
errorMessage: validationErrors.join('\n')
});
} }
} }