From da0545bed6532997f4ee29340c6ec6d4569d7832 Mon Sep 17 00:00:00 2001 From: Ritchie Martori Date: Mon, 18 Nov 2013 16:13:40 -0800 Subject: [PATCH 1/4] Initial auto wiring for model dataSources --- lib/loopback.js | 72 +++++++++++++++++++++++++++++++++- lib/models/model.js | 2 +- test/access-token.test.js | 3 -- test/acl.test.js | 16 -------- test/email.test.js | 9 ----- test/model.application.test.js | 2 +- test/support.js | 15 +++++++ test/user.test.js | 16 +++----- 8 files changed, 93 insertions(+), 42 deletions(-) diff --git a/lib/loopback.js b/lib/loopback.js index 3de9b04c..f8712ca1 100644 --- a/lib/loopback.js +++ b/lib/loopback.js @@ -108,7 +108,9 @@ loopback.createDataSource = function (name, options) { */ loopback.createModel = function (name, properties, options) { - return loopback.Model.extend(name, properties, options); + var model = loopback.Model.extend(name, properties, options); + + return model; } /** @@ -173,6 +175,53 @@ loopback.getModel = function(modelName) { return loopback.Model.modelBuilder.models[modelName]; }; +/** + * Set the default `dataSource` for a given `type`. + */ + +loopback.setDefaultDataSourceForType = function(type, dataSource) { + var defaultDataSources = this.defaultDataSources || (this.defaultDataSources = {}); + + if(!(dataSource instanceof DataSource)) { + dataSource = this.createDataSource(dataSource); + } + + defaultDataSources[type] = dataSource; + return dataSource; +} + +/** + * Get the default `dataSource` for a given `type`. + */ + +loopback.getDefaultDataSourceForType = function(type) { + return this.defaultDataSources && this.defaultDataSources[type]; +} + +/** + * Attach any model that does not have a dataSource to + * the default dataSource for the type the Model requests + */ + +loopback.autoAttach = function() { + var models = this.Model.modelBuilder.models; + assert.equal(typeof models, 'object', 'Cannot autoAttach without a models object'); + var lb = this; + + Object.keys(models).forEach(function(modelName) { + var ModelCtor = models[modelName]; + + if(ModelCtor.autoAttach) { + var ds = lb.getDefaultDataSourceForType(ModelCtor.autoAttach); + + assert(ds instanceof DataSource, 'cannot autoAttach model "' + modelName + + '". No dataSource found of type ' + ModelCtor.attachTo); + + ModelCtor.attachTo(ds); + } + }); +} + /* * Built in models / services */ @@ -182,3 +231,24 @@ loopback.Email = require('./models/email'); loopback.User = require('./models/user'); loopback.Application = require('./models/application'); loopback.AccessToken = require('./models/access-token'); +loopback.Role = require('./models/role'); +loopback.ACL = require('./models/acl').ACL; +loopback.Scope = require('./models/acl').Scope; + + +/** + * Automatically attach these models to dataSources + */ + +var dataSourceTypes = { + DB: 'db', + MAIL: 'mail' +}; + +loopback.Email.autoAttach = dataSourceTypes.MAIL; +loopback.User.autoAttach = dataSourceTypes.DB; +loopback.AccessToken.autoAttach = dataSourceTypes.DB; +loopback.Role.autoAttach = dataSourceTypes.DB; +loopback.ACL.autoAttach = dataSourceTypes.DB; +loopback.Scope.autoAttach = dataSourceTypes.DB; +loopback.Application.autoAttach = dataSourceTypes.DB; diff --git a/lib/models/model.js b/lib/models/model.js index 9d2f2ef3..04f2372c 100644 --- a/lib/models/model.js +++ b/lib/models/model.js @@ -1,7 +1,7 @@ /** * Module Dependencies. */ - +var loopback = require('../loopback'); var ModelBuilder = require('loopback-datasource-juggler').ModelBuilder; var modeler = new ModelBuilder(); diff --git a/test/access-token.test.js b/test/access-token.test.js index d74ef153..ae48721f 100644 --- a/test/access-token.test.js +++ b/test/access-token.test.js @@ -1,9 +1,6 @@ var loopback = require('../'); var Token = loopback.AccessToken.extend('MyToken'); -// attach Token to testing memory ds -Token.attachTo(loopback.memory()); - describe('loopback.token(options)', function() { beforeEach(createTestingToken); diff --git a/test/acl.test.js b/test/acl.test.js index 0736a406..2df75544 100644 --- a/test/acl.test.js +++ b/test/acl.test.js @@ -14,18 +14,10 @@ function checkResult(err, result) { describe('security scopes', function () { it("should allow access to models for the given scope by wildcard", function () { - var ds = loopback.createDataSource({connector: loopback.Memory}); - Scope.attachTo(ds); - ACL.attachTo(ds); - - // console.log(Scope.relations); - Scope.create({name: 'userScope', description: 'access user information'}, function (err, scope) { - // console.log(scope); ACL.create({principalType: ACL.SCOPE, principalId: scope.id, model: 'User', property: ACL.ALL, accessType: ACL.ALL, permission: ACL.ALLOW}, function (err, resource) { - // console.log(resource); Scope.checkPermission('userScope', 'User', ACL.ALL, ACL.ALL, checkResult); Scope.checkPermission('userScope', 'User', 'name', ACL.ALL, checkResult); Scope.checkPermission('userScope', 'User', 'name', ACL.READ, checkResult); @@ -36,13 +28,7 @@ describe('security scopes', function () { it("should allow access to models for the given scope", function () { var ds = loopback.createDataSource({connector: loopback.Memory}); - Scope.attachTo(ds); - ACL.attachTo(ds); - - // console.log(Scope.relations); - Scope.create({name: 'userScope', description: 'access user information'}, function (err, scope) { - // console.log(scope); ACL.create({principalType: ACL.SCOPE, principalId: scope.id, model: 'User', property: 'name', accessType: ACL.READ, permission: ACL.ALLOW}, function (err, resource) { @@ -74,7 +60,6 @@ describe('security ACLs', function () { it("should allow access to models for the given principal by wildcard", function () { var ds = loopback.createDataSource({connector: loopback.Memory}); - ACL.attachTo(ds); ACL.create({principalType: ACL.USER, principalId: 'u001', model: 'User', property: ACL.ALL, accessType: ACL.ALL, permission: ACL.ALLOW}, function (err, acl) { @@ -117,7 +102,6 @@ describe('security ACLs', function () { {principalType: ACL.USER, principalId: 'u001', accessType: ACL.ALL, permission: ACL.ALLOW} ]; */ - ACL.attachTo(ds); ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.WRITE, function (err, perm) { assert(perm.permission === ACL.DENY); diff --git a/test/email.test.js b/test/email.test.js index 36727dca..91ff7650 100644 --- a/test/email.test.js +++ b/test/email.test.js @@ -1,17 +1,8 @@ var loopback = require('../'); -var MailConnector = loopback.Mail; var MyEmail = loopback.Email.extend('my-email'); var assert = require('assert'); describe('Email and SMTP', function () { - var mail = loopback.createDataSource({ - connector: MailConnector, - transports: [ - {type: 'STUB'} - ] - }); - - MyEmail.attachTo(mail); it('should have a send method', function () { assert(typeof MyEmail.send === 'function'); diff --git a/test/model.application.test.js b/test/model.application.test.js index 4ff6dbdb..d209cd54 100644 --- a/test/model.application.test.js +++ b/test/model.application.test.js @@ -27,7 +27,7 @@ describe('Application', function () { }); }); - it('Register a new application', function (done) { + beforeEach(function (done) { Application.register('rfeng', 'MyApp2', {description: 'My second mobile application'}, function (err, result) { var app = result; diff --git a/test/support.js b/test/support.js index 438e6148..15999e40 100644 --- a/test/support.js +++ b/test/support.js @@ -12,6 +12,21 @@ request = require('supertest'); beforeEach(function () { app = loopback(); + + // setup default data sources + loopback.setDefaultDataSourceForType('db', { + connector: loopback.Memory + }); + + loopback.setDefaultDataSourceForType('mail', { + connector: loopback.Mail, + transports: [ + {type: 'STUB'} + ] + }); + + // auto attach data sources to models + loopback.autoAttach(); }); assertValidDataSource = function (dataSource) { diff --git a/test/user.test.js b/test/user.test.js index bc2ea76f..0c3d7722 100644 --- a/test/user.test.js +++ b/test/user.test.js @@ -8,20 +8,14 @@ var userMemory = loopback.createDataSource({ }); describe('User', function(){ - - var mailDataSource = loopback.createDataSource({ - connector: MailConnector, - transports: [{type: 'STUB'}] - }); - User.attachTo(userMemory); - AccessToken.attachTo(userMemory); - // TODO(ritch) - this should be a default relationship - User.hasMany(AccessToken, {as: 'accessTokens', foreignKey: 'userId'}); - User.email.attachTo(mailDataSource); - // allow many User.afterRemote's to be called User.setMaxListeners(0); + // should be able to remove this with autoAttach + User.attachTo(loopback.memory()) + AccessToken.attachTo(loopback.memory()); + User.hasMany(AccessToken, {as: 'accessTokens', foreignKey: 'userId'}); + beforeEach(function (done) { app.use(loopback.rest()); app.model(User); From cb39ae7adb64892c3db49cde9b9fa1026361b3c2 Mon Sep 17 00:00:00 2001 From: Ritchie Martori Date: Tue, 19 Nov 2013 10:29:02 -0800 Subject: [PATCH 2/4] Debugging odd defineFK behavior --- test/model.application.test.js | 10 ---------- test/user.test.js | 8 ++++---- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/test/model.application.test.js b/test/model.application.test.js index d209cd54..7edf3e31 100644 --- a/test/model.application.test.js +++ b/test/model.application.test.js @@ -1,16 +1,11 @@ var loopback = require(('../')); var assert = require('assert'); - -var dataSource = loopback.createDataSource('db', {connector: loopback.Memory}); - var Application = loopback.Application; -Application.attachTo(dataSource); describe('Application', function () { var registeredApp = null; it('Create a new application', function (done) { - Application.create({owner: 'rfeng', name: 'MyApp1', description: 'My first mobile application'}, function (err, result) { var app = result; assert.equal(app.owner, 'rfeng'); @@ -28,7 +23,6 @@ describe('Application', function () { }); beforeEach(function (done) { - Application.register('rfeng', 'MyApp2', {description: 'My second mobile application'}, function (err, result) { var app = result; assert.equal(app.owner, 'rfeng'); @@ -47,7 +41,6 @@ describe('Application', function () { }); it('Reset keys', function (done) { - Application.resetKeys(registeredApp.id, function (err, result) { var app = result; assert.equal(app.owner, 'rfeng'); @@ -73,7 +66,6 @@ describe('Application', function () { }); it('Authenticate with application id & clientKey', function (done) { - Application.authenticate(registeredApp.id, registeredApp.clientKey, function (err, result) { assert.equal(result, 'clientKey'); done(err, result); @@ -81,7 +73,6 @@ describe('Application', function () { }); it('Authenticate with application id & javaScriptKey', function (done) { - Application.authenticate(registeredApp.id, registeredApp.javaScriptKey, function (err, result) { assert.equal(result, 'javaScriptKey'); done(err, result); @@ -116,6 +107,5 @@ describe('Application', function () { done(err, result); }); }); - }); diff --git a/test/user.test.js b/test/user.test.js index 0c3d7722..26b3ef90 100644 --- a/test/user.test.js +++ b/test/user.test.js @@ -11,10 +11,10 @@ describe('User', function(){ // allow many User.afterRemote's to be called User.setMaxListeners(0); - // should be able to remove this with autoAttach - User.attachTo(loopback.memory()) - AccessToken.attachTo(loopback.memory()); - User.hasMany(AccessToken, {as: 'accessTokens', foreignKey: 'userId'}); + before(function () { + debugger; + User.hasMany(AccessToken, {as: 'accessTokens', foreignKey: 'userId'}); + }); beforeEach(function (done) { app.use(loopback.rest()); From 178e5dab303d7f76fac4d7a286dd8af67285cb03 Mon Sep 17 00:00:00 2001 From: Ritchie Martori Date: Tue, 19 Nov 2013 11:02:43 -0800 Subject: [PATCH 3/4] Remove .attachTo() from tests --- lib/loopback.js | 4 +++- test/role.test.js | 14 -------------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/lib/loopback.js b/lib/loopback.js index f8712ca1..44892820 100644 --- a/lib/loopback.js +++ b/lib/loopback.js @@ -231,7 +231,8 @@ loopback.Email = require('./models/email'); loopback.User = require('./models/user'); loopback.Application = require('./models/application'); loopback.AccessToken = require('./models/access-token'); -loopback.Role = require('./models/role'); +loopback.Role = require('./models/role').Role; +loopback.RoleMapping = require('./models/role').RoleMapping; loopback.ACL = require('./models/acl').ACL; loopback.Scope = require('./models/acl').Scope; @@ -249,6 +250,7 @@ loopback.Email.autoAttach = dataSourceTypes.MAIL; loopback.User.autoAttach = dataSourceTypes.DB; loopback.AccessToken.autoAttach = dataSourceTypes.DB; loopback.Role.autoAttach = dataSourceTypes.DB; +loopback.RoleMapping.autoAttach = dataSourceTypes.DB; loopback.ACL.autoAttach = dataSourceTypes.DB; loopback.Scope.autoAttach = dataSourceTypes.DB; loopback.Application.autoAttach = dataSourceTypes.DB; diff --git a/test/role.test.js b/test/role.test.js index acd561d9..45d7385e 100644 --- a/test/role.test.js +++ b/test/role.test.js @@ -13,10 +13,6 @@ function checkResult(err, result) { describe('role model', function () { it("should define role/role relations", function () { - var ds = loopback.createDataSource({connector: 'memory'}); - Role.attachTo(ds); - RoleMapping.attachTo(ds); - Role.create({name: 'user'}, function (err, userRole) { Role.create({name: 'admin'}, function (err, adminRole) { userRole.principals.create({principalType: RoleMapping.ROLE, principalId: adminRole.id}, function (err, mapping) { @@ -41,11 +37,6 @@ describe('role model', function () { }); it("should define role/user relations", function () { - var ds = loopback.createDataSource({connector: 'memory'}); - User.attachTo(ds); - Role.attachTo(ds); - RoleMapping.attachTo(ds); - User.create({name: 'Raymond', email: 'x@y.com', password: 'foobar'}, function (err, user) { // console.log('User: ', user.id); Role.create({name: 'userRole'}, function (err, role) { @@ -75,11 +66,6 @@ describe('role model', function () { }); it("should support getRoles() and isInRole()", function () { - var ds = loopback.createDataSource({connector: 'memory'}); - User.attachTo(ds); - Role.attachTo(ds); - RoleMapping.attachTo(ds); - User.create({name: 'Raymond', email: 'x@y.com', password: 'foobar'}, function (err, user) { // console.log('User: ', user.id); Role.create({name: 'userRole'}, function (err, role) { From 9db8a7a25fb62af81d0f3446b34babb669e0890d Mon Sep 17 00:00:00 2001 From: Ritchie Martori Date: Tue, 19 Nov 2013 12:23:02 -0800 Subject: [PATCH 4/4] Auto attach all models created --- lib/application.js | 3 +++ lib/loopback.js | 29 +++++++++++++++++++++-------- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/lib/application.js b/lib/application.js index 590b83ab..a702913b 100644 --- a/lib/application.js +++ b/lib/application.js @@ -218,6 +218,9 @@ app.boot = function(options) { app.model(key, obj); }); + // attach models to dataSources by type + loopback.autoAttach(); + // require directories var requiredModels = requireDir(path.join(appRootDir, 'models')); } diff --git a/lib/loopback.js b/lib/loopback.js index 44892820..5204ec21 100644 --- a/lib/loopback.js +++ b/lib/loopback.js @@ -96,6 +96,11 @@ loopback.createDataSource = function (name, options) { ModelCtor.attachTo(ds); return ModelCtor; }; + + if(options && options.defaultForType) { + loopback.setDefaultDataSourceForType(options.defaultForType, ds); + } + return ds; }; @@ -110,6 +115,9 @@ loopback.createDataSource = function (name, options) { loopback.createModel = function (name, properties, options) { var model = loopback.Model.extend(name, properties, options); + // try to attach + loopback.autoAttachModel(model); + return model; } @@ -206,22 +214,27 @@ loopback.getDefaultDataSourceForType = function(type) { loopback.autoAttach = function() { var models = this.Model.modelBuilder.models; assert.equal(typeof models, 'object', 'Cannot autoAttach without a models object'); - var lb = this; Object.keys(models).forEach(function(modelName) { var ModelCtor = models[modelName]; - if(ModelCtor.autoAttach) { - var ds = lb.getDefaultDataSourceForType(ModelCtor.autoAttach); - - assert(ds instanceof DataSource, 'cannot autoAttach model "' + modelName - + '". No dataSource found of type ' + ModelCtor.attachTo); - - ModelCtor.attachTo(ds); + if(ModelCtor) { + loopback.autoAttachModel(ModelCtor); } }); } +loopback.autoAttachModel = function(ModelCtor) { + if(ModelCtor.autoAttach) { + var ds = loopback.getDefaultDataSourceForType(ModelCtor.autoAttach); + + assert(ds instanceof DataSource, 'cannot autoAttach model "' + ModelCtor.modelName + + '". No dataSource found of type ' + ModelCtor.attachTo); + + ModelCtor.attachTo(ds); + } +} + /* * Built in models / services */