Merge 0.9 into master

This commit is contained in:
Ritchie 2013-07-17 14:08:14 -07:00
commit a2308cbc1c
12 changed files with 344 additions and 86 deletions

View File

@ -13,7 +13,7 @@ v0.9.0
- [DataSource](#data-source)
- [Connectors](#connectors)
- [Loopback Types](#loopback-types)
- [GeoPoint](#geo-point)
- [GeoPoint](#geo-point)
- [REST Router](#rest-router)
- [Bundled Models](#bundled-models)
- [User](#user-model)

View File

@ -0,0 +1,45 @@
var models = require('../../lib/models');
var loopback = require('../../');
var app = loopback();
app.use(loopback.rest());
var dataSource = loopback.createDataSource('db', {connector: loopback.Memory});
var Application = models.Application(dataSource);
app.model(Application);
var data = {pushSettings: [
{ "platform": "apns",
"apns": {
"pushOptions": {
"gateway": "gateway.sandbox.push.apple.com",
"cert": "credentials/apns_cert_dev.pem",
"key": "credentials/apns_key_dev.pem"
},
"feedbackOptions": {
"gateway": "feedback.sandbox.push.apple.com",
"cert": "credentials/apns_cert_dev.pem",
"key": "credentials/apns_key_dev.pem",
"batchFeedback": true,
"interval": 300
}
}}
]}
Application.create(data, function(err, data) {
console.log('Created: ', data.toObject());
});
Application.register('MyApp', 'My first mobile application', 'rfeng', function (err, result) {
console.log(result.toObject());
result.resetKeys(function (err, result) {
console.log(result.toObject());
});
});

View File

@ -25,6 +25,21 @@ app.remotes = function () {
}
}
/**
* Expose an object or Class remotely.
*
* @param {String} name The remote namespace (eg. url base)
* @param {Object|Function} obj The object to remote
*/
app.remote = function (name, obj) {
// add the object to the remote exports
this.remotes().exports[name] = obj;
// clear the handlers cache
this._handlers = {};
}
/**
* Remove a route by reference.
*/
@ -44,6 +59,7 @@ app.disuse = function (route) {
*/
app._models = [];
app._services = [];
/**
* Expose a model.
@ -52,6 +68,8 @@ app._models = [];
*/
app.model = function (Model) {
var remotes = this.remotes();
this._models.push(Model);
Model.shared = true;
Model.app = this;
@ -67,22 +85,28 @@ app.models = function () {
}
/**
* Get all remote objects.
* Expose a service.
*
* @param {String} name
* @param {Service} service
*/
app.remoteObjects = function () {
var result = {};
var models = this.models();
app.service = function (name, service) {
this._services.push(service);
service.shared = true;
// add in models
models.forEach(function (ModelCtor) {
// only add shared models
if(ModelCtor.shared && typeof ModelCtor.sharedCtor === 'function') {
result[ModelCtor.pluralModelName] = ModelCtor;
}
});
return result;
service.app = this;
// add to the remote exports
this.remote(name, service);
}
/**
* Get all exposed services.
*/
app.services = function () {
return this._services;
}
/**
@ -91,4 +115,29 @@ app.remoteObjects = function () {
app.remotes = function () {
return this._remotes || (this._remotes = RemoteObjects.create());
}
}
/**
* Get a remotes handler.
*/
app.handler = function (type) {
var handler = this._handlers[type];
if(!handler) {
// get the sl remoting object
var remotes = this.remotes();
// create and save the handler
handler = this._handlers[type] = remotes.handler(type);
}
return handler;
}
/*!
* Handlers
*/
app._handlers = {};

View File

@ -17,21 +17,12 @@ module.exports = rest;
function rest() {
return function (req, res, next) {
var app = req.app;
var remotes = app.remotes();
// get all remote objects
var objs = app.remoteObjects();
// export remote objects
remotes.exports = objs;
var handler = remotes.handler('rest');
var handler = req.app.handler('rest');
if(req.url === '/routes') {
res.send(handler.adapter.allRoutes());
} else if(req.url === '/models') {
return res.send(remotes.toJSON());
return res.send(req.app.remotes().toJSON());
} else {
handler(req, res, next);
}

View File

@ -1,17 +1,25 @@
// Schema ACL options
/**
Schema ACL options
Object level permissions, for example, an album owned by a user
// Object level permissions
Factors to be authorized against:
// open: no protection
// none: always rejected
// owner: only the owner
// loggedIn: any logged in user
// roles: logged in users with the roles
// related: owner of the related objects
// Class level permissions
* model name: Album
* model instance properties: userId of the album, friends, shared
* methods
* app and/or user ids/roles
** loggedIn
** roles
** userId
** appId
** none
** everyone
** relations: owner/friend/granted
Class level permissions, for example, Album
* model name: Album
* methods
// blog posts
allow: ['owner', 'admin'] to: '*' // allow owner's of posts and admins to do anything
allow: '*' to: ['find', 'read'] // allow everyone to read and find
@ -27,5 +35,4 @@ allow: 'owner' to: ['*.destroy', '*.save']
// scopes
// URL level permissions
*/

View File

@ -1,17 +1,54 @@
// Application model
// Authentication schemes
var AuthenticationSchemeSchema = {
scheme: String, // local, facebook, google, twitter, linkedin, github
credential: Object // Scheme-specific credentials
}
var APNSSettingSchema = {
pushOptions: {type: {
gateway: String,
cert: String,
key: String
}},
feedbackOptions: {type: {
gateway: String,
cert: String,
key: String,
batchFeedback: Boolean,
interval: Number
}}
};
// Push notification settings
var PushNotificationSettingSchema = {
platform: {type: String, required: true}, // apns, gcm, mpns
// configuration: {type: Object} // platform-specific configurations
apns: APNSSettingSchema
}
/**
* Data model for Application
*/
var ApplicationSchema = {
// Basic information
id: {type: String, required: true},
name: {type: String, required: true},
description: String, // description
icon: String, // The icon url
public: Boolean,
permissions: [String],
id: {type: String, required: true}, // The id
name: {type: String, required: true}, // The name
description: String, // The description
icon: String, // The icon image url
userId: String,
owner: String, // The user id of the developer who registers the application
collaborators: [String], // A list of users ids who have permissions to work on this app
status: String,
// EMail
email: String, // e-mail address
emailVerified: Boolean, // Is the e-mail verified
// oAuth 2.0 settings
url: String, // The application url
callbackUrls: [String], // oAuth 2.0 code/token callback url
permissions: [String], // A list of permissions required by the application
// Keys
clientKey: String,
@ -21,21 +58,80 @@ var ApplicationSchema = {
masterKey: String,
// Push notification
pushPlatforms: [String],
pushCredentials: [],
pushSettings: [PushNotificationSettingSchema],
// Authentication
authenticationEnabled: Boolean,
anonymousAllowed: Boolean,
schemes: [String], // Basic, facebook, github, google
attachedCredentials: [],
// User Authentication
authenticationEnabled: {type: Boolean, default: true},
anonymousAllowed: {type: Boolean, default: true},
authenticationSchemes: [AuthenticationSchemeSchema],
// email
email: String, // e-mail address
emailVerified: Boolean, // Is the e-mail verified
status: {type: String, default: 'sandbox'}, // Status of the application, production/sandbox/disabled
collaborators: [String], // A list of users ids who have permissions to work on this app
created: Date,
lastUpdated: Date
// Timestamps
created: {type: Date, default: Date},
modified: {type: Date, default: Date}
};
/**
* Application management functions
*/
var crypto = require('crypto');
function generateKey(hmacKey, algorithm) {
hmacKey = hmacKey || 'loopback';
algorithm = algorithm || 'sha256';
var hmac = crypto.createHmac(algorithm, hmacKey);
var buf = crypto.randomBytes(64);
hmac.update(buf);
return hmac.digest('base64');
}
module.exports = function (dataSource) {
dataSource = dataSource || new require('loopback-data').ModelBuilder();
// var AuthenticationScheme = dataSource.define('AuthenticationScheme', AuthenticationSchemeSchema);
// ApplicationSchema.authenticationSchemes = [AuthenticationScheme];
// var PushNotificationSetting = dataSource.define('PushNotificationSetting', PushNotificationSettingSchema);
// ApplicationSchema.pushSettings = [PushNotificationSetting];
var Application = dataSource.define('Application', ApplicationSchema);
// Application.hasMany(AuthenticationScheme, {as: 'authenticationSchemes', foreignKey: 'appId'});
// Application.hasMany(PushNotificationSetting, {as: 'pushNotificationSettings', foreignKey: 'appId'});
Application.afterInitialize = function () {
var app = this;
// use data argument to update object
app.created = app.modified = new Date();
app.id = generateKey('id', 'sha1');
app.clientKey = generateKey('client');
app.javaScriptKey = generateKey('javaScript');
app.restApiKey = generateKey('restApi');
app.windowsKey = generateKey('windows');
app.masterKey = generateKey('master');
};
// Register a new application
Application.register = function (name, description, owner, cb) {
Application.create({name: name, description: description, owner: owner}, cb);
}
Application.prototype.resetKeys = function(cb) {
this.clientKey = generateKey('client');
this.javaScriptKey = generateKey('javaScript');
this.restApiKey = generateKey('restApi');
this.windowsKey = generateKey('windows');
this.masterKey = generateKey('master');
this.save(cb);
}
return Application;
}

6
lib/models/index.js Normal file
View File

@ -0,0 +1,6 @@
exports.Application = require('./application');
exports.ACL = require('./acl');
exports.Role = require('./role');
exports.Installation = require('./installation');

View File

@ -1,4 +1,26 @@
// Device registration
// See Device registration
var InstallationSchema = {
id: {
type: String,
required: true,
id: 1
},
appId: String, // Application id
appVersion: String, // Application version
userId: String, // User id
deviceToken: String,
deviceType: String,
subscriptions: [String],
status: {type: String, default: 'active'}, // Status of the application, production/sandbox/disabled
// Timestamps
created: {type: Date, default: Date},
modified: {type: Date, default: Date}
};
module.exports = function(dataSource) {
dataSource = dataSource || new require('loopback-data').ModelBuilder();
var Installation = dataSource.define('Installation', InstallationSchema);
return Installation;
}

View File

@ -1,11 +1,18 @@
// Role model
var RoleSchema = {
id: {type: String, required: true},
name: {type: String, required: true},
roles: [String],
users: [String],
acl: [],
id: {type: String, required: true}, // Id
name: {type: String, required: true}, // The name of a role
description: String, // Description
roles: [String], // A role can be an aggregate of other roles
users: [String], // A role contains a list of user ids
created: Date,
lastUpdated: Date
// Timestamps
created: {type: Date, default: Date},
modified: {type: Date, default: Date}
}
module.exports = function(dataSource) {
dataSource = dataSource || new require('loopback-data').ModelBuilder();
var Role = dataSource.define('Role', RoleSchema);
return Role;
}

View File

@ -22,6 +22,7 @@ var properties = {
email: String,
emailVerified: Boolean,
verificationToken: String,
credentials: [
'UserCredential' // User credentials, private or public, such as private/public keys, Kerberos tickets, oAuth tokens, facebook, google, github ids
],
@ -328,4 +329,4 @@ User.prototype.logout = function (fn) {
* Setup the base user.
*/
User.setup();
User.setup();

View File

@ -2,6 +2,12 @@
- [app](#app)
- [app.model(Model)](#app-appmodelmodel)
- [app.models()](#app-appmodels)
<<<<<<< HEAD
=======
- [loopback](#loopback)
- [loopback.createDataSource(options)](#loopback-loopbackcreatedatasourceoptions)
- [loopback.remoteMethod(Model, fn, [options]);](#loopback-loopbackremotemethodmodel-fn-options)
>>>>>>> master
- [DataSource](#datasource)
- [dataSource.createModel(name, properties, settings)](#datasource-datasourcecreatemodelname-properties-settings)
- [dataSource.operations()](#datasource-datasourceoperations)
@ -76,6 +82,48 @@ assert.equal(models.length, 1);
assert.equal(models[0].modelName, 'color');
```
<<<<<<< HEAD
=======
<a name="loopback"></a>
# loopback
<a name="loopback-loopbackcreatedatasourceoptions"></a>
## loopback.createDataSource(options)
Create a data source with a connector..
```js
var dataSource = loopback.createDataSource({
connector: loopback.Memory
});
assert(dataSource.connector());
```
<a name="loopback-loopbackremotemethodmodel-fn-options"></a>
## loopback.remoteMethod(Model, fn, [options]);
Setup a remote method..
```js
var Product = loopback.createModel('product', {price: Number});
Product.stats = function(fn) {
// ...
}
loopback.remoteMethod(
Product.stats,
{
returns: {arg: 'stats', type: 'array'},
http: {path: '/info', verb: 'get'}
}
);
assert.equal(Product.stats.returns.arg, 'stats');
assert.equal(Product.stats.returns.type, 'array');
assert.equal(Product.stats.http.path, '/info');
assert.equal(Product.stats.http.verb, 'get');
assert.equal(Product.stats.shared, true);
```
>>>>>>> master
<a name="datasource"></a>
# DataSource
<a name="datasource-datasourcecreatemodelname-properties-settings"></a>

View File

@ -1,6 +1,6 @@
describe('loopback', function() {
describe('loopback.createDataSource(options)', function(){
it('Create a data source with a connector', function() {
it('Create a data source with a connector.', function() {
var dataSource = loopback.createDataSource({
connector: loopback.Memory
});
@ -9,7 +9,7 @@ describe('loopback', function() {
});
describe('loopback.remoteMethod(Model, fn, [options]);', function() {
it("Setup a remote method", function() {
it("Setup a remote method.", function() {
var Product = loopback.createModel('product', {price: Number});
Product.stats = function(fn) {
@ -31,18 +31,4 @@ describe('loopback', function() {
assert.equal(Product.stats.shared, true);
});
});
describe('loopback.memory([name])', function(){
it('Get an in-memory data source. Use one if it already exists', function() {
var memory = loopback.memory();
assertValidDataSource(memory);
var m1 = loopback.memory();
var m2 = loopback.memory('m2');
var alsoM2 = loopback.memory('m2');
assert(m1 === memory);
assert(m1 !== m2);
assert(alsoM2 === m2);
});
});
});