Merge latest from master

This commit is contained in:
Ritchie Martori 2014-05-20 14:31:09 -07:00
commit d237ae5ddb
15 changed files with 290 additions and 170 deletions

194
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,194 @@
### Contributing ###
Thank you for your interest in `loopback`, an open source project
administered by StrongLoop.
Contributing to loopback is easy. In a few simple steps:
* Ensure that your effort is aligned with the projects roadmap by
talking to the maintainers, especially if you are going to spend a
lot of time on it. This project is currently maintained by
[@ritch](https://github.com/ritch), [@raymondfeng](https://github.com/raymondfeng),
and [@bajtos](https://github.com/bajtos). The preferred channel of communication
is [LoopBack Forum](https://groups.google.com/forum/#!forum/loopbackjs) or
[Github Issues](https://github.com/strongloop/loopback/issues).
* Make something better or fix a bug.
* Adhere to code style outlined in the
[Google Javascript Style Guide][].
* [Sign your patches](#signing-patches) to indicate that your are
making your contribution available under the terms of the
[Contributor License Agreement](#contributor-license-agreement).
* Submit a pull request through Github.
### Signing patches ###
Like many open source projects, we need a contributor license agreement
from you before we can merge in your changes.
In summary, by submitting your code, you are granting us a right to use
that code under the terms of this Agreement, including providing it to
others. You are also certifying that you wrote it, and that you are
allowed to license it to us. You are not giving up your copyright in
your work. The license does not change your rights to use your own
contributions for any other purpose.
Contributor License Agreements are important because they define the
chain of ownership of a piece of software. Some companies won't allow
the use of free software without clear agreements around code ownership.
That's why many open source projects collect similar agreements from
contributors. The CLA here is based on the Apache CLA.
To signify your agreement to these terms, add the following line to the
bottom of your commit message. Use your real name and an actual e-mail
address.
```
Signed-off-by: Random J Developer <random@developer.example.org>
```
Alternatively you can use the git command line to automatically add this
line, as follows:
```
$ git commit -sm "Replace rainbows by unicorns"
```
### Contributor License Agreement ###
```
Individual Contributor License Agreement
By signing this Individual Contributor License Agreement
("Agreement"), and making a Contribution (as defined below) to
StrongLoop, Inc. ("StrongLoop"), You (as defined below) accept and
agree to the following terms and conditions for Your present and
future Contributions submitted to StrongLoop. Except for the license
granted in this Agreement to StrongLoop and recipients of software
distributed by StrongLoop, You reserve all right, title, and interest
in and to Your Contributions.
1. Definitions
"You" or "Your" shall mean the copyright owner or the individual
authorized by the copyright owner that is entering into this
Agreement with StrongLoop.
"Contribution" shall mean any original work of authorship,
including any modifications or additions to an existing work, that
is intentionally submitted by You to StrongLoop for inclusion in,
or documentation of, any of the products owned or managed by
StrongLoop ("Work"). For purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication
sent to StrongLoop or its representatives, including but not
limited to communication or electronic mailing lists, source code
control systems, and issue tracking systems that are managed by,
or on behalf of, StrongLoop for the purpose of discussing and
improving the Work, but excluding communication that is
conspicuously marked or otherwise designated in writing by You as
"Not a Contribution."
2. You Grant a Copyright License to StrongLoop
Subject to the terms and conditions of this Agreement, You hereby
grant to StrongLoop and recipients of software distributed by
StrongLoop, a perpetual, worldwide, non-exclusive, no-charge,
royalty-free, irrevocable copyright license to reproduce, prepare
derivative works of, publicly display, publicly perform,
sublicense, and distribute Your Contributions and such derivative
works under any license and without any restrictions.
3. You Grant a Patent License to StrongLoop
Subject to the terms and conditions of this Agreement, You hereby
grant to StrongLoop and to recipients of software distributed by
StrongLoop a perpetual, worldwide, non-exclusive, no-charge,
royalty-free, irrevocable (except as stated in this Section)
patent license to make, have made, use, offer to sell, sell,
import, and otherwise transfer the Work under any license and
without any restrictions. The patent license You grant to
StrongLoop under this Section applies only to those patent claims
licensable by You that are necessarily infringed by Your
Contributions(s) alone or by combination of Your Contributions(s)
with the Work to which such Contribution(s) was submitted. If any
entity institutes a patent litigation against You or any other
entity (including a cross-claim or counterclaim in a lawsuit)
alleging that Your Contribution, or the Work to which You have
contributed, constitutes direct or contributory patent
infringement, any patent licenses granted to that entity under
this Agreement for that Contribution or Work shall terminate as
of the date such litigation is filed.
4. You Have the Right to Grant Licenses to StrongLoop
You represent that You are legally entitled to grant the licenses
in this Agreement.
If Your employer(s) has rights to intellectual property that You
create, You represent that You have received permission to make
the Contributions on behalf of that employer, that Your employer
has waived such rights for Your Contributions, or that Your
employer has executed a separate Corporate Contributor License
Agreement with StrongLoop.
5. The Contributions Are Your Original Work
You represent that each of Your Contributions are Your original
works of authorship (see Section 8 (Submissions on Behalf of
Others) for submission on behalf of others). You represent that to
Your knowledge, no other person claims, or has the right to claim,
any right in any intellectual property right related to Your
Contributions.
You also represent that You are not legally obligated, whether by
entering into an agreement or otherwise, in any way that conflicts
with the terms of this Agreement.
You represent that Your Contribution submissions include complete
details of any third-party license or other restriction (including,
but not limited to, related patents and trademarks) of which You
are personally aware and which are associated with any part of
Your Contributions.
6. You Don't Have an Obligation to Provide Support for Your Contributions
You are not expected to provide support for Your Contributions,
except to the extent You desire to provide support. You may provide
support for free, for a fee, or not at all.
6. No Warranties or Conditions
StrongLoop acknowledges that unless required by applicable law or
agreed to in writing, You provide Your Contributions on an "AS IS"
BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER
EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES
OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR
FITNESS FOR A PARTICULAR PURPOSE.
7. Submission on Behalf of Others
If You wish to submit work that is not Your original creation, You
may submit it to StrongLoop separately from any Contribution,
identifying the complete details of its source and of any license
or other restriction (including, but not limited to, related
patents, trademarks, and license agreements) of which You are
personally aware, and conspicuously marking the work as
"Submitted on Behalf of a Third-Party: [named here]".
8. Agree to Notify of Change of Circumstances
You agree to notify StrongLoop of any facts or circumstances of
which You become aware that would make these representations
inaccurate in any respect. Email us at callback@strongloop.com.
```
[Google Javascript Style Guide]: https://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
[license]: LICENSE

View File

@ -1,4 +1,4 @@
Copyright (c) 2013-2014 StrongLoop, Inc.
Copyright (c) 2013-2014 StrongLoop, Inc and other contributors.
loopback uses a 'dual license' model. Users may use loopback under the terms of
the MIT license, or under the StrongLoop License. The text of both is included
@ -26,7 +26,12 @@ THE SOFTWARE.
StrongLoop License
You may obtain a copy of the License at
http://www.strongloop.com/license/
STRONGLOOP SUBSCRIPTION AGREEMENT
PLEASE READ THIS AGREEMENT CAREFULLY BEFORE YOU AGREE TO THESE TERMS. IF YOU
ARE ACTING ON BEHALF OF AN ENTITY, THEN YOU REPRESENT THAT YOU HAVE THE
AUTHORITY TO ENTER INTO THIS AGREEMENT ON BEHALF OF THAT ENTITY. IF YOU DO NOT

View File

@ -1,29 +1,78 @@
# LoopBack
LoopBack is a highly extensible, open source Node.js framework that enables you to:
For a quick introduction and overview, see http://loopback.io/.
* Create dynamic end-to-end REST APIs with little or no coding
* Easily access data from Oracle, MySQL, PostgreSQL, MS SQL Server, MongoDB, SOAP and other REST APIs
* Incorporate model relationships and access controls for complex APIs
* Run your application on-premises or in the cloud
* Use built-in push, geolocation, and file services for mobile use cases
* Easily create client apps using Android, iOS, and JavaScript SDKs
## Documentation
LoopBack consists of:
* A library of Node.js modules.
* A command line tool, `slc`, for creating and working with LoopBack applications.
* Client SDKs for native and web-based mobile clients.
[See the full documentation](http://docs.strongloop.com/display/DOC/LoopBack).
For more details, see http://loopback.io/.
## API
## LoopBack modules
[Browse the API documentation](http://apidocs.strongloop.com/loopback).
In addition to the [main LoopBack module](https://github.com/strongloop/loopback), LoopBack consists of numerous other modules that implement specific functionality,
as illustrated below:
## Mailing List
![LoopBack modules](./docs/assets/lb-modules.png "LoopBack modules")
Discuss features and ask questions on [LoopBack Forum](https://groups.google.com/forum/#!forum/loopbackjs).
* Frameworks
* [loopback](https://github.com/strongloop/loopback)
* [loopback-datasource-juggler](https://github.com/strongloop/loopback-datasource-juggler)
* [strong-remoting](https://github.com/strongloop/strong-remoting)
## Issues
* Enterprise Connectors
* [loopback-connector-mongodb](https://github.com/strongloop/loopback-connector-mongodb)
* [loopback-connector-mysql](https://github.com/strongloop/loopback-connector-mysql)
* [loopback-connector-oracle](https://github.com/strongloop/loopback-connector-oracle)
* [loopback-connector-postgresql](https://github.com/strongloop/loopback-connector-postgresql)
* [loopback-connector-rest](https://github.com/strongloop/loopback-connector-rest)
* [loopback-connector-soap](https://github.com/strongloop/loopback-connector-soap)
File any issues [here on GitHub](https://github.com/strongloop/loopback/issues).
* Mobile services
* [loopback-push-notification](https://github.com/strongloop/loopback-push-notification)
* [loopback-storage-service](https://github.com/strongloop/loopback-storage-service)
## Features
* Clients
* [loopback-ios](https://github.com/strongloop/loopback-ios)
* [loopback-remoting-ios](https://github.com/strongloop/loopback-remoting-ios)
* [loopback-android](https://github.com/strongloop/loopback-android)
* [loopback-remoting-android](https://github.com/strongloop/loopback-remoting-android)
* [loopback-angular](https://github.com/strongloop/loopback-angular)
Read about the [features of LoopBack](https://github.com/strongloop/loopback/wiki/Features).
* Tools
* [loopback-explorer](https://github.com/strongloop/loopback-explorer)
* [loopback-workspace](https://github.com/strongloop/loopback-workspace)
* [strong-cli](https://github.com/strongloop/strong-cli)
* Examples
* [loopback-example-full-stack](https://github.com/strongloop/loopback-example-full-stack)
* [loopback-example-office-supplies](https://github.com/strongloop/loopback-example-office-supplies)
* [loopback-example-todo](https://github.com/strongloop/loopback-example-todo)
* [loopback-example-access-control](https://github.com/strongloop/loopback-example-access-control)
* [loopback-example-proxy](https://github.com/strongloop/loopback-example-proxy)
* [strongloop-community/loopback-example-datagraph](https://github.com/strongloop-community/loopback-example-datagraph)
* [strongloop-community/loopback-mysql-example](https://github.com/strongloop-community/loopback-mysql-example)
* [strongloop-community/loopback-examples-ios](https://github.com/strongloop-community/loopback-examples-ios)
* [strongloop-community/loopback-example-ssl](https://github.com/strongloop-community/loopback-example-ssl)
## Resources
* [Documentation](http://docs.strongloop.com/display/DOC/LoopBack).
* [API documentation](http://apidocs.strongloop.com/loopback).
* [LoopBack Google Group](https://groups.google.com/forum/#!forum/loopbackjs).
* [GitHub issues](https://github.com/strongloop/loopback/issues).
* Read more about the [LoopBack's features](https://github.com/strongloop/loopback/wiki/Features).
## Contributing
Please read [CONTRIBUTING.md](CONTRIBUTING.md) on how to contribute.
A brief guide for [contributing to LoopBack projects](https://github.com/strongloop/loopback/wiki/How-To-Contribute).
[![githalytics.com alpha](https://cruel-carlota.pagodabox.com/8caf6b5cce4be4d13c01ea9aafc9f407 "githalytics.com")](http://githalytics.com/strongloop/loopback)
[![Analytics](https://sl-beacon.appspot.com/UA-37775386-1/github/loopback/readme?pixel)](https://github.com/strongloop/loopback)

View File

@ -2,9 +2,7 @@
"title": "LoopBack Documentation",
"content": [
"lib/application.js",
{"title": "LoopBack", "depth": 3},
"lib/loopback.js",
{"title": "Middleware", "depth": 3},
"lib/middleware/token.js",
"lib/models/access-token.js",
"lib/models/access-context.js",

View File

@ -442,145 +442,6 @@ appearing in many groups, you could declare the models this way,
user.groups.add(group, callback); // connect an existing group with the user
user.groups.remove(group, callback); // remove the user from the group
```
### Validations
#### Model.validatesFormatOf(property, options)
Require a model to include a property that matches the given format.
```js
User.validatesFormat('name', {with: /\w+/});
```
#### Model.validatesPresenceOf(properties...)
Require a model to include a property to be considered valid.
```js
User.validatesPresenceOf('first', 'last', 'age');
```
#### Model.validatesLengthOf(property, options)
Require a property length to be within a specified range.
```js
User.validatesLengthOf('password', {min: 5, message: {min: 'Password is too short'}});
```
#### Model.validatesInclusionOf(property, options)
Require a value for `property` to be in the specified array.
```js
User.validatesInclusionOf('gender', {in: ['male', 'female']});
```
#### Model.validatesExclusionOf(property, options)
Require a value for `property` to not exist in the specified array.
```js
User.validatesExclusionOf('domain', {in: ['www', 'billing', 'admin']});
```
#### Model.validatesNumericalityOf(property, options)
Require a value for `property` to be a specific type of `Number`.
```js
User.validatesNumericalityOf('age', {int: true});
```
#### Model.validatesUniquenessOf(property, options)
Ensure the value for `property` is unique in the collection of models.
```js
User.validatesUniquenessOf('email', {message: 'email is not unique'});
```
**Note:** not available for all [connectors](#connectors).
Currently supported in these connectors:
- [In Memory](#memory-connector)
- [Oracle](http://github.com/strongloop/loopback-connector-oracle)
- [MongoDB](http://github.com/strongloop/loopback-connector-mongodb)
#### myModel.isValid()
Validate the model instance.
```js
user.isValid(function (valid) {
if (!valid) {
console.log(user.errors);
// => hash of errors
// => {
// => username: [errmessage, errmessage, ...],
// => email: ...
// => }
}
});
```
#### loopback.ValidationError
`ValidationError` is raised when the application attempts to save an invalid
model instance.
Example:
```js
{
"name": "ValidationError",
"status": 422,
"message": "The Model instance is not valid. \
See `details` property of the error object for more info.",
"statusCode": 422,
"details": {
"context": "user",
"codes": {
"password": [
"presence"
],
"email": [
"uniqueness"
]
},
"messages": {
"password": [
"can't be blank"
],
"email": [
"Email already exists"
]
}
},
}
```
You might run into situations where you need to raise a validation error
yourself, for example in a "before" hook or a custom model method.
```js
MyModel.prototype.preflight = function(changes, callback) {
// Update properties, do not save to db
for (var key in changes) {
model[key] = changes[key];
}
if (model.isValid()) {
return callback(null, { success: true });
}
// This line shows how to create a ValidationError
err = new ValidationError(model);
callback(err);
}
```
### Shared methods

BIN
docs/assets/lb-modules.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

View File

@ -3,7 +3,7 @@ var client = loopback();
var CartItem = require('./models').CartItem;
var remote = loopback.createDataSource({
connector: loopback.Remote,
root: 'http://localhost:3000'
url: 'http://localhost:3000'
});
client.model(CartItem);

View File

@ -7,6 +7,7 @@ var debug = require('debug')('loopback:security:access-context');
* resources
*
* @class
* @options {Object} context The context object
* @property {Principal[]} principals An array of principals
* @property {Function} model The model class
* @property {String} modelName The model name
@ -16,7 +17,6 @@ var debug = require('debug')('loopback:security:access-context');
* @property {String} accessType The access type
* @property {AccessToken} accessToken The access token
*
* @param {Object} context The context object
* @returns {AccessContext}
* @constructor
*/

View File

@ -296,6 +296,9 @@ ACL.getStaticACLs = function getStaticACLs(model, property) {
ACL.checkPermission = function checkPermission(principalType, principalId,
model, property, accessType,
callback) {
if(principalId !== null && principalId !== undefined && (typeof principalId !== 'string') ) {
principalId = principalId.toString();
}
property = property || ACL.ALL;
var propertyQuery = (property === ACL.ALL) ? undefined : {inq: [property, ACL.ALL]};
accessType = accessType || ACL.ALL;
@ -356,7 +359,7 @@ ACL.prototype.debug = function() {
* @property {String} accessType The access type
* @param {Function} callback
*/
ACL.checkAccess = function (context, callback) {
ACL.checkAccessForContext = function (context, callback) {
if(!(context instanceof AccessContext)) {
context = new AccessContext(context);
}
@ -422,7 +425,7 @@ ACL.checkAccess = function (context, callback) {
if(resolved && resolved.permission === ACL.DEFAULT) {
resolved.permission = (model && model.settings.defaultPermission) || ACL.ALLOW;
}
debug('checkAccess() returns: %j', resolved);
debug('checkAccessForContext() returns: %j', resolved);
callback && callback(null, resolved);
});
});
@ -455,7 +458,7 @@ ACL.checkAccessForToken = function (token, model, modelId, method, callback) {
context.debug();
this.checkAccess(context, function (err, access) {
this.checkAccessForContext(context, function (err, access) {
if (err) {
callback && callback(err);
return;

View File

@ -78,7 +78,7 @@ function convertNullToNotFoundError(ctx, cb) {
var modelName = ctx.method.sharedClass.name;
var id = ctx.getArgByName('id');
var msg = 'Unkown "' + modelName + '" id "' + id + '".';
var msg = 'Unknown "' + modelName + '" id "' + id + '".';
var error = new Error(msg);
error.statusCode = error.status = 404;
cb(error);

View File

@ -373,8 +373,14 @@ Role.isInRole = function (role, context, callback) {
async.some(context.principals, function (p, done) {
var principalType = p.type || undefined;
var principalId = p.id || undefined;
var roleId = result.id.toString();
if(principalId !== null && principalId !== undefined && (typeof principalId !== 'string') ) {
principalId = principalId.toString();
}
if (principalType && principalId) {
roleMappingModel.findOne({where: {roleId: result.id,
roleMappingModel.findOne({where: {roleId: roleId,
principalType: principalType, principalId: principalId}},
function (err, result) {
debug('Role mapping found: %j', result);

View File

@ -379,7 +379,7 @@ User.resetPassword = function(options, cb) {
options = options || {};
if(typeof options.email === 'string') {
UserModel.findOne({email: options.email}, function(err, user) {
UserModel.findOne({ where: {email: options.email} }, function(err, user) {
if(err) {
cb(err);
} else if(user) {
@ -392,7 +392,8 @@ User.resetPassword = function(options, cb) {
cb();
UserModel.emit('resetPasswordRequest', {
email: options.email,
accessToken: accessToken
accessToken: accessToken,
user: user
});
}
})

View File

@ -1,6 +1,7 @@
{
"name": "loopback",
"description": "LoopBack: Open Mobile Platform for Node.js",
"homepage": "http://loopback.io",
"keywords": [
"StrongLoop",
"LoopBack",
@ -9,7 +10,7 @@
"Platform",
"mBaaS"
],
"version": "1.8.0",
"version": "1.8.2",
"scripts": {
"test": "mocha -R spec"
},

View File

@ -268,7 +268,7 @@ describe('security ACLs', function () {
log('ACL 2: ', acl.toObject());
ACL.checkAccess({
ACL.checkAccessForContext({
principals: [
{type: ACL.USER, id: userId}
],
@ -279,7 +279,7 @@ describe('security ACLs', function () {
assert(!err && access.permission === ACL.ALLOW);
});
ACL.checkAccess({
ACL.checkAccessForContext({
principals: [
{type: ACL.ROLE, id: Role.EVERYONE}
],

View File

@ -3,8 +3,9 @@ describe('GeoPoint', function() {
it("Get the distance to another `GeoPoint`", function() {
var here = new GeoPoint({lat: 10, lng: 10});
var there = new GeoPoint({lat: 5, lng: 5});
assert.equal(here.distanceTo(there, {type: 'meters'}), 782777.923052584);
var distance = here.distanceTo(there, {type: 'meters'});
assert.equal(Math.floor(distance), 782777);
});
});
@ -12,8 +13,9 @@ describe('GeoPoint', function() {
it("Get the distance between two points", function() {
var here = new GeoPoint({lat: 10, lng: 10});
var there = new GeoPoint({lat: 5, lng: 5});
var distance = GeoPoint.distanceBetween(here, there, {type: 'feet'});
assert.equal(GeoPoint.distanceBetween(here, there, {type: 'feet'}), 2568169.038886431);
assert.equal(Math.floor(distance), 2568169);
});
});