diff --git a/README.md b/README.md index 46464037..c10345d5 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,71 @@ -# 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 +The LoopBack Node.js modules are illustrated below: -[Browse the API documentation](http://apidocs.strongloop.com/loopback). +![LoopBack modules](http://content.screencast.com/users/RaymondFeng/folders/Jing/media/74fcdafa-7d4b-4b9e-9f1f-f303b2b6adc4/00000065.png) -## Mailing List +* 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) -Discuss features and ask questions on [LoopBack Forum](https://groups.google.com/forum/#!forum/loopbackjs). +* 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) -## Issues +* Mobile services + * [loopback-push-notification](https://github.com/strongloop/loopback-push-notification) + * [loopback-storage-service](https://github.com/strongloop/loopback-storage-service) -File any issues [here on GitHub](https://github.com/strongloop/loopback/issues). +* 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) -## 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) -Read about the [features of LoopBack](https://github.com/strongloop/loopback/wiki/Features). +* 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 diff --git a/docs.json b/docs.json index 805f38ca..8c0894b5 100644 --- a/docs.json +++ b/docs.json @@ -13,8 +13,6 @@ "lib/models/data-model.js", "lib/models/role.js", "lib/models/user.js", - "docs/api-datasource.md", - "docs/api-geopoint.md", "docs/api-model.md", "docs/api-model-remote.md" ], diff --git a/docs/api-model.md b/docs/api-model.md index 5ec0beb2..7aaf3687 100644 --- a/docs/api-model.md +++ b/docs/api-model.md @@ -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 diff --git a/example/client-server/client.js b/example/client-server/client.js index 4e1e423c..436e266b 100644 --- a/example/client-server/client.js +++ b/example/client-server/client.js @@ -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); diff --git a/lib/models/acl.js b/lib/models/acl.js index b14ebabc..ce6f936f 100644 --- a/lib/models/acl.js +++ b/lib/models/acl.js @@ -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; diff --git a/lib/models/data-model.js b/lib/models/data-model.js index c3966036..4c0aeafd 100644 --- a/lib/models/data-model.js +++ b/lib/models/data-model.js @@ -66,7 +66,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); diff --git a/lib/models/role.js b/lib/models/role.js index 86ff5cc3..8b64796f 100644 --- a/lib/models/role.js +++ b/lib/models/role.js @@ -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); diff --git a/lib/models/user.js b/lib/models/user.js index 374fe7c4..73a18d39 100644 --- a/lib/models/user.js +++ b/lib/models/user.js @@ -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 }); } }) diff --git a/package.json b/package.json index 1ed4687e..52eac453 100644 --- a/package.json +++ b/package.json @@ -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.1", + "version": "1.8.2", "scripts": { "test": "mocha -R spec" }, @@ -29,10 +30,10 @@ "async": "~0.2.10" }, "peerDependencies": { - "loopback-datasource-juggler": "~1.3.11" + "loopback-datasource-juggler": "^1.3.11" }, "devDependencies": { - "loopback-datasource-juggler": "~1.3.11", + "loopback-datasource-juggler": "^1.3.11", "mocha": "~1.17.1", "strong-task-emitter": "0.0.x", "supertest": "~0.9.0", diff --git a/test/geo-point.test.js b/test/geo-point.test.js index 2f3da597..bc84f717 100644 --- a/test/geo-point.test.js +++ b/test/geo-point.test.js @@ -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); }); });