2018-01-03 04:05:53 +00:00
|
|
|
// Copyright IBM Corp. 2013,2018. All Rights Reserved.
|
2016-05-03 22:50:21 +00:00
|
|
|
// Node module: loopback
|
|
|
|
// This file is licensed under the MIT License.
|
|
|
|
// License text available at https://opensource.org/licenses/MIT
|
|
|
|
|
2014-01-17 18:40:41 +00:00
|
|
|
/*!
|
2013-07-16 18:05:38 +00:00
|
|
|
* Module dependencies.
|
|
|
|
*/
|
|
|
|
|
2016-11-15 21:46:23 +00:00
|
|
|
'use strict';
|
2014-11-11 19:27:39 +00:00
|
|
|
var express = require('express');
|
|
|
|
var loopbackExpress = require('./server-app');
|
2014-10-30 20:49:47 +00:00
|
|
|
var proto = require('./application');
|
|
|
|
var fs = require('fs');
|
|
|
|
var ejs = require('ejs');
|
|
|
|
var path = require('path');
|
|
|
|
var merge = require('util')._extend;
|
|
|
|
var assert = require('assert');
|
2015-04-01 21:50:36 +00:00
|
|
|
var Registry = require('./registry');
|
|
|
|
var juggler = require('loopback-datasource-juggler');
|
2017-08-15 01:34:46 +00:00
|
|
|
var configureSharedMethods = require('./configure-shared-methods');
|
2013-07-16 18:05:38 +00:00
|
|
|
|
|
|
|
/**
|
2014-10-15 07:07:30 +00:00
|
|
|
* LoopBack core module. It provides static properties and
|
2014-01-17 18:40:41 +00:00
|
|
|
* methods to create models and data sources. The module itself is a function
|
2014-10-15 07:07:30 +00:00
|
|
|
* that creates loopback `app`. For example:
|
2014-01-17 18:40:41 +00:00
|
|
|
*
|
|
|
|
* ```js
|
|
|
|
* var loopback = require('loopback');
|
|
|
|
* var app = loopback();
|
|
|
|
* ```
|
2014-04-02 22:46:39 +00:00
|
|
|
*
|
2014-10-15 07:07:30 +00:00
|
|
|
* @property {String} version Version of LoopBack framework. Static read-only property.
|
|
|
|
* @property {Boolean} isBrowser True if running in a browser environment; false otherwise. Static read-only property.
|
|
|
|
* @property {Boolean} isServer True if running in a server environment; false otherwise. Static read-only property.
|
2015-04-01 21:50:36 +00:00
|
|
|
* @property {Registry} registry The global `Registry` object.
|
2014-11-04 18:59:38 +00:00
|
|
|
* @property {String} faviconFile Path to a default favicon shipped with LoopBack.
|
|
|
|
* Use as follows: `app.use(require('serve-favicon')(loopback.faviconFile));`
|
2014-04-02 22:46:39 +00:00
|
|
|
* @class loopback
|
|
|
|
* @header loopback
|
2013-07-16 18:05:38 +00:00
|
|
|
*/
|
|
|
|
|
2014-11-04 07:13:21 +00:00
|
|
|
var loopback = module.exports = createApplication;
|
2013-07-16 18:05:38 +00:00
|
|
|
|
2014-10-15 07:07:30 +00:00
|
|
|
/*!
|
2014-06-10 09:19:36 +00:00
|
|
|
* Framework version.
|
|
|
|
*/
|
|
|
|
|
|
|
|
loopback.version = require('../package.json').version;
|
|
|
|
|
2015-04-17 17:21:50 +00:00
|
|
|
loopback.registry = new Registry();
|
|
|
|
|
2015-04-17 16:02:04 +00:00
|
|
|
Object.defineProperties(loopback, {
|
|
|
|
Model: {
|
2016-04-01 09:14:26 +00:00
|
|
|
get: function() { return this.registry.getModel('Model'); },
|
2015-04-17 16:02:04 +00:00
|
|
|
},
|
|
|
|
PersistedModel: {
|
2016-04-01 09:14:26 +00:00
|
|
|
get: function() { return this.registry.getModel('PersistedModel'); },
|
2015-04-17 16:02:04 +00:00
|
|
|
},
|
|
|
|
defaultDataSources: {
|
2016-04-01 09:14:26 +00:00
|
|
|
get: function() { return this.registry.defaultDataSources; },
|
2015-04-17 16:02:04 +00:00
|
|
|
},
|
|
|
|
modelBuilder: {
|
2016-04-01 09:14:26 +00:00
|
|
|
get: function() { return this.registry.modelBuilder; },
|
|
|
|
},
|
2015-04-01 21:50:36 +00:00
|
|
|
});
|
|
|
|
|
2014-03-11 01:05:44 +00:00
|
|
|
/*!
|
2013-07-16 18:05:38 +00:00
|
|
|
* Create an loopback application.
|
|
|
|
*
|
|
|
|
* @return {Function}
|
|
|
|
* @api public
|
|
|
|
*/
|
|
|
|
|
2015-04-01 21:50:36 +00:00
|
|
|
function createApplication(options) {
|
2014-11-11 19:27:39 +00:00
|
|
|
var app = loopbackExpress();
|
2013-07-16 18:05:38 +00:00
|
|
|
|
2014-02-12 00:01:51 +00:00
|
|
|
merge(app, proto);
|
2013-07-16 18:05:38 +00:00
|
|
|
|
2014-06-13 08:27:23 +00:00
|
|
|
app.loopback = loopback;
|
|
|
|
|
2017-08-15 01:34:46 +00:00
|
|
|
app.on('modelRemoted', function() {
|
|
|
|
app.models().forEach(function(Model) {
|
|
|
|
if (!Model.config) return;
|
|
|
|
configureSharedMethods(Model, app.get('remoting'), Model.config);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2014-02-04 19:28:19 +00:00
|
|
|
// Create a new instance of models registry per each app instance
|
|
|
|
app.models = function() {
|
|
|
|
return proto.models.apply(this, arguments);
|
|
|
|
};
|
2013-07-16 18:05:38 +00:00
|
|
|
|
2014-05-27 12:33:42 +00:00
|
|
|
// Create a new instance of datasources registry per each app instance
|
|
|
|
app.datasources = app.dataSources = {};
|
|
|
|
|
2014-05-28 13:02:55 +00:00
|
|
|
// Create a new instance of connector registry per each app instance
|
|
|
|
app.connectors = {};
|
|
|
|
|
|
|
|
// Register built-in connectors. It's important to keep this code
|
|
|
|
// hand-written, so that all require() calls are static
|
|
|
|
// and thus browserify can process them (include connectors in the bundle)
|
|
|
|
app.connector('memory', loopback.Memory);
|
|
|
|
app.connector('remote', loopback.Remote);
|
2016-11-15 21:46:23 +00:00
|
|
|
app.connector('kv-memory',
|
|
|
|
require('loopback-datasource-juggler/lib/connectors/kv-memory'));
|
2014-05-28 13:02:55 +00:00
|
|
|
|
2015-04-01 21:50:36 +00:00
|
|
|
if (loopback.localRegistry || options && options.localRegistry === true) {
|
|
|
|
// setup the app registry
|
|
|
|
var registry = app.registry = new Registry();
|
2015-06-01 10:07:15 +00:00
|
|
|
if (options && options.loadBuiltinModels === true) {
|
|
|
|
require('./builtin-models')(registry);
|
|
|
|
}
|
2015-04-01 21:50:36 +00:00
|
|
|
} else {
|
|
|
|
app.registry = loopback.registry;
|
|
|
|
}
|
|
|
|
|
2013-07-16 18:05:38 +00:00
|
|
|
return app;
|
|
|
|
}
|
|
|
|
|
2014-06-06 09:47:25 +00:00
|
|
|
function mixin(source) {
|
|
|
|
for (var key in source) {
|
|
|
|
var desc = Object.getOwnPropertyDescriptor(source, key);
|
2014-06-23 12:44:19 +00:00
|
|
|
|
|
|
|
// Fix for legacy (pre-ES5) browsers like PhantomJS
|
|
|
|
if (!desc) continue;
|
|
|
|
|
2014-06-06 09:47:25 +00:00
|
|
|
Object.defineProperty(loopback, key, desc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mixin(require('./runtime'));
|
|
|
|
|
2014-01-17 18:40:41 +00:00
|
|
|
/*!
|
2016-05-31 14:50:04 +00:00
|
|
|
* Expose static express methods like `express.Router`.
|
2013-07-16 18:05:38 +00:00
|
|
|
*/
|
|
|
|
|
2014-06-06 09:47:25 +00:00
|
|
|
mixin(express);
|
2013-07-16 18:05:38 +00:00
|
|
|
|
2014-01-17 18:40:41 +00:00
|
|
|
/*!
|
2013-07-16 18:05:38 +00:00
|
|
|
* Expose additional loopback middleware
|
|
|
|
* for example `loopback.configure` etc.
|
2014-02-12 00:01:51 +00:00
|
|
|
*
|
|
|
|
* ***only in node***
|
2013-07-16 18:05:38 +00:00
|
|
|
*/
|
|
|
|
|
2014-02-12 00:01:51 +00:00
|
|
|
if (loopback.isServer) {
|
|
|
|
fs
|
2014-11-12 11:36:16 +00:00
|
|
|
.readdirSync(path.join(__dirname, '..', 'server', 'middleware'))
|
2014-10-30 20:49:47 +00:00
|
|
|
.filter(function(file) {
|
2014-02-12 00:01:51 +00:00
|
|
|
return file.match(/\.js$/);
|
|
|
|
})
|
2014-10-30 20:49:47 +00:00
|
|
|
.forEach(function(m) {
|
2014-11-12 11:36:16 +00:00
|
|
|
loopback[m.replace(/\.js$/, '')] = require('../server/middleware/' + m);
|
2014-02-12 00:01:51 +00:00
|
|
|
});
|
2014-11-12 11:36:16 +00:00
|
|
|
|
|
|
|
loopback.urlNotFound = loopback['url-not-found'];
|
|
|
|
delete loopback['url-not-found'];
|
2015-07-05 17:20:34 +00:00
|
|
|
|
|
|
|
loopback.errorHandler = loopback['error-handler'];
|
|
|
|
delete loopback['error-handler'];
|
2014-02-12 00:01:51 +00:00
|
|
|
}
|
2013-07-16 18:05:38 +00:00
|
|
|
|
2016-07-26 22:52:40 +00:00
|
|
|
// Expose path to the default favicon file
|
|
|
|
// ***only in node***
|
2014-11-03 09:00:24 +00:00
|
|
|
|
|
|
|
if (loopback.isServer) {
|
2014-11-04 18:59:38 +00:00
|
|
|
/*!
|
2014-11-03 09:00:24 +00:00
|
|
|
* Path to a default favicon shipped with LoopBack.
|
|
|
|
*
|
|
|
|
* **Example**
|
|
|
|
*
|
|
|
|
* ```js
|
|
|
|
* app.use(require('serve-favicon')(loopback.faviconFile));
|
|
|
|
* ```
|
|
|
|
*/
|
|
|
|
loopback.faviconFile = path.resolve(__dirname, '../favicon.ico');
|
|
|
|
}
|
|
|
|
|
2013-07-16 18:05:38 +00:00
|
|
|
/**
|
|
|
|
* Add a remote method to a model.
|
|
|
|
* @param {Function} fn
|
|
|
|
* @param {Object} options (optional)
|
|
|
|
*/
|
|
|
|
|
2014-10-30 20:49:47 +00:00
|
|
|
loopback.remoteMethod = function(fn, options) {
|
2013-07-16 18:05:38 +00:00
|
|
|
fn.shared = true;
|
2014-10-30 20:49:47 +00:00
|
|
|
if (typeof options === 'object') {
|
|
|
|
Object.keys(options).forEach(function(key) {
|
2013-07-16 18:05:38 +00:00
|
|
|
fn[key] = options[key];
|
|
|
|
});
|
|
|
|
}
|
2016-11-15 21:46:23 +00:00
|
|
|
fn.http = fn.http || {verb: 'get'};
|
2014-02-14 18:31:30 +00:00
|
|
|
};
|
2013-07-16 18:05:38 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a template helper.
|
|
|
|
*
|
|
|
|
* var render = loopback.template('foo.ejs');
|
|
|
|
* var html = render({foo: 'bar'});
|
|
|
|
*
|
2017-10-04 07:26:17 +00:00
|
|
|
* @param {String} file Path to the template file.
|
2013-07-16 18:05:38 +00:00
|
|
|
* @returns {Function}
|
|
|
|
*/
|
|
|
|
|
2014-10-30 20:49:47 +00:00
|
|
|
loopback.template = function(file) {
|
2013-07-16 18:05:38 +00:00
|
|
|
var templates = this._templates || (this._templates = {});
|
|
|
|
var str = templates[file] || (templates[file] = fs.readFileSync(file, 'utf8'));
|
2015-08-27 15:37:04 +00:00
|
|
|
return ejs.compile(str, {
|
2016-04-01 09:14:26 +00:00
|
|
|
filename: file,
|
2015-08-27 15:37:04 +00:00
|
|
|
});
|
2014-02-14 18:31:30 +00:00
|
|
|
};
|
2013-07-16 18:05:38 +00:00
|
|
|
|
2016-07-28 14:36:23 +00:00
|
|
|
require('../lib/current-context')(loopback);
|
2014-11-10 18:12:00 +00:00
|
|
|
|
2015-04-01 21:50:36 +00:00
|
|
|
/**
|
|
|
|
* Create a named vanilla JavaScript class constructor with an attached
|
|
|
|
* set of properties and options.
|
|
|
|
*
|
|
|
|
* This function comes with two variants:
|
|
|
|
* * `loopback.createModel(name, properties, options)`
|
|
|
|
* * `loopback.createModel(config)`
|
|
|
|
*
|
|
|
|
* In the second variant, the parameters `name`, `properties` and `options`
|
|
|
|
* are provided in the config object. Any additional config entries are
|
|
|
|
* interpreted as `options`, i.e. the following two configs are identical:
|
|
|
|
*
|
|
|
|
* ```js
|
|
|
|
* { name: 'Customer', base: 'User' }
|
|
|
|
* { name: 'Customer', options: { base: 'User' } }
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* **Example**
|
|
|
|
*
|
|
|
|
* Create an `Author` model using the three-parameter variant:
|
|
|
|
*
|
|
|
|
* ```js
|
|
|
|
* loopback.createModel(
|
|
|
|
* 'Author',
|
|
|
|
* {
|
|
|
|
* firstName: 'string',
|
|
|
|
* lastName: 'string'
|
|
|
|
* },
|
|
|
|
* {
|
|
|
|
* relations: {
|
|
|
|
* books: {
|
|
|
|
* model: 'Book',
|
|
|
|
* type: 'hasAndBelongsToMany'
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* );
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* Create the same model using a config object:
|
|
|
|
*
|
|
|
|
* ```js
|
|
|
|
* loopback.createModel({
|
|
|
|
* name: 'Author',
|
|
|
|
* properties: {
|
|
|
|
* firstName: 'string',
|
|
|
|
* lastName: 'string'
|
|
|
|
* },
|
|
|
|
* relations: {
|
|
|
|
* books: {
|
|
|
|
* model: 'Book',
|
|
|
|
* type: 'hasAndBelongsToMany'
|
|
|
|
* }
|
|
|
|
* }
|
|
|
|
* });
|
|
|
|
* ```
|
|
|
|
*
|
|
|
|
* @param {String} name Unique name.
|
|
|
|
* @param {Object} properties
|
|
|
|
* @param {Object} options (optional)
|
|
|
|
*
|
|
|
|
* @header loopback.createModel
|
|
|
|
*/
|
|
|
|
|
|
|
|
loopback.createModel = function(name, properties, options) {
|
|
|
|
return this.registry.createModel.apply(this.registry, arguments);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Alter an existing Model class.
|
|
|
|
* @param {Model} ModelCtor The model constructor to alter.
|
|
|
|
* @options {Object} config Additional configuration to apply
|
|
|
|
* @property {DataSource} dataSource Attach the model to a dataSource.
|
|
|
|
* @property {Object} [relations] Model relations to add/update.
|
|
|
|
*
|
|
|
|
* @header loopback.configureModel(ModelCtor, config)
|
|
|
|
*/
|
|
|
|
|
|
|
|
loopback.configureModel = function(ModelCtor, config) {
|
|
|
|
return this.registry.configureModel.apply(this.registry, arguments);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Look up a model class by name from all models created by
|
|
|
|
* `loopback.createModel()`
|
|
|
|
* @param {String} modelName The model name
|
|
|
|
* @returns {Model} The model class
|
|
|
|
*
|
|
|
|
* @header loopback.findModel(modelName)
|
|
|
|
*/
|
|
|
|
loopback.findModel = function(modelName) {
|
|
|
|
return this.registry.findModel.apply(this.registry, arguments);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Look up a model class by name from all models created by
|
|
|
|
* `loopback.createModel()`. Throw an error when no such model exists.
|
|
|
|
*
|
|
|
|
* @param {String} modelName The model name
|
|
|
|
* @returns {Model} The model class
|
|
|
|
*
|
|
|
|
* @header loopback.getModel(modelName)
|
|
|
|
*/
|
|
|
|
loopback.getModel = function(modelName) {
|
|
|
|
return this.registry.getModel.apply(this.registry, arguments);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Look up a model class by the base model class.
|
|
|
|
* The method can be used by LoopBack
|
|
|
|
* to find configured models in models.json over the base model.
|
|
|
|
* @param {Model} modelType The base model class
|
|
|
|
* @returns {Model} The subclass if found or the base class
|
|
|
|
*
|
|
|
|
* @header loopback.getModelByType(modelType)
|
|
|
|
*/
|
|
|
|
loopback.getModelByType = function(modelType) {
|
|
|
|
return this.registry.getModelByType.apply(this.registry, arguments);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a data source with passing the provided options to the connector.
|
|
|
|
*
|
|
|
|
* @param {String} name Optional name.
|
|
|
|
* @options {Object} options Data Source options
|
|
|
|
* @property {Object} connector LoopBack connector.
|
|
|
|
* @property {*} [*] Other connector properties.
|
|
|
|
* See the relevant connector documentation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
loopback.createDataSource = function(name, options) {
|
|
|
|
return this.registry.createDataSource.apply(this.registry, arguments);
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get an in-memory data source. Use one if it already exists.
|
|
|
|
*
|
|
|
|
* @param {String} [name] The name of the data source.
|
|
|
|
* If not provided, the `'default'` is used.
|
|
|
|
*/
|
|
|
|
|
|
|
|
loopback.memory = function(name) {
|
|
|
|
return this.registry.memory.apply(this.registry, arguments);
|
|
|
|
};
|
2014-03-11 01:05:44 +00:00
|
|
|
/*!
|
2013-07-16 18:05:38 +00:00
|
|
|
* Built in models / services
|
|
|
|
*/
|
|
|
|
|
2014-10-09 14:51:12 +00:00
|
|
|
require('./builtin-models')(loopback);
|
2015-04-01 21:50:36 +00:00
|
|
|
|
|
|
|
loopback.DataSource = juggler.DataSource;
|