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:
- **http.path**: path (relative to the model) at which the method is exposed. May be a path fragment (for example, `/:myArg`) that will be populated by an arg of the same name in the `accepts` description. For example, the `stats` method above will be at the whole path `/products/stats`.
- **http.verb**: HTTP method (verb) from which the method is available (one of: get, post, put, del, or all).
@@ -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);