merge from sl-origin master

This commit is contained in:
Doped Dude 2016-06-28 22:43:46 +05:30
commit b3d1a2f4a7
40 changed files with 1017 additions and 603 deletions

View File

@ -1,3 +1,2 @@
node_modules/
coverage/
test/sandbox/

3
.eslintrc Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "loopback"
}

25
.jscsrc
View File

@ -1,25 +0,0 @@
{
"preset": "google",
"requireCurlyBraces": [
"do",
"try",
"catch"
],
"disallowMultipleVarDecl": "exceptUndefined",
"disallowSpacesInsideObjectBrackets": null,
"requireSpaceAfterLineComment": true,
"maximumLineLength": {
"value": 80,
"allowRegex": true
},
"validateJSDoc": {
"checkParamNames": false,
"checkRedundantParams": false,
"requireParamTypes": true
},
"excludeFiles": [
"node_modules/**",
"coverage/**",
"test/sandbox/**"
]
}

View File

@ -1,21 +0,0 @@
{
"node": true,
"browser": true,
"eqnull" : true,
"indent": 2,
"undef": true,
"unused": true,
"quotmark": "single",
"newcap": true,
"nonew": true,
"sub": true,
"unused": "vars",
"globals": {
"describe": false,
"it": false,
"before": false,
"beforeEach": false,
"after": false,
"afterEach": false
}
}

View File

@ -1,3 +1,35 @@
2016-06-20, Version 2.19.0
==========================
* update copyright notices and license (Ryan Graham)
* Add flag var lazyConnect to ds config (juehou)
2016-04-13, Version 2.18.1
==========================
* parse config: should ignore null values (Loïc Mahieu)
2016-04-07, Version 2.18.0
==========================
* Dynamic datasources.json from ENV and config.json (David Cheung)
* Use eslint with loopback config (Miroslav Bajtoš)
2016-02-23, Version 2.17.0
==========================
* executor: move "booted" and cb() to the next tick (Miroslav Bajtoš)
* Fix lodash 4.0.0 breaking changes (Jérémie Drouet)
* When config is overriden with null don't merge (Farid Neshat)
2015-12-22, Version 2.16.0
==========================
@ -314,8 +346,6 @@
2014-07-17, Version v2.0.0-beta3
================================
* v2.0.0-beta3 (Miroslav Bajtoš)
* compiler: return a clone of instructions (Miroslav Bajtoš)
@ -334,8 +364,6 @@
2014-06-26, Version 2.0.0-beta1
===============================
* 2.0.0-beta1 (Miroslav Bajtoš)
* test: fix jshint warnings (Miroslav Bajtoš)
* compiler: fix references to loopback (Miroslav Bajtoš)

25
LICENSE Normal file
View File

@ -0,0 +1,25 @@
Copyright (c) IBM Corp. 2014,2016. All Rights Reserved.
Node module: loopback-boot
This project is licensed under the MIT License, full text below.
--------
MIT license
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -1,9 +0,0 @@
Copyright (c) 2013-2015 StrongLoop, Inc and other contributors.
loopback-boot uses a dual license model.
You may use this library under the terms of the [MIT License][],
or under the terms of the [StrongLoop Subscription Agreement][].
[MIT License]: http://opensource.org/licenses/MIT
[StrongLoop Subscription Agreement]: http://strongloop.com/license

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
var execute = require('./lib/executor');
/**

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
var ConfigLoader = require('./lib/config-loader');
var compile = require('./lib/compiler');
var execute = require('./lib/executor');
@ -154,7 +159,7 @@ exports.compileToBrowserify = function(options, bundler) {
addInstructionsToBrowserify(compile(options), bundler);
};
/*-- undocumented low-level API --*/
/* -- undocumented low-level API -- */
exports.ConfigLoader = ConfigLoader;
exports.compile = compile;

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
var fs = require('fs');
var path = require('path');
var commondir = require('commondir');

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
var assert = require('assert');
var cloneDeep = require('lodash').cloneDeep;
var fs = require('fs');
@ -102,8 +107,8 @@ module.exports = function compile(options) {
components: componentInstructions,
mixins: mixinInstructions,
files: {
boot: bootScripts
}
boot: bootScripts,
},
};
if (options.appId)
@ -214,7 +219,7 @@ function buildAllModelInstructions(rootDir, modelsConfig, sources,
name: name,
config: config,
definition: definition.definition,
sourceFile: definition.sourceFile
sourceFile: definition.sourceFile,
};
});
@ -414,7 +419,6 @@ function tryResolveAppPath(rootDir, relativePath, resolveOptions) {
try {
// NOTE(bajtos) We need to create a proper String object here,
// otherwise we can't attach additional properties to it
/*jshint -W053 */
var filePath = new String(require.resolve(absPath));
filePath.unresolvedPath = absPath;
return filePath;
@ -456,7 +460,7 @@ function loadModelDefinition(rootDir, jsonFile, allFiles) {
return {
definition: definition,
sourceFile: sourceFile
sourceFile: sourceFile,
};
}
@ -494,7 +498,7 @@ function buildMiddlewareInstructions(rootDir, config) {
var item = {
sourceFile: resolved.sourceFile,
config: middlewareConfig
config: middlewareConfig,
};
if (resolved.fragment) {
item.fragment = resolved.fragment;
@ -516,13 +520,13 @@ function buildMiddlewareInstructions(rootDir, config) {
return {
phases: flattenedPhaseNames,
middleware: middlewareList
middleware: middlewareList,
};
}
function resolveMiddlewarePath(rootDir, middleware, config) {
var resolved = {
optional: !!config.optional
optional: !!config.optional,
};
var segments = middleware.split('#');
@ -531,7 +535,7 @@ function resolveMiddlewarePath(rootDir, middleware, config) {
var middlewarePath = pathName;
var opts = {
strict: true,
optional: !!config.optional
optional: !!config.optional,
};
if (fragment) {
@ -551,7 +555,7 @@ function resolveMiddlewarePath(rootDir, middleware, config) {
// node_modules/strong-express-metrics
// instead of
// node_modules/strong-express-metrics/index.js
fullResolve: false
fullResolve: false,
});
var sourceFile = resolveAppScriptPath(rootDir, middlewarePath, resolveOpts);
@ -580,14 +584,13 @@ function resolveMiddlewarePath(rootDir, middleware, config) {
// pathName + '/' + fragment
];
var err;
var err = undefined; // see https://github.com/eslint/eslint/issues/5744
for (var ix in candidates) {
try {
resolved.sourceFile = resolveAppScriptPath(rootDir, candidates[ix], opts);
delete resolved.fragment;
return resolved;
}
catch (e) {
} catch (e) {
// Report the error for the first candidate when no candidate matches
if (!err) err = e;
}
@ -614,7 +617,7 @@ function buildComponentInstructions(rootDir, componentConfig) {
.map(function(name) {
return {
sourceFile: resolveAppScriptPath(rootDir, name, { strict: true }),
config: componentConfig[name]
config: componentConfig[name],
};
});
}
@ -634,11 +637,11 @@ function resolveRelativePaths(relativePaths, appRootDir) {
function getExcludedExtensions() {
return {
'.json': '.json',
'.node': 'node'
'.node': 'node',
};
}
function isPreferredExtension (filename) {
function isPreferredExtension(filename) {
var includeExtensions = require.extensions;
var ext = path.extname(filename);
@ -792,7 +795,9 @@ function normalizeMixinName(str, options) {
case 'classify':
str = String(str).replace(/([A-Z]+)/g, ' $1').trim();
str = String(str).replace(/[\W_]/g, ' ').toLowerCase();
str = str.replace(/(?:^|\s|-)\S/g, function(c) { return c.toUpperCase(); });
str = str.replace(/(?:^|\s|-)\S/g, function(c) {
return c.toUpperCase();
});
str = str.replace(/\s+/g, '');
return str;

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
var fs = require('fs');
var path = require('path');
var debug = require('debug')('loopback:boot:config-loader');
@ -32,7 +37,6 @@ ConfigLoader.loadDataSources = function(rootDir, env) {
* @returns {Object}
*/
ConfigLoader.loadModels = function(rootDir, env) {
/*jshint unused:false */
return loadNamed(rootDir, env, 'model-config', mergeModelConfig);
};
@ -98,7 +102,7 @@ function findConfigFiles(appRootDir, env, name) {
var candidates = [
master,
ifExistsWithAnyExt(name + '.local'),
ifExistsWithAnyExt(name + '.' + env)
ifExistsWithAnyExt(name + '.' + env),
];
return candidates.filter(function(c) { return c !== undefined; });
@ -124,7 +128,7 @@ function loadConfigFiles(files) {
var config = require(f);
Object.defineProperty(config, '_filename', {
enumerable: false,
value: f
value: f,
});
return config;
});
@ -166,7 +170,7 @@ function mergeAppConfig(target, config, fileName) {
}
function mergeMiddlewareConfig(target, config, fileName) {
var err;
var err = undefined; // see https://github.com/eslint/eslint/issues/5744
for (var phase in config) {
if (phase in target) {
err = mergePhaseConfig(target[phase], config[phase], phase);
@ -203,7 +207,7 @@ function mergeNamedItems(arr1, arr2, key) {
}
function mergePhaseConfig(target, config, phase) {
var err;
var err = undefined; // see https://github.com/eslint/eslint/issues/5744
for (var mw in config) {
if (mw in target) {
var targetMiddleware = target[mw];

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
var assert = require('assert');
var semver = require('semver');
var debug = require('debug')('loopback:boot:executor');
@ -43,15 +48,20 @@ module.exports = function execute(app, instructions, callback) {
function(done) {
enableAnonymousSwagger(app, instructions);
done();
}], function(err) {
app.booting = false;
},
// Ensure both the "booted" event and the callback are always called
// in the next tick of the even loop.
// See http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony
process.nextTick,
], function(err) {
app.booting = false;
if (err) return callback(err);
if (err) return callback(err);
app.emit('booted');
app.emit('booted');
callback();
});
callback();
});
};
function patchAppLoopback(app) {
@ -123,7 +133,7 @@ function setPort(app, instructions) {
instructions.config.port,
process.env.npm_package_config_port,
app.get('port'),
3000
3000,
], function(p) {
return p != null;
});
@ -166,6 +176,15 @@ function applyAppConfig(app, instructions) {
function setupDataSources(app, instructions) {
forEachKeyedObject(instructions.dataSources, function(key, obj) {
var opts = {
useEnvVars: true,
};
obj = getUpdatedConfigObject(app, obj, opts);
var lazyConnect = process.env.LB_LAZYCONNECT_DATASOURCES;
if (lazyConnect) {
obj.lazyConnect =
lazyConnect === 'false' || lazyConnect === '0' ? false : true;
}
app.dataSource(key, obj);
});
}
@ -272,7 +291,7 @@ function runScripts(app, list, callback) {
debug('Exported function detected %s', filepath);
functions.push({
path: filepath,
func: exports
func: exports,
});
}
} catch (err) {
@ -325,24 +344,42 @@ function setupMiddleware(app, instructions) {
}
assert(typeof factory === 'function',
'Middleware factory must be a function');
data.config = getUpdatedConfigObject(app, data.config);
var opts = {
useEnvVars: true,
};
data.config = getUpdatedConfigObject(app, data.config, opts);
app.middlewareFromConfig(factory, data.config);
});
}
function getUpdatedConfigObject(app, config) {
function getUpdatedConfigObject(app, config, opts) {
var DYNAMIC_CONFIG_PARAM = /\$\{(\w+)\}$/;
var useEnvVars = opts && opts.useEnvVars;
function getConfigVariable(param) {
var configVariable = param;
var match = configVariable.match(DYNAMIC_CONFIG_PARAM);
if (match) {
var appValue = app.get(match[1]);
if (appValue !== undefined) {
var varName = match[1];
if (useEnvVars && process.env[varName] !== undefined) {
debug('Dynamic Configuration: Resolved via process.env: %s as %s',
process.env[varName], param);
configVariable = process.env[varName];
} else if (app.get(varName) !== undefined) {
debug('Dynamic Configuration: Resolved via app.get(): %s as %s',
app.get(varName), param);
var appValue = app.get(varName);
configVariable = appValue;
} else {
console.warn('%s does not resolve to a valid value. ' +
'"%s" must be resolvable by app.get().', param, match[1]);
// previously it returns the original string such as "${restApiRoot}"
// it will now return `undefined`, for the use case of
// dynamic datasources url:`undefined` to fallback to other parameters
configVariable = undefined;
console.warn('%s does not resolve to a valid value, returned as %s. ' +
'"%s" must be resolvable in Environment variable or by app.get().',
param, configVariable, varName);
debug('Dynamic Configuration: Cannot resolve variable for `%s`, ' +
'returned as %s', varName, configVariable);
}
}
return configVariable;
@ -373,6 +410,8 @@ function getUpdatedConfigObject(app, config) {
interpolated[configKey] = value.map(interpolateVariables);
} else if (typeof value === 'string') {
interpolated[configKey] = getConfigVariable(value);
} else if (value === null) {
interpolated[configKey] = value;
} else if (typeof value === 'object' && Object.keys(value).length) {
interpolated[configKey] = interpolateVariables(value);
} else {
@ -389,7 +428,10 @@ function setupComponents(app, instructions) {
instructions.components.forEach(function(data) {
debug('Configuring component %j', data.sourceFile);
var configFn = require(data.sourceFile);
data.config = getUpdatedConfigObject(app, data.config);
var opts = {
useEnvVars: true,
};
data.config = getUpdatedConfigObject(app, data.config, opts);
configFn(app, data.config);
});
}

View File

@ -1,6 +1,6 @@
{
"name": "loopback-boot",
"version": "2.16.0",
"version": "2.19.0",
"description": "Convention-based bootstrapper for LoopBack applications",
"keywords": [
"StrongLoop",
@ -15,8 +15,9 @@
"main": "index.js",
"browser": "browser.js",
"scripts": {
"pretest": "jscs . && jshint .",
"test": "mocha"
"test": "mocha",
"posttest": "npm run lint",
"lint": "eslint ."
},
"license": "MIT",
"dependencies": {
@ -32,9 +33,9 @@
"chai": "^1.10.0",
"coffee-script": "^1.8.0",
"coffeeify": "^0.7.0",
"eslint": "^2.5.3",
"eslint-config-loopback": "^1.0.0",
"fs-extra": "^0.12.0",
"jscs": "^1.7.3",
"jshint": "^2.5.6",
"loopback": "^2.16.3",
"mocha": "^1.19.0",
"supertest": "^0.14.0"

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
var boot = require('../');
var exportBrowserifyToFile = require('./helpers/browserify').exportToSandbox;
var fs = require('fs');
@ -22,14 +27,14 @@ describe('browser support for multiple apps', function() {
{
appDir: app1Dir,
appFile: './app.js',
moduleName: 'browser-app'
moduleName: 'browser-app',
},
{
appDir: app2Dir,
appFile: './app.js',
moduleName: 'browser-app2',
appId: 'browserApp2'
}
appId: 'browserApp2',
},
];
browserifyTestApps(apps, function(err, bundlePath) {
@ -55,7 +60,7 @@ describe('browser support for multiple apps', function() {
function browserifyTestApps(apps, next) {
var b = browserify({
debug: true
debug: true,
});
for (var i in apps) {
@ -65,13 +70,13 @@ function browserifyTestApps(apps, next) {
var appId = apps[i].appId;
appFile = path.join(appDir, appFile);
b.require(appFile, {expose: moduleName});
b.require(appFile, { expose: moduleName });
var opts = appDir;
if (appId) {
opts = {
appId: appId,
appRootDir: appDir
appRootDir: appDir,
};
}
boot.compileToBrowserify(opts, b);

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
var boot = require('../');
var exportBrowserifyToFile = require('./helpers/browserify').exportToSandbox;
var fs = require('fs');
@ -13,7 +18,7 @@ var compileStrategies = {
'default': function(appDir) {
var b = browserify({
basedir: appDir,
debug: true
debug: true,
});
b.require('./app.js', { expose: 'browser-app' });
@ -24,7 +29,7 @@ var compileStrategies = {
var b = browserify({
basedir: appDir,
extensions: ['.coffee'],
debug: true
debug: true,
});
b.transform('coffeeify');
@ -64,7 +69,7 @@ describe('browser support', function() {
it('loads mixins', function(done) {
var appDir = path.resolve(__dirname, './fixtures/browser-app');
var options = {
appRootDir: appDir
appRootDir: appDir,
};
browserifyTestApp(options, function(err, bundlePath) {

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
var async = require('async');
var boot = require('../');
var path = require('path');
@ -37,22 +42,22 @@ describe('executor', function() {
host: '127.0.0.1',
restApiRoot: '/rest-api',
foo: { bar: 'bat' },
baz: true
baz: true,
},
models: [
{
name: 'User',
config: {
dataSource: 'the-db'
}
}
dataSource: 'the-db',
},
},
],
dataSources: {
'the-db': {
connector: 'memory',
defaultForType: 'db'
}
}
defaultForType: 'db',
},
},
});
describe('when booting', function() {
@ -66,15 +71,15 @@ describe('executor', function() {
});
});
it('should emit the `booted` event', function(done) {
it('should emit the `booted` event in the next tick', function(done) {
boot.execute(app, dummyInstructions, function(err) {
expect(err).to.be.undefined();
});
app.on('booted', function() {
// This test fails with a timeout when the `booted` event has not been
// emitted correctly
done();
});
boot.execute(app, dummyInstructions, function(err) {
expect(err).to.be.undefined();
});
});
it('should work when called synchronously', function() {
@ -109,9 +114,9 @@ describe('executor', function() {
name: 'Customer',
base: 'User',
},
sourceFile: path.resolve(appdir.PATH, 'models', 'Customer.js')
}
]
sourceFile: path.resolve(appdir.PATH, 'models', 'Customer.js'),
},
],
}));
expect(app.models.Customer).to.exist();
@ -127,9 +132,9 @@ describe('executor', function() {
name: 'Vehicle',
config: undefined,
definition: {
name: 'Vehicle'
name: 'Vehicle',
},
sourceFile: undefined
sourceFile: undefined,
},
{
name: 'Car',
@ -138,9 +143,9 @@ describe('executor', function() {
name: 'Car',
base: 'Vehicle',
},
sourceFile: undefined
sourceFile: undefined,
},
]
],
}));
expect(Object.keys(app.models)).to.eql(['Car']);
@ -166,15 +171,15 @@ describe('executor', function() {
name: 'Customer',
config: { dataSource: 'db' },
definition: { name: 'Customer' },
sourceFile: path.resolve(appdir.PATH, 'models', 'Customer.js')
sourceFile: path.resolve(appdir.PATH, 'models', 'Customer.js'),
},
{
name: 'UniqueName',
config: { dataSource: 'db' },
definition: { name: 'UniqueName' },
sourceFile: undefined
}
]
sourceFile: undefined,
},
],
}));
expect(app.models.Customer._modelsWhenAttached).to.include('UniqueName');
@ -188,9 +193,9 @@ describe('executor', function() {
name: 'LocalCustomer',
config: { dataSource: 'db' },
definition: { name: 'LocalCustomer' },
sourceFile: undefined
}
]
sourceFile: undefined,
},
],
}));
expect(Object.keys(loopback.registry.modelBuilder.models), 'global models')
@ -204,7 +209,7 @@ describe('executor', function() {
'require("doesnt-exist"); module.exports = {};');
function doBoot() {
boot.execute(app, someInstructions({ files: { boot: [file] } }));
boot.execute(app, someInstructions({ files: { boot: [file] }}));
}
expect(doBoot).to.throw(/Cannot find module \'doesnt-exist\'/);
@ -236,7 +241,7 @@ describe('executor', function() {
require.resolve('loopback/common/models/user.json')
),
config: { dataSource: 'db' },
sourceFile: require.resolve('loopback/common/models/user.js')
sourceFile: require.resolve('loopback/common/models/user.js'),
};
builtinModel.definition.redefined = true;
@ -260,7 +265,7 @@ describe('executor', function() {
'barLoaded',
'barSyncLoaded',
'fooLoaded',
'barStarted'
'barStarted',
]);
// bar finished happens in the next tick
@ -272,7 +277,7 @@ describe('executor', function() {
'fooLoaded',
'barStarted',
'barFinished',
'barSyncExecuted'
'barSyncExecuted',
]);
done();
}, 10);
@ -288,7 +293,7 @@ describe('executor', function() {
'fooLoaded',
'barStarted',
'barFinished',
'barSyncExecuted'
'barSyncExecuted',
]);
done();
});
@ -306,11 +311,11 @@ describe('executor', function() {
'function(Model, options) {}');
appdir.writeConfigFileSync('custom-mixins/time-stamps.json', {
name: 'Timestamping'
name: 'Timestamping',
});
options = {
appRootDir: appdir.PATH
appRootDir: appdir.PATH,
};
});
@ -357,8 +362,8 @@ describe('executor', function() {
boot.execute(app, someInstructions({
config: {
port: undefined,
host: undefined
}
host: undefined,
},
}));
}
@ -388,6 +393,7 @@ describe('executor', function() {
it('should prioritize host sources', function() {
// jscs:disable requireCamelCaseOrUpperCaseIdentifiers
/*eslint-disable camelcase*/
process.env.npm_config_host = randomHost();
process.env.OPENSHIFT_SLS_IP = randomHost();
process.env.OPENSHIFT_NODEJS_IP = randomHost();
@ -397,9 +403,11 @@ describe('executor', function() {
bootWithDefaults();
assert.equal(app.get('host'), process.env.npm_config_host);
/*eslint-enable camelcase*/
});
it('should prioritize port sources', function() {
/*eslint-disable camelcase*/
process.env.npm_config_port = randomPort();
process.env.OPENSHIFT_SLS_PORT = randomPort();
process.env.OPENSHIFT_NODEJS_PORT = randomPort();
@ -409,6 +417,7 @@ describe('executor', function() {
bootWithDefaults();
assert.equal(app.get('port'), process.env.npm_config_port);
/*eslint-enable camelcase*/
});
function randomHost() {
@ -420,24 +429,28 @@ describe('executor', function() {
}
it('should honor 0 for free port', function() {
boot.execute(app, someInstructions({ config: { port: 0 } }));
boot.execute(app, someInstructions({ config: { port: 0 }}));
assert.equal(app.get('port'), 0);
});
it('should default to port 3000', function() {
boot.execute(app, someInstructions({ config: { port: undefined } }));
boot.execute(app, someInstructions({ config: { port: undefined }}));
assert.equal(app.get('port'), 3000);
});
it('should respect named pipes port values in ENV', function() {
var NAMED_PORT = '\\.\\pipe\\test';
process.env.PORT = NAMED_PORT;
boot.execute(app, someInstructions({ config: { port: 3000 } }));
boot.execute(app, someInstructions({ config: { port: 3000 }}));
assert.equal(app.get('port'), NAMED_PORT);
});
});
describe('with middleware.json', function() {
beforeEach(function() {
delete process.env.restApiRoot;
});
it('should parse a simple config variable', function(done) {
boot.execute(app, simpleMiddlewareConfig('routes',
{ path: '${restApiRoot}' }
@ -450,6 +463,36 @@ describe('executor', function() {
});
});
it('should parse simple config variable from env var', function(done) {
process.env.restApiRoot = '/url-from-env-var';
boot.execute(app, simpleMiddlewareConfig('routes',
{ path: '${restApiRoot}' }
));
supertest(app).get('/url-from-env-var').end(function(err, res) {
if (err) return done(err);
expect(res.body.path).to.equal('/url-from-env-var');
done();
});
});
it('dynamic variable from `env var` should have' +
' precedence over app.get()', function(done) {
process.env.restApiRoot = '/url-from-env-var';
var bootInstructions;
bootInstructions = simpleMiddlewareConfig('routes',
{ path: '${restApiRoot}' });
bootInstructions.config = { restApiRoot: '/url-from-config' };
boot.execute(app, someInstructions(bootInstructions));
supertest(app).get('/url-from-env-var').end(function(err, res) {
if (err) return done(err);
expect(app.get('restApiRoot')).to.equal('/url-from-config');
expect(res.body.path).to.equal('/url-from-env-var');
done();
});
});
it('should parse multiple config variables', function(done) {
boot.execute(app, simpleMiddlewareConfig('routes',
{ path: '${restApiRoot}', env: '${env}' }
@ -479,13 +522,13 @@ describe('executor', function() {
it('should parse config variables in an object', function(done) {
boot.execute(app, simpleMiddlewareConfig('routes',
{ info: { path: '${restApiRoot}' } }
{ info: { path: '${restApiRoot}' }}
));
supertest(app).get('/').end(function(err, res) {
if (err) return done(err);
expect(res.body.info).to.eql({
path: app.get('restApiRoot')
path: app.get('restApiRoot'),
});
done();
});
@ -493,13 +536,30 @@ describe('executor', function() {
it('should parse config variables in a nested object', function(done) {
boot.execute(app, simpleMiddlewareConfig('routes',
{ nested: { info: { path: '${restApiRoot}' } } }
{ nested: { info: { path: '${restApiRoot}' }}}
));
supertest(app).get('/').end(function(err, res) {
if (err) return done(err);
expect(res.body.nested).to.eql({
info: { path: app.get('restApiRoot') }
info: { path: app.get('restApiRoot') },
});
done();
});
});
it('should parse config variables with null values', function(done) {
boot.execute(app, simpleMiddlewareConfig('routes',
{ nested: { info: { path: '${restApiRoot}', some: null }}}
));
supertest(app).get('/').end(function(err, res) {
if (err) return done(err);
expect(res.body.nested).to.eql({
info: {
path: app.get('restApiRoot'),
some: null,
},
});
done();
});
@ -509,7 +569,7 @@ describe('executor', function() {
var invalidDataTypes = [undefined, function() {}];
async.each(invalidDataTypes, function(invalidDataType, cb) {
var config = simpleMiddlewareConfig('routes', {
path: invalidDataType
path: invalidDataType,
});
boot.execute(app, config);
@ -525,7 +585,7 @@ describe('executor', function() {
it('should parse valid config variables', function(done) {
var config = simpleMiddlewareConfig('routes', {
props: ['a', '${vVar}', 1, true, function() {}, {x:1, y: '${y}'}]
props: ['a', '${vVar}', 1, true, function() {}, { x: 1, y: '${y}' }],
});
boot.execute(app, config);
@ -552,6 +612,10 @@ describe('executor', function() {
});
describe('with component-config.json', function() {
beforeEach(function() {
delete process.env.DYNAMIC_ENVVAR;
delete process.env.DYNAMIC_VARIABLE;
});
it('should parse a simple config variable', function(done) {
boot.execute(app, simpleComponentConfig(
@ -565,6 +629,46 @@ describe('executor', function() {
});
});
it('should parse config from `env-var` and `config`', function(done) {
var bootInstructions = simpleComponentConfig(
{
path: '${restApiRoot}',
fromConfig: '${DYNAMIC_CONFIG}',
fromEnvVar: '${DYNAMIC_ENVVAR}',
}
);
// result should get value from config.json
bootInstructions.config['DYNAMIC_CONFIG'] = 'FOOBAR-CONFIG';
// result should get value from env var
process.env.DYNAMIC_ENVVAR = 'FOOBAR-ENVVAR';
boot.execute(app, bootInstructions);
supertest(app).get('/component').end(function(err, res) {
if (err) return done(err);
expect(res.body.fromConfig).to.equal('FOOBAR-CONFIG');
expect(res.body.fromEnvVar).to.equal('FOOBAR-ENVVAR');
done();
});
});
it('`env-var` should have precedence over `config`', function(done) {
var key = 'DYNAMIC_VARIABLE';
var bootInstructions = simpleComponentConfig({
path: '${restApiRoot}',
isDynamic: '${' + key + '}',
});
bootInstructions.config[key] = 'should be overwritten';
process.env[key] = 'successfully overwritten';
boot.execute(app, bootInstructions);
supertest(app).get('/component').end(function(err, res) {
if (err) return done(err);
expect(res.body.isDynamic).to.equal('successfully overwritten');
done();
});
});
it('should parse multiple config variables', function(done) {
boot.execute(app, simpleComponentConfig(
{ path: '${restApiRoot}', env: '${env}' }
@ -594,13 +698,13 @@ describe('executor', function() {
it('should parse config variables in an object', function(done) {
boot.execute(app, simpleComponentConfig(
{ info: { path: '${restApiRoot}' } }
{ info: { path: '${restApiRoot}' }}
));
supertest(app).get('/component').end(function(err, res) {
if (err) return done(err);
expect(res.body.info).to.eql({
path: app.get('restApiRoot')
path: app.get('restApiRoot'),
});
done();
});
@ -608,18 +712,17 @@ describe('executor', function() {
it('should parse config variables in a nested object', function(done) {
boot.execute(app, simpleComponentConfig(
{ nested: { info: { path: '${restApiRoot}' } } }
{ nested: { info: { path: '${restApiRoot}' }}}
));
supertest(app).get('/component').end(function(err, res) {
if (err) return done(err);
expect(res.body.nested).to.eql({
info: { path: app.get('restApiRoot') }
info: { path: app.get('restApiRoot') },
});
done();
});
});
});
it('calls function exported by boot/init.js', function() {
@ -627,7 +730,7 @@ describe('executor', function() {
'module.exports = function(app) { app.fnCalled = true; };');
delete app.fnCalled;
boot.execute(app, someInstructions({ files: { boot: [file] } }));
boot.execute(app, someInstructions({ files: { boot: [file] }}));
expect(app.fnCalled, 'exported fn was called').to.be.true();
});
@ -642,33 +745,33 @@ describe('executor', function() {
sourceFile: pushNamePath,
config: {
phase: 'initial',
params: 'initial'
}
params: 'initial',
},
},
{
sourceFile: pushNamePath,
config: {
phase: 'custom',
params: 'custom'
}
params: 'custom',
},
},
{
sourceFile: pushNamePath,
config: {
phase: 'routes',
params: 'routes'
}
params: 'routes',
},
},
{
sourceFile: pushNamePath,
config: {
phase: 'routes',
enabled: false,
params: 'disabled'
}
}
]
}
params: 'disabled',
},
},
],
},
}));
supertest(app)
@ -682,7 +785,6 @@ describe('executor', function() {
});
it('configures middleware using shortform', function(done) {
boot.execute(app, someInstructions({
middleware: {
middleware: [
@ -691,11 +793,11 @@ describe('executor', function() {
fragment: 'static',
config: {
phase: 'files',
params: path.join(__dirname, './fixtures/simple-app/client/')
}
}
]
}
params: path.join(__dirname, './fixtures/simple-app/client/'),
},
},
],
},
}));
supertest(app)
@ -725,8 +827,8 @@ describe('executor', function() {
it('configures components', function() {
appdir.writeConfigFileSync('component-config.json', {
'./components/test-component': {
option: 'value'
}
option: 'value',
},
});
appdir.writeFileSync('components/test-component/index.js',
@ -743,7 +845,7 @@ describe('executor', function() {
it('disables component when configuration is not set', function() {
appdir.writeConfigFileSync('component-config.json', {
'./components/test-component': false
'./components/test-component': false,
});
appdir.writeFileSync('components/test-component/index.js',
@ -758,10 +860,10 @@ describe('executor', function() {
it('disable component if overrided by production configuration', function() {
appdir.writeConfigFileSync('component-config.json', {
'./components/test-component': {}
'./components/test-component': {},
});
appdir.writeConfigFileSync('component-config.production.json', {
'./components/test-component': null
'./components/test-component': null,
});
appdir.writeFileSync('components/test-component/index.js',
@ -785,11 +887,11 @@ describe('executor', function() {
sourceFile: passportPath,
fragment: 'initialize',
config: {
phase: 'auth:before'
}
}
]
}
phase: 'auth:before',
},
},
],
},
}));
supertest(app)
@ -809,6 +911,132 @@ describe('executor', function() {
});
});
describe('when booting with lazy connect', function() {
var SAMPLE_INSTRUCTION = someInstructions({
dataSources: {
lazyConnector: {
connector: 'testLazyConnect',
name: 'lazyConnector',
},
},
});
var connectTriggered = true;
beforeEach(function() {
app.connector('testLazyConnect', {
initialize: function(dataSource, callback) {
if (dataSource.settings.lazyConnect) {
connectTriggered = false;
} else {
connectTriggered = true;
}
},
});
});
it('should trigger connect with ENV undefined', function(done) {
delete process.env.LB_LAZYCONNECT_DATASOURCES;
boot.execute(app, SAMPLE_INSTRUCTION, function() {
expect(connectTriggered).to.equal(true);
done();
});
});
it('should not trigger connect with ENV true', function(done) {
process.env.LB_LAZYCONNECT_DATASOURCES = 'true';
boot.execute(app, SAMPLE_INSTRUCTION, function() {
expect(connectTriggered).to.equal(false);
done();
});
});
it('should trigger connect with ENV false', function(done) {
process.env.LB_LAZYCONNECT_DATASOURCES = 'false';
boot.execute(app, SAMPLE_INSTRUCTION, function() {
expect(connectTriggered).to.equal(true);
done();
});
});
it('should trigger connect with ENV 0', function(done) {
process.env.LB_LAZYCONNECT_DATASOURCES = '0';
boot.execute(app, SAMPLE_INSTRUCTION, function() {
expect(connectTriggered).to.equal(true);
done();
});
});
});
describe('dynamic configuration for datasources.json', function() {
beforeEach(function() {
delete process.env.DYNAMIC_HOST;
delete process.env.DYNAMIC_PORT;
});
it('should convert dynamic variable for datasource', function(done) {
var datasource = {
mydb: {
host: '${DYNAMIC_HOST}',
port: '${DYNAMIC_PORT}',
},
};
var bootInstructions = { dataSources: datasource };
process.env.DYNAMIC_PORT = '10007';
process.env.DYNAMIC_HOST = '123.321.123.132';
boot.execute(app, someInstructions(bootInstructions), function() {
expect(app.datasources.mydb.settings.host).to.equal('123.321.123.132');
expect(app.datasources.mydb.settings.port).to.equal('10007');
done();
});
});
it('should resolve dynamic config via app.get()', function(done) {
var datasource = {
mydb: { host: '${DYNAMIC_HOST}' },
};
var bootInstructions = {
config: { DYNAMIC_HOST: '127.0.0.4' },
dataSources: datasource,
};
boot.execute(app, someInstructions(bootInstructions), function() {
expect(app.get('DYNAMIC_HOST')).to.equal('127.0.0.4');
expect(app.datasources.mydb.settings.host).to.equal(
'127.0.0.4');
done();
});
});
it('should take ENV precedence over config.json', function(done) {
process.env.DYNAMIC_HOST = '127.0.0.2';
var datasource = {
mydb: { host: '${DYNAMIC_HOST}' },
};
var bootInstructions = {
config: { DYNAMIC_HOST: '127.0.0.3' },
dataSources: datasource,
};
boot.execute(app, someInstructions(bootInstructions), function() {
expect(app.get('DYNAMIC_HOST')).to.equal('127.0.0.3');
expect(app.datasources.mydb.settings.host).to.equal('127.0.0.2');
done();
});
});
it('empty dynamic conf should resolve as `undefined`', function(done) {
var datasource = {
mydb: { host: '${DYNAMIC_HOST}' },
};
var bootInstructions = { dataSources: datasource };
boot.execute(app, someInstructions(bootInstructions), function() {
expect(app.get('DYNAMIC_HOST')).to.be.undefined();
expect(app.datasources.mydb.settings.host).to.be.undefined();
done();
});
});
});
});
function simpleMiddlewareConfig(phase, paths, params) {
@ -819,7 +1047,7 @@ function simpleMiddlewareConfig(phase, paths, params) {
var config = {
phase: phase,
params: params
params: params,
};
if (paths) config.paths = paths;
@ -831,9 +1059,9 @@ function simpleMiddlewareConfig(phase, paths, params) {
{
sourceFile: path.join(__dirname, './fixtures/simple-middleware.js'),
config: config,
}
]
}
},
],
},
});
}
@ -842,9 +1070,9 @@ function simpleComponentConfig(config) {
components: [
{
sourceFile: path.join(__dirname, './fixtures/simple-component.js'),
config: config
}
]
config: config,
},
],
});
}
@ -869,12 +1097,12 @@ function someInstructions(values) {
var result = {
config: values.config || {},
models: values.models || [],
dataSources: values.dataSources || { db: { connector: 'memory' } },
dataSources: values.dataSources || { db: { connector: 'memory' }},
middleware: values.middleware || { phases: [], middleware: [] },
components: values.components || [],
files: {
boot: []
}
boot: [],
},
};
if (values.env)
@ -898,6 +1126,6 @@ function envAppInstructions() {
fs.copySync(ENV_APP, appdir.PATH);
return boot.compile({
appRootDir: appdir.PATH,
env: 'test'
env: 'test',
});
}

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
var loopback = require('loopback');
var boot = require('../../../');
@ -5,5 +10,5 @@ var app = module.exports = loopback();
boot(app, {
appId: 'browserApp2',
appRootDir: __dirname
appRootDir: __dirname,
});

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2015. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
module.exports = function(Robot) {
Robot.settings._customized = 'Robot';
Robot.base.settings._customized = 'Robot';

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
var loopback = require('loopback');
var boot = require('../../../');

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2014. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
module.exports = function(app) {
app.set('custom-key', 'custom-value');
};

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2015. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
module.exports = function(app, options) {
app.dummyComponentOptions = options;
};

View File

@ -1,5 +1,8 @@
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
module.exports = function(Model, options) {
Model.timeStampsMixin = true;
};

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2014. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
module.exports = function(Customer) {
Customer.settings._customized = 'Customer';
Customer.base.settings._customized = 'Base';

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2015. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
process.bootFlags.push('barLoadedInTest');
module.exports = function(app, callback) {
callback();

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
var framework = {
initialize: function(passport) {
return function(req, res, next) {
@ -5,7 +10,7 @@ var framework = {
res.setHeader('passport', 'initialized');
next();
};
}
},
};
var Passport = function() {

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2014. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
process.bootFlags.push('barLoaded');
module.exports = function(app, callback) {
process.bootFlags.push('barStarted');

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2014. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
process.bootFlags.push('barSyncLoaded');
module.exports = function(app) {
process.bootFlags.push('barSyncExecuted');

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2015. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
module.exports = function(app, cb) {
if (app.booting)
process.bootingFlagSet = true;

View File

@ -1 +1,6 @@
// Copyright IBM Corp. 2014. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
process.bootFlags.push('fooLoaded');

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2014. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
exports.myMiddleware = function(name) {
return function(req, res, next) {
req._names = req._names || [];

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2014. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
/**
* Exporting a middleware as a property of the main module
*/

View File

@ -1,7 +1,10 @@
module.exports = function(loopbackApp, params) {
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
module.exports = function(loopbackApp, params) {
loopbackApp.use('/component', function(req, res, next) {
res.send(params);
});
};

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2015. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
module.exports = function(params) {
return function(req, res, next) {
res.send(params);

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2014,2016. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
var path = require('path');
var fs = require('fs-extra');
var extend = require('util')._extend;
@ -27,8 +32,8 @@ appdir.createConfigFilesSync = function(appConfig, dataSources, models) {
dataSources = extend({
db: {
connector: 'memory',
defaultForType: 'db'
}
defaultForType: 'db',
},
}, dataSources);
appdir.writeConfigFileSync ('datasources.json', dataSources);

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2015,2016. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
var vm = require('vm');
function createContext() {
@ -8,14 +13,14 @@ function createContext() {
localStorage: {
// used by `debug` module
debug: process.env.DEBUG
debug: process.env.DEBUG,
},
// used by DataSource.prototype.ready
setTimeout: setTimeout,
// used by `debug` module
document: { documentElement: { style: {} } },
document: { documentElement: { style: {}}},
// used by `debug` module
navigator: { userAgent: 'sandbox' },
@ -39,9 +44,9 @@ function createContext() {
_logs: {
log: [],
warn: [],
error: []
error: [],
},
}
},
};
// `window` is used by loopback to detect browser runtime
@ -52,9 +57,10 @@ function createContext() {
exports.createContext = createContext;
function printContextLogs(context) {
for (var k in context.console._logs) {
var k, ix; // see https://github.com/eslint/eslint/issues/5744
for (k in context.console._logs) {
var items = context.console._logs[k];
for (var ix in items) {
for (ix in items) {
console[k].apply(console, items[ix]);
}
}

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2015. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
var fs = require('fs');
var sandbox = require('./sandbox');

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2014. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
module.exports = function(name) {
return function(req, res, next) {
req._names = req._names || [];

View File

@ -1,3 +1,8 @@
// Copyright IBM Corp. 2014. All Rights Reserved.
// Node module: loopback-boot
// This file is licensed under the MIT License.
// License text available at https://opensource.org/licenses/MIT
var fs = require('fs-extra');
var path = require('path');