diff --git a/lib/application.js b/lib/application.js index eb01d398e..c56066e08 100644 --- a/lib/application.js +++ b/lib/application.js @@ -372,210 +372,9 @@ app.enableAuth = function() { this.isAuthEnabled = true; }; -/** - * Initialize an application from an options object or a set of JSON and JavaScript files. - * - * **Deprecated. Use the package - * [loopback-boot](https://github.com/strongloop/loopback-boot) instead.** - * - * This function takes an optional argument that is either a string or an object. - * - * If the argument is a string, then it sets the application root directory based on the string value. Then it: - * 1. Creates DataSources from the `datasources.json` file in the application root directory. - * 2. Creates Models from the `models.json` file in the application root directory. - * - * If the argument is an object, then it looks for `model`, `dataSources`, and `appRootDir` properties of the object. - * If the object has no `appRootDir` property then it sets the current working directory as the application root directory. - * Then it: - * 1. Creates DataSources from the `options.dataSources` object. - * 2. Creates Models from the `options.models` object. - * - * In both cases, the function loads JavaScript files in the `/models` and `/boot` subdirectories of the application root directory with `require()`. - * - * **NOTE:** mixing `app.boot()` and `app.model(name, config)` in multiple - * files may result in models being **undefined** due to race conditions. - * To avoid this when using `app.boot()` make sure all models are passed as part of the `models` definition. - * - * Throws an error if the config object is not valid or if boot fails. - * - * - * **Model Definitions** - * - * The following is example JSON for two `Model` definitions: "dealership" and "location". - * - * ```js - * { - * "dealership": { - * // a reference, by name, to a dataSource definition - * "dataSource": "my-db", - * // the options passed to Model.extend(name, properties, options) - * "options": { - * "relations": { - * "cars": { - * "type": "hasMany", - * "model": "Car", - * "foreignKey": "dealerId" - * } - * } - * }, - * // the properties passed to Model.extend(name, properties, options) - * "properties": { - * "id": {"id": true}, - * "name": "String", - * "zip": "Number", - * "address": "String" - * } - * }, - * "car": { - * "dataSource": "my-db" - * "properties": { - * "id": { - * "type": "String", - * "required": true, - * "id": true - * }, - * "make": { - * "type": "String", - * "required": true - * }, - * "model": { - * "type": "String", - * "required": true - * } - * } - * } - * } - * ``` - * @options {String|Object} options Boot options; If String, this is the application root directory; if object, has below properties. - * @property {String} appRootDir Directory to use when loading JSON and JavaScript files (optional). Defaults to the current directory (`process.cwd()`). - * @property {Object} models Object containing `Model` definitions (optional). - * @property {Object} dataSources Object containing `DataSource` definitions (optional). - * @end - * - * @header app.boot([options]) - */ - app.boot = function(options) { - options = options || {}; - - if(typeof options === 'string') { - options = {appRootDir: options}; - } - var app = this; - var appRootDir = options.appRootDir = options.appRootDir || process.cwd(); - var ctx = {}; - var appConfig = options.app; - var modelConfig = options.models; - var dataSourceConfig = options.dataSources; - - if(!appConfig) { - appConfig = tryReadConfig(appRootDir, 'app') || {}; - } - if(!modelConfig) { - modelConfig = tryReadConfig(appRootDir, 'models') || {}; - } - if(!dataSourceConfig) { - dataSourceConfig = tryReadConfig(appRootDir, 'datasources') || {}; - } - - assertIsValidConfig('app', appConfig); - assertIsValidConfig('model', modelConfig); - assertIsValidConfig('data source', dataSourceConfig); - - appConfig.host = - process.env.npm_config_host || - process.env.OPENSHIFT_SLS_IP || - process.env.OPENSHIFT_NODEJS_IP || - process.env.HOST || - appConfig.host || - process.env.npm_package_config_host || - app.get('host'); - - appConfig.port = _.find([ - process.env.npm_config_port, - process.env.OPENSHIFT_SLS_PORT, - process.env.OPENSHIFT_NODEJS_PORT, - process.env.PORT, - appConfig.port, - process.env.npm_package_config_port, - app.get('port'), - 3000 - ], _.isFinite); - - appConfig.restApiRoot = - appConfig.restApiRoot || - app.get('restApiRoot') || - '/api'; - - if(appConfig.host !== undefined) { - assert(typeof appConfig.host === 'string', 'app.host must be a string'); - app.set('host', appConfig.host); - } - - if(appConfig.port !== undefined) { - var portType = typeof appConfig.port; - assert(portType === 'string' || portType === 'number', 'app.port must be a string or number'); - app.set('port', appConfig.port); - } - - assert(appConfig.restApiRoot !== undefined, 'app.restApiRoot is required'); - assert(typeof appConfig.restApiRoot === 'string', 'app.restApiRoot must be a string'); - assert(/^\//.test(appConfig.restApiRoot), 'app.restApiRoot must start with "/"'); - app.set('restApiRoot', appConfig.restApiRoot); - - for(var configKey in appConfig) { - var cur = app.get(configKey); - if(cur === undefined || cur === null) { - app.set(configKey, appConfig[configKey]); - } - } - - // instantiate data sources - forEachKeyedObject(dataSourceConfig, function(key, obj) { - app.dataSource(key, obj); - }); - - // instantiate models - forEachKeyedObject(modelConfig, function(key, obj) { - app.model(key, obj); - }); - - // try to attach models to dataSources by type - try { - registry.autoAttach(); - } catch(e) { - if(e.name === 'AssertionError') { - console.warn(e); - } else { - throw e; - } - } - - // disable token requirement for swagger, if available - var swagger = app.remotes().exports.swagger; - var requireTokenForSwagger = appConfig.swagger - && appConfig.swagger.requireToken; - if(swagger) { - swagger.requireToken = requireTokenForSwagger || false; - } - - // require directories - var requiredModels = requireDir(path.join(appRootDir, 'models')); - var requiredBootScripts = requireDir(path.join(appRootDir, 'boot')); -} - -function assertIsValidConfig(name, config) { - if(config) { - assert(typeof config === 'object', name + ' config must be a valid JSON object'); - } -} - -function forEachKeyedObject(obj, fn) { - if(typeof obj !== 'object') return; - - Object.keys(obj).forEach(function(key) { - fn(key, obj[key]); - }); + throw new Error( + '`app.boot` was removed, use the new module loopback-boot instead'); } function classify(str) { @@ -628,85 +427,6 @@ function configureModel(ModelCtor, config, app) { registry.configureModel(ModelCtor, config); } -function requireDir(dir, basenames) { - assert(dir, 'cannot require directory contents without directory name'); - - var requires = {}; - - if (arguments.length === 2) { - // if basenames argument is passed, explicitly include those files - basenames.forEach(function (basename) { - var filepath = Path.resolve(Path.join(dir, basename)); - requires[basename] = tryRequire(filepath); - }); - } else if (arguments.length === 1) { - // if basenames arguments isn't passed, require all javascript - // files (except for those prefixed with _) and all directories - - var files = tryReadDir(dir); - - // sort files in lowercase alpha for linux - files.sort(function (a,b) { - a = a.toLowerCase(); - b = b.toLowerCase(); - - if (a < b) { - return -1; - } else if (b < a) { - return 1; - } else { - return 0; - } - }); - - files.forEach(function (filename) { - // ignore index.js and files prefixed with underscore - if ((filename === 'index.js') || (filename[0] === '_')) { return; } - - var filepath = path.resolve(path.join(dir, filename)); - var ext = path.extname(filename); - var stats = fs.statSync(filepath); - - // only require files supported by require.extensions (.txt .md etc.) - if (stats.isFile() && !(ext in require.extensions)) { return; } - - var basename = path.basename(filename, ext); - - requires[basename] = tryRequire(filepath); - }); - - } - - return requires; -}; - -function tryRequire(modulePath) { - try { - return require.apply(this, arguments); - } catch(e) { - console.error('failed to require "%s"', modulePath); - throw e; - } -} - -function tryReadDir() { - try { - return fs.readdirSync.apply(fs, arguments); - } catch(e) { - return []; - } -} - -function tryReadConfig(cwd, fileName) { - try { - return require(path.join(cwd, fileName + '.json')); - } catch(e) { - if(e.code !== "MODULE_NOT_FOUND") { - throw e; - } - } -} - function clearHandlerCache(app) { app._handlers = undefined; } diff --git a/package.json b/package.json index 53668356e..bef9fb8d9 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "strong-task-emitter": "0.0.x", "supertest": "~0.13.0", "chai": "~1.9.1", + "loopback-boot": "1.x >=1.1", "loopback-testing": "~0.2.0", "browserify": "~4.1.6", "grunt": "~0.4.5", diff --git a/test/app.test.js b/test/app.test.js index e3fed516d..de001b509 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -86,15 +86,8 @@ describe('app', function() { beforeEach(function() { app = loopback(); - app.boot({ - app: {port: 3000, host: '127.0.0.1'}, - // prevent loading of models.json, it is not available in the browser - models: {}, - dataSources: { - db: { - connector: 'memory' - } - } + app.dataSource('db', { + connector: 'memory' }); }); @@ -190,189 +183,6 @@ describe('app', function() { }); }); - describe.onServer('app.boot([options])', function () { - beforeEach(function () { - app.boot({ - app: { - port: 3000, - host: '127.0.0.1', - restApiRoot: '/rest-api', - foo: {bar: 'bat'}, - baz: true - }, - models: { - 'foo-bar-bat-baz': { - options: { - plural: 'foo-bar-bat-bazzies' - }, - dataSource: 'the-db' - } - }, - dataSources: { - 'the-db': { - connector: 'memory' - } - } - }); - }); - - it('should have port setting', function () { - assert.equal(this.app.get('port'), 3000); - }); - - it('should have host setting', function() { - assert.equal(this.app.get('host'), '127.0.0.1'); - }); - - it('should have restApiRoot setting', function() { - assert.equal(this.app.get('restApiRoot'), '/rest-api'); - }); - - it('should have other settings', function () { - expect(this.app.get('foo')).to.eql({ - bar: 'bat' - }); - expect(this.app.get('baz')).to.eql(true); - }); - - describe('boot and models directories', function() { - beforeEach(function() { - var app = this.app = loopback(); - app.boot(SIMPLE_APP); - }); - - it('should run all modules in the boot directory', function () { - assert(process.loadedFooJS); - delete process.loadedFooJS; - }); - - it('should run all modules in the models directory', function () { - assert(process.loadedBarJS); - delete process.loadedBarJS; - }); - }); - - describe('PaaS and npm env variables', function() { - beforeEach(function() { - this.boot = function () { - var app = loopback(); - app.boot({ - app: { - port: undefined, - host: undefined - } - }); - return app; - } - }); - - it('should be honored', function() { - var assertHonored = function (portKey, hostKey) { - process.env[hostKey] = randomPort(); - process.env[portKey] = randomHost(); - var app = this.boot(); - assert.equal(app.get('port'), process.env[portKey]); - assert.equal(app.get('host'), process.env[hostKey]); - delete process.env[portKey]; - delete process.env[hostKey]; - }.bind(this); - - assertHonored('OPENSHIFT_SLS_PORT', 'OPENSHIFT_NODEJS_IP'); - assertHonored('npm_config_port', 'npm_config_host'); - assertHonored('npm_package_config_port', 'npm_package_config_host'); - assertHonored('OPENSHIFT_SLS_PORT', 'OPENSHIFT_SLS_IP'); - assertHonored('PORT', 'HOST'); - }); - - it('should be honored in order', function() { - process.env.npm_config_host = randomHost(); - process.env.OPENSHIFT_SLS_IP = randomHost(); - process.env.OPENSHIFT_NODEJS_IP = randomHost(); - process.env.HOST = randomHost(); - process.env.npm_package_config_host = randomHost(); - - var app = this.boot(); - assert.equal(app.get('host'), process.env.npm_config_host); - - delete process.env.npm_config_host; - delete process.env.OPENSHIFT_SLS_IP; - delete process.env.OPENSHIFT_NODEJS_IP; - delete process.env.HOST; - delete process.env.npm_package_config_host; - - process.env.npm_config_port = randomPort(); - process.env.OPENSHIFT_SLS_PORT = randomPort(); - process.env.OPENSHIFT_NODEJS_PORT = randomPort(); - process.env.PORT = randomPort(); - process.env.npm_package_config_port = randomPort(); - - var app = this.boot(); - assert.equal(app.get('host'), process.env.npm_config_host); - assert.equal(app.get('port'), process.env.npm_config_port); - - delete process.env.npm_config_port; - delete process.env.OPENSHIFT_SLS_PORT; - delete process.env.OPENSHIFT_NODEJS_PORT; - delete process.env.PORT; - delete process.env.npm_package_config_port; - }); - - function randomHost() { - return Math.random().toString().split('.')[1]; - } - - function randomPort() { - return Math.floor(Math.random() * 10000); - } - - it('should honor 0 for free port', function () { - var app = loopback(); - app.boot({app: {port: 0}}); - assert.equal(app.get('port'), 0); - }); - - it('should default to port 3000', function () { - var app = loopback(); - app.boot({app: {port: undefined}}); - assert.equal(app.get('port'), 3000); - }); - }); - - it('Instantiate models', function () { - assert(app.models); - assert(app.models.FooBarBatBaz); - assert(app.models.fooBarBatBaz); - assertValidDataSource(app.models.FooBarBatBaz.dataSource); - assert.isFunc(app.models.FooBarBatBaz, 'find'); - assert.isFunc(app.models.FooBarBatBaz, 'create'); - }); - - it('Attach models to data sources', function () { - assert.equal(app.models.FooBarBatBaz.dataSource, app.dataSources.theDb); - }); - - it('Instantiate data sources', function () { - assert(app.dataSources); - assert(app.dataSources.theDb); - assertValidDataSource(app.dataSources.theDb); - assert(app.dataSources.TheDb); - }); - }); - - describe.onServer('app.boot(appRootDir)', function () { - it('Load config files', function () { - var app = loopback(); - - app.boot(SIMPLE_APP); - - assert(app.models.foo); - assert(app.models.Foo); - assert(app.models.Foo.dataSource); - assert.isFunc(app.models.Foo, 'find'); - assert.isFunc(app.models.Foo, 'create'); - }); - }); - describe.onServer('listen()', function() { it('starts http server', function(done) { var app = loopback(); diff --git a/test/fixtures/access-control/app.js b/test/fixtures/access-control/app.js index fb01c3dac..a8736a445 100644 --- a/test/fixtures/access-control/app.js +++ b/test/fixtures/access-control/app.js @@ -1,8 +1,9 @@ var loopback = require('../../../'); +var boot = require('loopback-boot'); var path = require('path'); var app = module.exports = loopback(); -app.boot(__dirname); +boot(app, __dirname); var apiPath = '/api'; app.use(loopback.cookieParser('secret')); diff --git a/test/fixtures/simple-integration-app/app.js b/test/fixtures/simple-integration-app/app.js index 5f71b1d3f..a4cbb2a44 100644 --- a/test/fixtures/simple-integration-app/app.js +++ b/test/fixtures/simple-integration-app/app.js @@ -1,8 +1,9 @@ var loopback = require('../../../'); +var boot = require('loopback-boot'); var path = require('path'); var app = module.exports = loopback(); -app.boot(__dirname); +boot(app, __dirname); app.use(loopback.favicon()); app.use(loopback.cookieParser({secret: app.get('cookieSecret')})); var apiPath = '/api';