diff --git a/docs/api-model-remote.md b/docs/api-model-remote.md index d45171a2..ae1255b1 100644 --- a/docs/api-model-remote.md +++ b/docs/api-model-remote.md @@ -32,8 +32,8 @@ The options argument is a JSON object, described in the following table. | Option | Required? | Description | | ----- | ----- | ----- | -| accepts | No | Describes the remote method's arguments, as explained below. The callback is an assumed argument; do not specify. | -| returns | No | Describes the remote method's callback arguments, as explained below.. The err argument is assumed; do not specify. | +| accepts | No | Describes the remote method's arguments; See Argument description. The `callback` argument is assumed; do not specify. | +| returns | No | Describes the remote method's callback arguments; See Argument description. The `err` argument is assumed; do not specify. | | http | No | HTTP routing information: @@ -42,8 +42,13 @@ The options argument is a JSON object, described in the following table. The arguments description defines either a single argument as an object or an ordered set of arguments as an array. Each individual argument has keys for: * arg: argument name - * type: argument datatype; must be a[loopback type](http://wiki.strongloop.com/display/DOC/LoopBack+types). + * type: argument datatype; must be a [loopback type](http://wiki.strongloop.com/display/DOC/LoopBack+types). * required: Boolean value indicating if argument is required. + * root: For callback arguments: set this property to `true` if your function + has a single callback argument to use as the root object + returned to remote caller. Otherwise the root object returned is a map (argument-name to argument-value). + * http: For input arguments: a function or an object describing mapping from HTTP request + to the argument value, as explained below. For example, a single argument, specified as an object: @@ -60,6 +65,58 @@ Multiple arguments, specified as an array: ] ``` + +**HTTP mapping of input arguments** + +There are two ways to specify HTTP mapping for input parameters (what the method accepts): + + * Provide an object with a `source` property + * Specify a custom mapping function + +To use the first way to specify HTTP mapping for input parameters, provide an object with a `source` property +that has one of the values shown in the following table. + +| Value of source property | Description | +|---|---| +| body | The whole request body is used as the value. | +| form | The value is looked up using `req.param`, which searches route arguments, the request body and the query string.| +| query | An alias for form (see above). | +| path | An alias for form (see above). | +| req | The whole HTTP reqest object is used as the value. | + +For example, an argument getting the whole request body as the value: + +```js +{ arg: 'data', type: 'object', http: { source: 'body' } } +``` + +The use the second way to specify HTTP mapping for input parameters, specify a custom mapping function +that looks like this: + +```js +{ + arg: 'custom', + type: 'number', + http: function(ctx) { + // ctx is LoopBack Context object + + // 1. Get the HTTP request object as provided by Express + var req = ctx.req; + + // 2. Get 'a' and 'b' from query string or form data + // and return their sum as the value + return +req.param('a') + req.param('b'); + } +} +``` + +If you don't specify a mapping, LoopBack will determine the value +as follows (assuming `name` as the name of the input parameter to resolve): + + 1. If there is a HTTP request parameter `args` with a JSON content, + then the value of `args['name']` is used if it is defined. + 2. Otherwise `req.param('name')` is returned. + ## Remote hooks Run a function before or after a remote method is called by a client. diff --git a/lib/application.js b/lib/application.js index 347004db..51896c9a 100644 --- a/lib/application.js +++ b/lib/application.js @@ -231,6 +231,25 @@ app.boot = function(options) { 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 = + 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; + if(appConfig.host !== undefined) { assert(typeof appConfig.host === 'string', 'app.host must be a string'); app.set('host', appConfig.host); diff --git a/package.json b/package.json index e3200f1e..f6853850 100644 --- a/package.json +++ b/package.json @@ -9,14 +9,13 @@ "Platform", "mBaaS" ], - "version": "1.3.1", + "version": "1.3.3", "scripts": { "test": "mocha -R spec" }, "dependencies": { "debug": "~0.7.2", "express": "~3.4.0", - "loopback-datasource-juggler": "~1.2.0", "strong-remoting": "~1.1.0", "inflection": "~1.2.5", "passport": "~0.1.17", @@ -29,7 +28,11 @@ "uid2": "0.0.3", "async": "~0.2.9" }, + "peerDependencies": { + "loopback-datasource-juggler": "~1.2.0" + }, "devDependencies": { + "loopback-datasource-juggler": "~1.2.0", "mocha": "~1.14.0", "strong-task-emitter": "0.0.x", "supertest": "~0.8.1" diff --git a/test/app.test.js b/test/app.test.js index bf47ff30..3787503a 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -77,6 +77,68 @@ describe('app', function() { assert.equal(this.app.get('host'), '127.0.0.1'); }); + 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); + + 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); + }); + + function randomHost() { + return Math.random().toString().split('.')[1]; + } + + function randomPort() { + return Math.floor(Math.random() * 10000); + } + }); + it('Instantiate models', function () { assert(app.models); assert(app.models.FooBarBatBaz);