Merge pull request #2193 from strongloop/feature/eslint
Use eslint with loopback config
This commit is contained in:
commit
1e7adb21ae
|
@ -0,0 +1,2 @@
|
||||||
|
dist
|
||||||
|
coverage
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"extends": "loopback",
|
||||||
|
"rules": {
|
||||||
|
"max-len": ["error", 100, 4, {
|
||||||
|
"ignoreComments": true,
|
||||||
|
"ignoreUrls": true,
|
||||||
|
"ignorePattern": "^\\s*var\\s.+=\\s*(require\\s*\\()|(/)"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +0,0 @@
|
||||||
node_modules
|
|
34
.jshintrc
34
.jshintrc
|
@ -1,34 +0,0 @@
|
||||||
{
|
|
||||||
"node": true,
|
|
||||||
"camelcase": true,
|
|
||||||
"eqnull": true,
|
|
||||||
"indent": 2,
|
|
||||||
"undef": true,
|
|
||||||
"quotmark": "single",
|
|
||||||
"newcap": true,
|
|
||||||
"nonew": true,
|
|
||||||
"sub": true,
|
|
||||||
"laxcomma": true,
|
|
||||||
"laxbreak": true,
|
|
||||||
"globals": {
|
|
||||||
/* mocha */
|
|
||||||
"after": true,
|
|
||||||
"afterEach": true,
|
|
||||||
"assert": true,
|
|
||||||
"before": true,
|
|
||||||
"beforeEach": true,
|
|
||||||
"context": true,
|
|
||||||
"describe": true,
|
|
||||||
"expect": true,
|
|
||||||
"it": true,
|
|
||||||
|
|
||||||
/* loopback */
|
|
||||||
"app": true,
|
|
||||||
"assertValidDataSource": true,
|
|
||||||
"GeoPoint": true,
|
|
||||||
"loopback": true,
|
|
||||||
"memoryConnector": true,
|
|
||||||
"request": true,
|
|
||||||
"TaskEmitter": true
|
|
||||||
}
|
|
||||||
}
|
|
104
Gruntfile.js
104
Gruntfile.js
|
@ -1,6 +1,5 @@
|
||||||
/*global module:false*/
|
/*global module:false*/
|
||||||
module.exports = function(grunt) {
|
module.exports = function(grunt) {
|
||||||
|
|
||||||
// Do not report warnings from unit-tests exercising deprecated paths
|
// Do not report warnings from unit-tests exercising deprecated paths
|
||||||
process.env.NO_DEPRECATION = 'loopback';
|
process.env.NO_DEPRECATION = 'loopback';
|
||||||
|
|
||||||
|
@ -18,58 +17,59 @@ module.exports = function(grunt) {
|
||||||
// Task configuration.
|
// Task configuration.
|
||||||
uglify: {
|
uglify: {
|
||||||
options: {
|
options: {
|
||||||
banner: '<%= banner %>'
|
banner: '<%= banner %>',
|
||||||
},
|
},
|
||||||
dist: {
|
dist: {
|
||||||
files: {
|
files: {
|
||||||
'dist/loopback.min.js': ['dist/loopback.js']
|
'dist/loopback.min.js': ['dist/loopback.js'],
|
||||||
}
|
},
|
||||||
}
|
|
||||||
},
|
|
||||||
jshint: {
|
|
||||||
options: {
|
|
||||||
jshintrc: true
|
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
eslint: {
|
||||||
gruntfile: {
|
gruntfile: {
|
||||||
src: 'Gruntfile.js'
|
src: 'Gruntfile.js',
|
||||||
},
|
},
|
||||||
lib: {
|
lib: {
|
||||||
src: ['lib/**/*.js']
|
src: ['lib/**/*.js'],
|
||||||
},
|
},
|
||||||
common: {
|
common: {
|
||||||
src: ['common/**/*.js']
|
src: ['common/**/*.js'],
|
||||||
},
|
},
|
||||||
browser: {
|
browser: {
|
||||||
src: ['browser/**/*.js']
|
src: ['browser/**/*.js'],
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
src: ['server/**/*.js']
|
src: ['server/**/*.js'],
|
||||||
},
|
},
|
||||||
test: {
|
test: {
|
||||||
src: ['test/**/*.js']
|
src: ['test/**/*.js'],
|
||||||
}
|
},
|
||||||
},
|
|
||||||
jscs: {
|
|
||||||
gruntfile: 'Gruntfile.js',
|
|
||||||
lib: ['lib/**/*.js'],
|
|
||||||
common: ['common/**/*.js'],
|
|
||||||
server: ['server/**/*.js'],
|
|
||||||
browser: ['browser/**/*.js'],
|
|
||||||
test: ['test/**/*.js']
|
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
gruntfile: {
|
gruntfile: {
|
||||||
files: '<%= jshint.gruntfile.src %>',
|
files: '<%= eslint.gruntfile.src %>',
|
||||||
tasks: ['jshint:gruntfile']
|
tasks: ['eslint:gruntfile'],
|
||||||
|
},
|
||||||
|
browser: {
|
||||||
|
files: ['<%= eslint.browser.src %>'],
|
||||||
|
tasks: ['eslint:browser'],
|
||||||
|
},
|
||||||
|
common: {
|
||||||
|
files: ['<%= eslint.common.src %>'],
|
||||||
|
tasks: ['eslint:common'],
|
||||||
},
|
},
|
||||||
lib: {
|
lib: {
|
||||||
files: ['<%= jshint.lib.src %>'],
|
files: ['<%= eslint.lib.src %>'],
|
||||||
tasks: ['jshint:lib']
|
tasks: ['eslint:lib'],
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
files: ['<%= eslint.server.src %>'],
|
||||||
|
tasks: ['eslint:server'],
|
||||||
},
|
},
|
||||||
test: {
|
test: {
|
||||||
files: ['<%= jshint.test.src %>'],
|
files: ['<%= eslint.test.src %>'],
|
||||||
tasks: ['jshint:test']
|
tasks: ['eslint:test'],
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
browserify: {
|
browserify: {
|
||||||
dist: {
|
dist: {
|
||||||
|
@ -78,24 +78,24 @@ module.exports = function(grunt) {
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
ignore: ['nodemailer', 'passport', 'bcrypt'],
|
ignore: ['nodemailer', 'passport', 'bcrypt'],
|
||||||
standalone: 'loopback'
|
standalone: 'loopback',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
mochaTest: {
|
mochaTest: {
|
||||||
'unit': {
|
'unit': {
|
||||||
src: 'test/*.js',
|
src: 'test/*.js',
|
||||||
options: {
|
options: {
|
||||||
reporter: 'dot',
|
reporter: 'dot',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
'unit-xml': {
|
'unit-xml': {
|
||||||
src: 'test/*.js',
|
src: 'test/*.js',
|
||||||
options: {
|
options: {
|
||||||
reporter: 'xunit',
|
reporter: 'xunit',
|
||||||
captureFile: 'xunit.xml'
|
captureFile: 'xunit.xml',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
karma: {
|
karma: {
|
||||||
'unit-once': {
|
'unit-once': {
|
||||||
|
@ -109,7 +109,7 @@ module.exports = function(grunt) {
|
||||||
|
|
||||||
// CI friendly test output
|
// CI friendly test output
|
||||||
junitReporter: {
|
junitReporter: {
|
||||||
outputFile: 'karma-xunit.xml'
|
outputFile: 'karma-xunit.xml',
|
||||||
},
|
},
|
||||||
|
|
||||||
browserify: {
|
browserify: {
|
||||||
|
@ -117,8 +117,8 @@ module.exports = function(grunt) {
|
||||||
// Fatal error: Maximum call stack size exceeded
|
// Fatal error: Maximum call stack size exceeded
|
||||||
debug: false,
|
debug: false,
|
||||||
// Disable watcher, grunt will exit after the first run
|
// Disable watcher, grunt will exit after the first run
|
||||||
watch: false
|
watch: false,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
unit: {
|
unit: {
|
||||||
configFile: 'test/karma.conf.js',
|
configFile: 'test/karma.conf.js',
|
||||||
|
@ -134,7 +134,7 @@ module.exports = function(grunt) {
|
||||||
// list of files / patterns to load in the browser
|
// list of files / patterns to load in the browser
|
||||||
files: [
|
files: [
|
||||||
'test/e2e/remote-connector.e2e.js',
|
'test/e2e/remote-connector.e2e.js',
|
||||||
'test/e2e/replication.e2e.js'
|
'test/e2e/replication.e2e.js',
|
||||||
],
|
],
|
||||||
|
|
||||||
// list of files to exclude
|
// list of files to exclude
|
||||||
|
@ -171,7 +171,7 @@ module.exports = function(grunt) {
|
||||||
// - PhantomJS
|
// - PhantomJS
|
||||||
// - IE (only Windows)
|
// - IE (only Windows)
|
||||||
browsers: [
|
browsers: [
|
||||||
'Chrome'
|
'Chrome',
|
||||||
],
|
],
|
||||||
|
|
||||||
// If browser does not capture in given timeout [ms], kill it
|
// If browser does not capture in given timeout [ms], kill it
|
||||||
|
@ -190,7 +190,7 @@ module.exports = function(grunt) {
|
||||||
'passport-local',
|
'passport-local',
|
||||||
'superagent',
|
'superagent',
|
||||||
'supertest',
|
'supertest',
|
||||||
'bcrypt'
|
'bcrypt',
|
||||||
],
|
],
|
||||||
// transform: ['coffeeify'],
|
// transform: ['coffeeify'],
|
||||||
// debug: true,
|
// debug: true,
|
||||||
|
@ -199,19 +199,18 @@ module.exports = function(grunt) {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Add browserify to preprocessors
|
// Add browserify to preprocessors
|
||||||
preprocessors: {'test/e2e/*': ['browserify']}
|
preprocessors: { 'test/e2e/*': ['browserify'] },
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// These plugins provide necessary tasks.
|
// These plugins provide necessary tasks.
|
||||||
grunt.loadNpmTasks('grunt-browserify');
|
grunt.loadNpmTasks('grunt-browserify');
|
||||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
grunt.loadNpmTasks('grunt-contrib-uglify');
|
||||||
grunt.loadNpmTasks('grunt-contrib-jshint');
|
grunt.loadNpmTasks('grunt-eslint');
|
||||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||||
grunt.loadNpmTasks('grunt-jscs');
|
|
||||||
grunt.loadNpmTasks('grunt-karma');
|
grunt.loadNpmTasks('grunt-karma');
|
||||||
|
|
||||||
grunt.registerTask('e2e-server', function() {
|
grunt.registerTask('e2e-server', function() {
|
||||||
|
@ -229,10 +228,9 @@ module.exports = function(grunt) {
|
||||||
grunt.registerTask('default', ['browserify']);
|
grunt.registerTask('default', ['browserify']);
|
||||||
|
|
||||||
grunt.registerTask('test', [
|
grunt.registerTask('test', [
|
||||||
'jscs',
|
'eslint',
|
||||||
'jshint',
|
|
||||||
process.env.JENKINS_HOME ? 'mochaTest:unit-xml' : 'mochaTest:unit',
|
process.env.JENKINS_HOME ? 'mochaTest:unit-xml' : 'mochaTest:unit',
|
||||||
'karma:unit-once']);
|
'karma:unit-once']);
|
||||||
|
|
||||||
// alias for sl-ci-run and `npm test`
|
// alias for sl-ci-run and `npm test`
|
||||||
grunt.registerTask('mocha-and-karma', ['test']);
|
grunt.registerTask('mocha-and-karma', ['test']);
|
||||||
|
|
|
@ -27,7 +27,6 @@ var DEFAULT_TOKEN_LEN = 64;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = function(AccessToken) {
|
module.exports = function(AccessToken) {
|
||||||
|
|
||||||
// Workaround for https://github.com/strongloop/loopback/issues/292
|
// Workaround for https://github.com/strongloop/loopback/issues/292
|
||||||
AccessToken.definition.rawProperties.created.default =
|
AccessToken.definition.rawProperties.created.default =
|
||||||
AccessToken.definition.properties.created.default = function() {
|
AccessToken.definition.properties.created.default = function() {
|
||||||
|
@ -42,7 +41,7 @@ module.exports = function(AccessToken) {
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
|
|
||||||
AccessToken.ANONYMOUS = new AccessToken({id: '$anonymous'});
|
AccessToken.ANONYMOUS = new AccessToken({ id: '$anonymous' });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a cryptographically random access token id.
|
* Create a cryptographically random access token id.
|
||||||
|
@ -165,8 +164,7 @@ module.exports = function(AccessToken) {
|
||||||
var headers = options.headers || [];
|
var headers = options.headers || [];
|
||||||
var cookies = options.cookies || [];
|
var cookies = options.cookies || [];
|
||||||
var i = 0;
|
var i = 0;
|
||||||
var length;
|
var length, id;
|
||||||
var id;
|
|
||||||
|
|
||||||
// https://github.com/strongloop/loopback/issues/1326
|
// https://github.com/strongloop/loopback/issues/1326
|
||||||
if (options.searchDefaultTokenKeys !== false) {
|
if (options.searchDefaultTokenKeys !== false) {
|
||||||
|
|
|
@ -78,7 +78,6 @@ assert(Role, 'Role model must be defined before ACL model');
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = function(ACL) {
|
module.exports = function(ACL) {
|
||||||
|
|
||||||
ACL.ALL = AccessContext.ALL;
|
ACL.ALL = AccessContext.ALL;
|
||||||
|
|
||||||
ACL.DEFAULT = AccessContext.DEFAULT; // Not specified
|
ACL.DEFAULT = AccessContext.DEFAULT; // Not specified
|
||||||
|
@ -112,7 +111,8 @@ module.exports = function(ACL) {
|
||||||
score = score * 4;
|
score = score * 4;
|
||||||
var ruleValue = rule[props[i]] || ACL.ALL;
|
var ruleValue = rule[props[i]] || ACL.ALL;
|
||||||
var requestedValue = req[props[i]] || ACL.ALL;
|
var requestedValue = req[props[i]] || ACL.ALL;
|
||||||
var isMatchingMethodName = props[i] === 'property' && req.methodNames.indexOf(ruleValue) !== -1;
|
var isMatchingMethodName = props[i] === 'property' &&
|
||||||
|
req.methodNames.indexOf(ruleValue) !== -1;
|
||||||
|
|
||||||
var isMatchingAccessType = ruleValue === requestedValue;
|
var isMatchingAccessType = ruleValue === requestedValue;
|
||||||
if (props[i] === 'accessType' && !isMatchingAccessType) {
|
if (props[i] === 'accessType' && !isMatchingAccessType) {
|
||||||
|
@ -278,7 +278,7 @@ module.exports = function(ACL) {
|
||||||
principalType: acl.principalType,
|
principalType: acl.principalType,
|
||||||
principalId: acl.principalId, // TODO: Should it be a name?
|
principalId: acl.principalId, // TODO: Should it be a name?
|
||||||
accessType: acl.accessType || ACL.ALL,
|
accessType: acl.accessType || ACL.ALL,
|
||||||
permission: acl.permission
|
permission: acl.permission,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -300,7 +300,7 @@ module.exports = function(ACL) {
|
||||||
principalType: acl.principalType,
|
principalType: acl.principalType,
|
||||||
principalId: acl.principalId,
|
principalId: acl.principalId,
|
||||||
accessType: acl.accessType,
|
accessType: acl.accessType,
|
||||||
permission: acl.permission
|
permission: acl.permission,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -325,9 +325,10 @@ module.exports = function(ACL) {
|
||||||
principalId = principalId.toString();
|
principalId = principalId.toString();
|
||||||
}
|
}
|
||||||
property = property || ACL.ALL;
|
property = property || ACL.ALL;
|
||||||
var propertyQuery = (property === ACL.ALL) ? undefined : {inq: [property, ACL.ALL]};
|
var propertyQuery = (property === ACL.ALL) ? undefined : { inq: [property, ACL.ALL] };
|
||||||
accessType = accessType || ACL.ALL;
|
accessType = accessType || ACL.ALL;
|
||||||
var accessTypeQuery = (accessType === ACL.ALL) ? undefined : {inq: [accessType, ACL.ALL, ACL.EXECUTE]};
|
var accessTypeQuery = (accessType === ACL.ALL) ? undefined :
|
||||||
|
{ inq: [accessType, ACL.ALL, ACL.EXECUTE] };
|
||||||
|
|
||||||
var req = new AccessRequest(model, property, accessType);
|
var req = new AccessRequest(model, property, accessType);
|
||||||
|
|
||||||
|
@ -345,8 +346,8 @@ module.exports = function(ACL) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
this.find({where: {principalType: principalType, principalId: principalId,
|
this.find({ where: { principalType: principalType, principalId: principalId,
|
||||||
model: model, property: propertyQuery, accessType: accessTypeQuery}},
|
model: model, property: propertyQuery, accessType: accessTypeQuery }},
|
||||||
function(err, dynACLs) {
|
function(err, dynACLs) {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (callback) callback(err);
|
if (callback) callback(err);
|
||||||
|
@ -399,13 +400,13 @@ module.exports = function(ACL) {
|
||||||
var modelName = context.modelName;
|
var modelName = context.modelName;
|
||||||
|
|
||||||
var methodNames = context.methodNames;
|
var methodNames = context.methodNames;
|
||||||
var propertyQuery = (property === ACL.ALL) ? undefined : {inq: methodNames.concat([ACL.ALL])};
|
var propertyQuery = (property === ACL.ALL) ? undefined : { inq: methodNames.concat([ACL.ALL]) };
|
||||||
|
|
||||||
var accessTypeQuery = (accessType === ACL.ALL) ?
|
var accessTypeQuery = (accessType === ACL.ALL) ?
|
||||||
undefined :
|
undefined :
|
||||||
(accessType === ACL.REPLICATE) ?
|
(accessType === ACL.REPLICATE) ?
|
||||||
{inq: [ACL.REPLICATE, ACL.WRITE, ACL.ALL]} :
|
{ inq: [ACL.REPLICATE, ACL.WRITE, ACL.ALL] } :
|
||||||
{inq: [accessType, ACL.ALL]};
|
{ inq: [accessType, ACL.ALL] };
|
||||||
|
|
||||||
var req = new AccessRequest(modelName, property, accessType, ACL.DEFAULT, methodNames);
|
var req = new AccessRequest(modelName, property, accessType, ACL.DEFAULT, methodNames);
|
||||||
|
|
||||||
|
@ -414,8 +415,8 @@ module.exports = function(ACL) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var roleModel = registry.getModelByType(Role);
|
var roleModel = registry.getModelByType(Role);
|
||||||
this.find({where: {model: model.modelName, property: propertyQuery,
|
this.find({ where: { model: model.modelName, property: propertyQuery,
|
||||||
accessType: accessTypeQuery}}, function(err, acls) {
|
accessType: accessTypeQuery }}, function(err, acls) {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (callback) callback(err);
|
if (callback) callback(err);
|
||||||
return;
|
return;
|
||||||
|
@ -485,7 +486,7 @@ module.exports = function(ACL) {
|
||||||
model: model,
|
model: model,
|
||||||
property: method,
|
property: method,
|
||||||
method: method,
|
method: method,
|
||||||
modelId: modelId
|
modelId: modelId,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.checkAccessForContext(context, function(err, access) {
|
this.checkAccessForContext(context, function(err, access) {
|
||||||
|
@ -518,15 +519,15 @@ module.exports = function(ACL) {
|
||||||
this.resolveRelatedModels();
|
this.resolveRelatedModels();
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ACL.ROLE:
|
case ACL.ROLE:
|
||||||
this.roleModel.findOne({where: {or: [{name: id}, {id: id}]}}, cb);
|
this.roleModel.findOne({ where: { or: [{ name: id }, { id: id }] }}, cb);
|
||||||
break;
|
break;
|
||||||
case ACL.USER:
|
case ACL.USER:
|
||||||
this.userModel.findOne(
|
this.userModel.findOne(
|
||||||
{where: {or: [{username: id}, {email: id}, {id: id}]}}, cb);
|
{ where: { or: [{ username: id }, { email: id }, { id: id }] }}, cb);
|
||||||
break;
|
break;
|
||||||
case ACL.APP:
|
case ACL.APP:
|
||||||
this.applicationModel.findOne(
|
this.applicationModel.findOne(
|
||||||
{where: {or: [{name: id}, {email: id}, {id: id}]}}, cb);
|
{ where: { or: [{ name: id }, { email: id }, { id: id }] }}, cb);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
process.nextTick(function() {
|
process.nextTick(function() {
|
||||||
|
@ -559,8 +560,8 @@ module.exports = function(ACL) {
|
||||||
where: {
|
where: {
|
||||||
roleId: role.id,
|
roleId: role.id,
|
||||||
principalType: principalType,
|
principalType: principalType,
|
||||||
principalId: String(principalId)
|
principalId: String(principalId),
|
||||||
}
|
},
|
||||||
}, function(err, result) {
|
}, function(err, result) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
return cb(null, !!result);
|
return cb(null, !!result);
|
||||||
|
|
|
@ -65,7 +65,6 @@ function generateKey(hmacKey, algorithm, encoding) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = function(Application) {
|
module.exports = function(Application) {
|
||||||
|
|
||||||
// Workaround for https://github.com/strongloop/loopback/issues/292
|
// Workaround for https://github.com/strongloop/loopback/issues/292
|
||||||
Application.definition.rawProperties.created.default =
|
Application.definition.rawProperties.created.default =
|
||||||
Application.definition.properties.created.default = function() {
|
Application.definition.properties.created.default = function() {
|
||||||
|
@ -122,7 +121,7 @@ module.exports = function(Application) {
|
||||||
}
|
}
|
||||||
cb = cb || utils.createPromiseCallback();
|
cb = cb || utils.createPromiseCallback();
|
||||||
|
|
||||||
var props = {owner: owner, name: name};
|
var props = { owner: owner, name: name };
|
||||||
for (var p in options) {
|
for (var p in options) {
|
||||||
if (!(p in props)) {
|
if (!(p in props)) {
|
||||||
props[p] = options[p];
|
props[p] = options[p];
|
||||||
|
@ -195,7 +194,7 @@ module.exports = function(Application) {
|
||||||
if (app[keyNames[i]] === key) {
|
if (app[keyNames[i]] === key) {
|
||||||
result = {
|
result = {
|
||||||
application: app,
|
application: app,
|
||||||
keyType: keyNames[i]
|
keyType: keyNames[i],
|
||||||
};
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ var PersistedModel = require('../../lib/loopback').PersistedModel;
|
||||||
var loopback = require('../../lib/loopback');
|
var loopback = require('../../lib/loopback');
|
||||||
var utils = require('../../lib/utils');
|
var utils = require('../../lib/utils');
|
||||||
var crypto = require('crypto');
|
var crypto = require('crypto');
|
||||||
var CJSON = {stringify: require('canonical-json')};
|
var CJSON = { stringify: require('canonical-json') };
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var debug = require('debug')('loopback:change');
|
var debug = require('debug')('loopback:change');
|
||||||
|
@ -31,7 +31,6 @@ var deprecate = require('depd')('loopback');
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = function(Change) {
|
module.exports = function(Change) {
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Constants
|
* Constants
|
||||||
*/
|
*/
|
||||||
|
@ -154,7 +153,7 @@ module.exports = function(Change) {
|
||||||
var ch = new Change({
|
var ch = new Change({
|
||||||
id: id,
|
id: id,
|
||||||
modelName: modelName,
|
modelName: modelName,
|
||||||
modelId: modelId
|
modelId: modelId,
|
||||||
});
|
});
|
||||||
ch.debug('creating change');
|
ch.debug('creating change');
|
||||||
Change.updateOrCreate(ch, callback);
|
Change.updateOrCreate(ch, callback);
|
||||||
|
@ -400,7 +399,7 @@ module.exports = function(Change) {
|
||||||
callback = callback || utils.createPromiseCallback();
|
callback = callback || utils.createPromiseCallback();
|
||||||
|
|
||||||
if (!Array.isArray(remoteChanges) || remoteChanges.length === 0) {
|
if (!Array.isArray(remoteChanges) || remoteChanges.length === 0) {
|
||||||
callback(null, {deltas: [], conflicts: []});
|
callback(null, { deltas: [], conflicts: [] });
|
||||||
return callback.promise;
|
return callback.promise;
|
||||||
}
|
}
|
||||||
var remoteChangeIndex = {};
|
var remoteChangeIndex = {};
|
||||||
|
@ -415,8 +414,8 @@ module.exports = function(Change) {
|
||||||
this.find({
|
this.find({
|
||||||
where: {
|
where: {
|
||||||
modelName: modelName,
|
modelName: modelName,
|
||||||
modelId: {inq: modelIds}
|
modelId: { inq: modelIds },
|
||||||
}
|
},
|
||||||
}, function(err, allLocalChanges) {
|
}, function(err, allLocalChanges) {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
var deltas = [];
|
var deltas = [];
|
||||||
|
@ -462,7 +461,7 @@ module.exports = function(Change) {
|
||||||
|
|
||||||
callback(null, {
|
callback(null, {
|
||||||
deltas: deltas,
|
deltas: deltas,
|
||||||
conflicts: conflicts
|
conflicts: conflicts,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return callback.promise;
|
return callback.promise;
|
||||||
|
@ -586,12 +585,11 @@ module.exports = function(Change) {
|
||||||
var conflict = this;
|
var conflict = this;
|
||||||
var SourceModel = this.SourceModel;
|
var SourceModel = this.SourceModel;
|
||||||
var TargetModel = this.TargetModel;
|
var TargetModel = this.TargetModel;
|
||||||
var source;
|
var source, target;
|
||||||
var target;
|
|
||||||
|
|
||||||
async.parallel([
|
async.parallel([
|
||||||
getSourceModel,
|
getSourceModel,
|
||||||
getTargetModel
|
getTargetModel,
|
||||||
], done);
|
], done);
|
||||||
|
|
||||||
function getSourceModel(cb) {
|
function getSourceModel(cb) {
|
||||||
|
@ -627,12 +625,11 @@ module.exports = function(Change) {
|
||||||
|
|
||||||
Conflict.prototype.changes = function(cb) {
|
Conflict.prototype.changes = function(cb) {
|
||||||
var conflict = this;
|
var conflict = this;
|
||||||
var sourceChange;
|
var sourceChange, targetChange;
|
||||||
var targetChange;
|
|
||||||
|
|
||||||
async.parallel([
|
async.parallel([
|
||||||
getSourceChange,
|
getSourceChange,
|
||||||
getTargetChange
|
getTargetChange,
|
||||||
], done);
|
], done);
|
||||||
|
|
||||||
function getSourceChange(cb) {
|
function getSourceChange(cb) {
|
||||||
|
|
|
@ -16,7 +16,6 @@ var assert = require('assert');
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = function(Checkpoint) {
|
module.exports = function(Checkpoint) {
|
||||||
|
|
||||||
// Workaround for https://github.com/strongloop/loopback/issues/292
|
// Workaround for https://github.com/strongloop/loopback/issues/292
|
||||||
Checkpoint.definition.rawProperties.time.default =
|
Checkpoint.definition.rawProperties.time.default =
|
||||||
Checkpoint.definition.properties.time.default = function() {
|
Checkpoint.definition.properties.time.default = function() {
|
||||||
|
@ -37,8 +36,8 @@ module.exports = function(Checkpoint) {
|
||||||
};
|
};
|
||||||
|
|
||||||
Checkpoint._getSingleton = function(cb) {
|
Checkpoint._getSingleton = function(cb) {
|
||||||
var query = {limit: 1}; // match all instances, return only one
|
var query = { limit: 1 }; // match all instances, return only one
|
||||||
var initialData = {seq: 1};
|
var initialData = { seq: 1 };
|
||||||
this.findOrCreate(query, initialData, cb);
|
this.findOrCreate(query, initialData, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -55,7 +54,7 @@ module.exports = function(Checkpoint) {
|
||||||
var originalSeq = cp.seq;
|
var originalSeq = cp.seq;
|
||||||
cp.seq++;
|
cp.seq++;
|
||||||
// Update the checkpoint but only if it was not changed under our hands
|
// Update the checkpoint but only if it was not changed under our hands
|
||||||
Checkpoint.updateAll({id: cp.id, seq: originalSeq}, {seq: cp.seq}, function(err, info) {
|
Checkpoint.updateAll({ id: cp.id, seq: originalSeq }, { seq: cp.seq }, function(err, info) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
// possible outcomes
|
// possible outcomes
|
||||||
// 1) seq was updated to seq+1 - exactly what we wanted!
|
// 1) seq was updated to seq+1 - exactly what we wanted!
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = function(Email) {
|
module.exports = function(Email) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send an email with the given `options`.
|
* Send an email with the given `options`.
|
||||||
*
|
*
|
||||||
|
|
|
@ -15,18 +15,17 @@ assert(RoleMapping, 'RoleMapping model must be defined before Role model');
|
||||||
* @header Role object
|
* @header Role object
|
||||||
*/
|
*/
|
||||||
module.exports = function(Role) {
|
module.exports = function(Role) {
|
||||||
|
|
||||||
// Workaround for https://github.com/strongloop/loopback/issues/292
|
// Workaround for https://github.com/strongloop/loopback/issues/292
|
||||||
Role.definition.rawProperties.created.default =
|
Role.definition.rawProperties.created.default =
|
||||||
Role.definition.properties.created.default = function() {
|
Role.definition.properties.created.default = function() {
|
||||||
return new Date();
|
return new Date();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Workaround for https://github.com/strongloop/loopback/issues/292
|
// Workaround for https://github.com/strongloop/loopback/issues/292
|
||||||
Role.definition.rawProperties.modified.default =
|
Role.definition.rawProperties.modified.default =
|
||||||
Role.definition.properties.modified.default = function() {
|
Role.definition.properties.modified.default = function() {
|
||||||
return new Date();
|
return new Date();
|
||||||
};
|
};
|
||||||
|
|
||||||
Role.resolveRelatedModels = function() {
|
Role.resolveRelatedModels = function() {
|
||||||
if (!this.userModel) {
|
if (!this.userModel) {
|
||||||
|
@ -39,7 +38,6 @@ module.exports = function(Role) {
|
||||||
|
|
||||||
// Set up the connection to users/applications/roles once the model
|
// Set up the connection to users/applications/roles once the model
|
||||||
Role.once('dataSourceAttached', function(roleModel) {
|
Role.once('dataSourceAttached', function(roleModel) {
|
||||||
|
|
||||||
['users', 'applications', 'roles'].forEach(function(rel) {
|
['users', 'applications', 'roles'].forEach(function(rel) {
|
||||||
/**
|
/**
|
||||||
* Fetch all users assigned to this role
|
* Fetch all users assigned to this role
|
||||||
|
@ -64,14 +62,14 @@ module.exports = function(Role) {
|
||||||
var relsToModels = {
|
var relsToModels = {
|
||||||
users: roleModel.userModel,
|
users: roleModel.userModel,
|
||||||
applications: roleModel.applicationModel,
|
applications: roleModel.applicationModel,
|
||||||
roles: roleModel
|
roles: roleModel,
|
||||||
};
|
};
|
||||||
|
|
||||||
var ACL = loopback.ACL;
|
var ACL = loopback.ACL;
|
||||||
var relsToTypes = {
|
var relsToTypes = {
|
||||||
users: ACL.USER,
|
users: ACL.USER,
|
||||||
applications: ACL.APP,
|
applications: ACL.APP,
|
||||||
roles: ACL.ROLE
|
roles: ACL.ROLE,
|
||||||
};
|
};
|
||||||
|
|
||||||
var model = relsToModels[rel];
|
var model = relsToModels[rel];
|
||||||
|
@ -94,7 +92,7 @@ module.exports = function(Role) {
|
||||||
}
|
}
|
||||||
|
|
||||||
roleModel.roleMappingModel.find({
|
roleModel.roleMappingModel.find({
|
||||||
where: {roleId: this.id, principalType: principalType}
|
where: { roleId: this.id, principalType: principalType },
|
||||||
}, function(err, mappings) {
|
}, function(err, mappings) {
|
||||||
var ids;
|
var ids;
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -104,13 +102,12 @@ module.exports = function(Role) {
|
||||||
return m.principalId;
|
return m.principalId;
|
||||||
});
|
});
|
||||||
query.where = query.where || {};
|
query.where = query.where || {};
|
||||||
query.where.id = {inq: ids};
|
query.where.id = { inq: ids };
|
||||||
model.find(query, function(err, models) {
|
model.find(query, function(err, models) {
|
||||||
callback(err, models);
|
callback(err, models);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Special roles
|
// Special roles
|
||||||
|
@ -305,7 +302,6 @@ module.exports = function(Role) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var inRole = context.principals.some(function(p) {
|
var inRole = context.principals.some(function(p) {
|
||||||
|
|
||||||
var principalType = p.type || undefined;
|
var principalType = p.type || undefined;
|
||||||
var principalId = p.id || undefined;
|
var principalId = p.id || undefined;
|
||||||
|
|
||||||
|
@ -322,7 +318,7 @@ module.exports = function(Role) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var roleMappingModel = this.roleMappingModel;
|
var roleMappingModel = this.roleMappingModel;
|
||||||
this.findOne({where: {name: role}}, function(err, result) {
|
this.findOne({ where: { name: role }}, function(err, result) {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (callback) callback(err);
|
if (callback) callback(err);
|
||||||
return;
|
return;
|
||||||
|
@ -338,14 +334,15 @@ module.exports = function(Role) {
|
||||||
var principalType = p.type || undefined;
|
var principalType = p.type || undefined;
|
||||||
var principalId = p.id || undefined;
|
var principalId = p.id || undefined;
|
||||||
var roleId = result.id.toString();
|
var roleId = result.id.toString();
|
||||||
|
var principalIdIsString = typeof principalId === 'string';
|
||||||
|
|
||||||
if (principalId !== null && principalId !== undefined && (typeof principalId !== 'string')) {
|
if (principalId !== null && principalId !== undefined && !principalIdIsString) {
|
||||||
principalId = principalId.toString();
|
principalId = principalId.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (principalType && principalId) {
|
if (principalType && principalId) {
|
||||||
roleMappingModel.findOne({where: {roleId: roleId,
|
roleMappingModel.findOne({ where: { roleId: roleId,
|
||||||
principalType: principalType, principalId: principalId}},
|
principalType: principalType, principalId: principalId }},
|
||||||
function(err, result) {
|
function(err, result) {
|
||||||
debug('Role mapping found: %j', result);
|
debug('Role mapping found: %j', result);
|
||||||
done(!err && result); // The only arg is the result
|
done(!err && result); // The only arg is the result
|
||||||
|
@ -360,7 +357,6 @@ module.exports = function(Role) {
|
||||||
if (callback) callback(null, inRole);
|
if (callback) callback(null, inRole);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -421,8 +417,8 @@ module.exports = function(Role) {
|
||||||
if (principalType && principalId) {
|
if (principalType && principalId) {
|
||||||
// Please find() treat undefined matches all values
|
// Please find() treat undefined matches all values
|
||||||
inRoleTasks.push(function(done) {
|
inRoleTasks.push(function(done) {
|
||||||
roleMappingModel.find({where: {principalType: principalType,
|
roleMappingModel.find({ where: { principalType: principalType,
|
||||||
principalId: principalId}}, function(err, mappings) {
|
principalId: principalId }}, function(err, mappings) {
|
||||||
debug('Role mappings found: %s %j', err, mappings);
|
debug('Role mappings found: %s %j', err, mappings);
|
||||||
if (err) {
|
if (err) {
|
||||||
if (done) done(err);
|
if (done) done(err);
|
||||||
|
|
|
@ -36,7 +36,7 @@ module.exports = function(Scope) {
|
||||||
assert(aclModel,
|
assert(aclModel,
|
||||||
'ACL model must be defined before Scope.checkPermission is called');
|
'ACL model must be defined before Scope.checkPermission is called');
|
||||||
|
|
||||||
this.findOne({where: {name: scope}}, function(err, scope) {
|
this.findOne({ where: { name: scope }}, function(err, scope) {
|
||||||
if (err) {
|
if (err) {
|
||||||
if (callback) callback(err);
|
if (callback) callback(err);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -69,7 +69,6 @@ var debug = require('debug')('loopback:user');
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module.exports = function(User) {
|
module.exports = function(User) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create access token for the logged in user. This method can be overridden to
|
* Create access token for the logged in user. This method can be overridden to
|
||||||
* customize how access tokens are generated
|
* customize how access tokens are generated
|
||||||
|
@ -99,7 +98,7 @@ module.exports = function(User) {
|
||||||
var userModel = this.constructor;
|
var userModel = this.constructor;
|
||||||
ttl = Math.min(ttl || userModel.settings.ttl, userModel.settings.maxTTL);
|
ttl = Math.min(ttl || userModel.settings.ttl, userModel.settings.maxTTL);
|
||||||
this.accessTokens.create({
|
this.accessTokens.create({
|
||||||
ttl: ttl
|
ttl: ttl,
|
||||||
}, cb);
|
}, cb);
|
||||||
return cb.promise;
|
return cb.promise;
|
||||||
};
|
};
|
||||||
|
@ -216,7 +215,7 @@ module.exports = function(User) {
|
||||||
return fn.promise;
|
return fn.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.findOne({where: query}, function(err, user) {
|
self.findOne({ where: query }, function(err, user) {
|
||||||
var defaultError = new Error('login failed');
|
var defaultError = new Error('login failed');
|
||||||
defaultError.statusCode = 401;
|
defaultError.statusCode = 401;
|
||||||
defaultError.code = 'LOGIN_FAILED';
|
defaultError.code = 'LOGIN_FAILED';
|
||||||
|
@ -369,11 +368,14 @@ module.exports = function(User) {
|
||||||
assert(typeof options === 'object', 'options required when calling user.verify()');
|
assert(typeof options === 'object', 'options required when calling user.verify()');
|
||||||
assert(options.type, 'You must supply a verification type (options.type)');
|
assert(options.type, 'You must supply a verification type (options.type)');
|
||||||
assert(options.type === 'email', 'Unsupported verification type');
|
assert(options.type === 'email', 'Unsupported verification type');
|
||||||
assert(options.to || this.email, 'Must include options.to when calling user.verify() or the user must have an email property');
|
assert(options.to || this.email,
|
||||||
|
'Must include options.to when calling user.verify() ' +
|
||||||
|
'or the user must have an email property');
|
||||||
assert(options.from, 'Must include options.from when calling user.verify()');
|
assert(options.from, 'Must include options.from when calling user.verify()');
|
||||||
|
|
||||||
options.redirect = options.redirect || '/';
|
options.redirect = options.redirect || '/';
|
||||||
options.template = path.resolve(options.template || path.join(__dirname, '..', '..', 'templates', 'verify.ejs'));
|
var defaultTemplate = path.join(__dirname, '..', '..', 'templates', 'verify.ejs');
|
||||||
|
options.template = path.resolve(options.template || defaultTemplate);
|
||||||
options.user = this;
|
options.user = this;
|
||||||
options.protocol = options.protocol || 'http';
|
options.protocol = options.protocol || 'http';
|
||||||
|
|
||||||
|
@ -423,7 +425,8 @@ module.exports = function(User) {
|
||||||
function sendEmail(user) {
|
function sendEmail(user) {
|
||||||
options.verifyHref += '&token=' + user.verificationToken;
|
options.verifyHref += '&token=' + user.verificationToken;
|
||||||
|
|
||||||
options.text = options.text || 'Please verify your email by opening this link in a web browser:\n\t{href}';
|
options.text = options.text || 'Please verify your email by opening ' +
|
||||||
|
'this link in a web browser:\n\t{href}';
|
||||||
|
|
||||||
options.text = options.text.replace('{href}', options.verifyHref);
|
options.text = options.text.replace('{href}', options.verifyHref);
|
||||||
|
|
||||||
|
@ -440,7 +443,7 @@ module.exports = function(User) {
|
||||||
if (err) {
|
if (err) {
|
||||||
fn(err);
|
fn(err);
|
||||||
} else {
|
} else {
|
||||||
fn(null, {email: email, token: user.verificationToken, uid: user.id});
|
fn(null, { email: email, token: user.verificationToken, uid: user.id });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -531,7 +534,7 @@ module.exports = function(User) {
|
||||||
return cb.promise;
|
return cb.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
UserModel.findOne({ where: {email: options.email} }, function(err, user) {
|
UserModel.findOne({ where: { email: options.email }}, function(err, user) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return cb(err);
|
return cb(err);
|
||||||
}
|
}
|
||||||
|
@ -543,7 +546,7 @@ module.exports = function(User) {
|
||||||
}
|
}
|
||||||
// create a short lived access token for temp login to change password
|
// create a short lived access token for temp login to change password
|
||||||
// TODO(ritch) - eventually this should only allow password change
|
// TODO(ritch) - eventually this should only allow password change
|
||||||
user.accessTokens.create({ttl: ttl}, function(err, accessToken) {
|
user.accessTokens.create({ ttl: ttl }, function(err, accessToken) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return cb(err);
|
return cb(err);
|
||||||
}
|
}
|
||||||
|
@ -551,7 +554,7 @@ module.exports = function(User) {
|
||||||
UserModel.emit('resetPasswordRequest', {
|
UserModel.emit('resetPasswordRequest', {
|
||||||
email: options.email,
|
email: options.email,
|
||||||
accessToken: accessToken,
|
accessToken: accessToken,
|
||||||
user: user
|
user: user,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -633,10 +636,10 @@ module.exports = function(User) {
|
||||||
{
|
{
|
||||||
description: 'Login a user with username/email and password.',
|
description: 'Login a user with username/email and password.',
|
||||||
accepts: [
|
accepts: [
|
||||||
{arg: 'credentials', type: 'object', required: true, http: {source: 'body'}},
|
{ arg: 'credentials', type: 'object', required: true, http: { source: 'body' }},
|
||||||
{arg: 'include', type: ['string'], http: {source: 'query' },
|
{ arg: 'include', type: ['string'], http: { source: 'query' },
|
||||||
description: 'Related objects to include in the response. ' +
|
description: 'Related objects to include in the response. ' +
|
||||||
'See the description of return value for more details.'}
|
'See the description of return value for more details.' },
|
||||||
],
|
],
|
||||||
returns: {
|
returns: {
|
||||||
arg: 'accessToken', type: 'object', root: true,
|
arg: 'accessToken', type: 'object', root: true,
|
||||||
|
@ -644,9 +647,9 @@ module.exports = function(User) {
|
||||||
'The response body contains properties of the AccessToken created on login.\n' +
|
'The response body contains properties of the AccessToken created on login.\n' +
|
||||||
'Depending on the value of `include` parameter, the body may contain ' +
|
'Depending on the value of `include` parameter, the body may contain ' +
|
||||||
'additional properties:\n\n' +
|
'additional properties:\n\n' +
|
||||||
' - `user` - `{User}` - Data of the currently logged in user. (`include=user`)\n\n'
|
' - `user` - `{User}` - Data of the currently logged in user. (`include=user`)\n\n',
|
||||||
},
|
},
|
||||||
http: {verb: 'post'}
|
http: { verb: 'post' },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -655,17 +658,17 @@ module.exports = function(User) {
|
||||||
{
|
{
|
||||||
description: 'Logout a user with access token.',
|
description: 'Logout a user with access token.',
|
||||||
accepts: [
|
accepts: [
|
||||||
{arg: 'access_token', type: 'string', required: true, http: function(ctx) {
|
{ arg: 'access_token', type: 'string', required: true, http: function(ctx) {
|
||||||
var req = ctx && ctx.req;
|
var req = ctx && ctx.req;
|
||||||
var accessToken = req && req.accessToken;
|
var accessToken = req && req.accessToken;
|
||||||
var tokenID = accessToken && accessToken.id;
|
var tokenID = accessToken && accessToken.id;
|
||||||
|
|
||||||
return tokenID;
|
return tokenID;
|
||||||
}, description: 'Do not supply this argument, it is automatically extracted ' +
|
}, description: 'Do not supply this argument, it is automatically extracted ' +
|
||||||
'from request headers.'
|
'from request headers.',
|
||||||
}
|
},
|
||||||
],
|
],
|
||||||
http: {verb: 'all'}
|
http: { verb: 'all' },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -674,11 +677,11 @@ module.exports = function(User) {
|
||||||
{
|
{
|
||||||
description: 'Confirm a user registration with email verification token.',
|
description: 'Confirm a user registration with email verification token.',
|
||||||
accepts: [
|
accepts: [
|
||||||
{arg: 'uid', type: 'string', required: true},
|
{ arg: 'uid', type: 'string', required: true },
|
||||||
{arg: 'token', type: 'string', required: true},
|
{ arg: 'token', type: 'string', required: true },
|
||||||
{arg: 'redirect', type: 'string'}
|
{ arg: 'redirect', type: 'string' },
|
||||||
],
|
],
|
||||||
http: {verb: 'get', path: '/confirm'}
|
http: { verb: 'get', path: '/confirm' },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -687,9 +690,9 @@ module.exports = function(User) {
|
||||||
{
|
{
|
||||||
description: 'Reset password for a user with email.',
|
description: 'Reset password for a user with email.',
|
||||||
accepts: [
|
accepts: [
|
||||||
{arg: 'options', type: 'object', required: true, http: {source: 'body'}}
|
{ arg: 'options', type: 'object', required: true, http: { source: 'body' }},
|
||||||
],
|
],
|
||||||
http: {verb: 'post', path: '/reset'}
|
http: { verb: 'post', path: '/reset' },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -714,12 +717,12 @@ module.exports = function(User) {
|
||||||
// email validation regex
|
// email validation regex
|
||||||
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||||
|
|
||||||
UserModel.validatesFormatOf('email', {with: re, message: 'Must provide a valid email'});
|
UserModel.validatesFormatOf('email', { with: re, message: 'Must provide a valid email' });
|
||||||
|
|
||||||
// FIXME: We need to add support for uniqueness of composite keys in juggler
|
// FIXME: We need to add support for uniqueness of composite keys in juggler
|
||||||
if (!(UserModel.settings.realmRequired || UserModel.settings.realmDelimiter)) {
|
if (!(UserModel.settings.realmRequired || UserModel.settings.realmDelimiter)) {
|
||||||
UserModel.validatesUniquenessOf('email', {message: 'Email already exists'});
|
UserModel.validatesUniquenessOf('email', { message: 'Email already exists' });
|
||||||
UserModel.validatesUniquenessOf('username', {message: 'User already exists'});
|
UserModel.validatesUniquenessOf('username', { message: 'User already exists' });
|
||||||
}
|
}
|
||||||
|
|
||||||
return UserModel;
|
return UserModel;
|
||||||
|
@ -730,5 +733,4 @@ module.exports = function(User) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
User.setup();
|
User.setup();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +3,7 @@ var client = loopback();
|
||||||
var CartItem = require('./models').CartItem;
|
var CartItem = require('./models').CartItem;
|
||||||
var remote = loopback.createDataSource({
|
var remote = loopback.createDataSource({
|
||||||
connector: loopback.Remote,
|
connector: loopback.Remote,
|
||||||
url: 'http://localhost:3000'
|
url: 'http://localhost:3000',
|
||||||
});
|
});
|
||||||
|
|
||||||
client.model(CartItem);
|
client.model(CartItem);
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
var loopback = require('../../');
|
var loopback = require('../../');
|
||||||
|
|
||||||
var CartItem = exports.CartItem = loopback.PersistedModel.extend('CartItem', {
|
var CartItem = exports.CartItem = loopback.PersistedModel.extend('CartItem', {
|
||||||
tax: {type: Number, default: 0.1},
|
tax: { type: Number, default: 0.1 },
|
||||||
price: Number,
|
price: Number,
|
||||||
item: String,
|
item: String,
|
||||||
qty: {type: Number, default: 0},
|
qty: { type: Number, default: 0 },
|
||||||
cartId: Number
|
cartId: Number,
|
||||||
});
|
});
|
||||||
|
|
||||||
CartItem.sum = function(cartId, callback) {
|
CartItem.sum = function(cartId, callback) {
|
||||||
this.find({where: {cartId: 1}}, function(err, items) {
|
this.find({ where: { cartId: 1 }}, function(err, items) {
|
||||||
var total = items
|
var total = items
|
||||||
.map(function(item) {
|
.map(function(item) {
|
||||||
return item.total();
|
return item.total();
|
||||||
|
@ -20,15 +20,15 @@ CartItem.sum = function(cartId, callback) {
|
||||||
|
|
||||||
callback(null, total);
|
callback(null, total);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
CartItem.remoteMethod('sum',
|
CartItem.remoteMethod('sum',
|
||||||
{
|
{
|
||||||
accepts: {arg: 'cartId', type: 'number'},
|
accepts: { arg: 'cartId', type: 'number' },
|
||||||
returns: {arg: 'total', type: 'number'}
|
returns: { arg: 'total', type: 'number' },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
CartItem.prototype.total = function() {
|
CartItem.prototype.total = function() {
|
||||||
return this.price * this.qty * 1 + this.tax;
|
return this.price * this.qty * 1 + this.tax;
|
||||||
}
|
};
|
||||||
|
|
|
@ -2,7 +2,7 @@ var loopback = require('../../');
|
||||||
var server = module.exports = loopback();
|
var server = module.exports = loopback();
|
||||||
var CartItem = require('./models').CartItem;
|
var CartItem = require('./models').CartItem;
|
||||||
var memory = loopback.createDataSource({
|
var memory = loopback.createDataSource({
|
||||||
connector: loopback.Memory
|
connector: loopback.Memory,
|
||||||
});
|
});
|
||||||
|
|
||||||
server.use(loopback.rest());
|
server.use(loopback.rest());
|
||||||
|
@ -12,9 +12,9 @@ CartItem.attachTo(memory);
|
||||||
|
|
||||||
// test data
|
// test data
|
||||||
CartItem.create([
|
CartItem.create([
|
||||||
{item: 'red hat', qty: 6, price: 19.99, cartId: 1},
|
{ item: 'red hat', qty: 6, price: 19.99, cartId: 1 },
|
||||||
{item: 'green shirt', qty: 1, price: 14.99, cartId: 1},
|
{ item: 'green shirt', qty: 1, price: 14.99, cartId: 1 },
|
||||||
{item: 'orange pants', qty: 58, price: 9.99, cartId: 1}
|
{ item: 'orange pants', qty: 58, price: 9.99, cartId: 1 },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
CartItem.sum(1, function(err, total) {
|
CartItem.sum(1, function(err, total) {
|
||||||
|
|
|
@ -4,16 +4,16 @@ var app = loopback();
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
|
|
||||||
var schema = {
|
var schema = {
|
||||||
name: String
|
name: String,
|
||||||
};
|
};
|
||||||
|
|
||||||
var Color = app.model('color', schema);
|
var Color = app.model('color', schema);
|
||||||
|
|
||||||
app.dataSource('db', {adapter: 'memory'}).attach(Color);
|
app.dataSource('db', { adapter: 'memory' }).attach(Color);
|
||||||
|
|
||||||
Color.create({name: 'red'});
|
Color.create({ name: 'red' });
|
||||||
Color.create({name: 'green'});
|
Color.create({ name: 'green' });
|
||||||
Color.create({name: 'blue'});
|
Color.create({ name: 'blue' });
|
||||||
|
|
||||||
app.listen(3000);
|
app.listen(3000);
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ app.use(function saveHostToContext(req, res, next) {
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
|
|
||||||
var Color = loopback.createModel('color', { 'name': String });
|
var Color = loopback.createModel('color', { 'name': String });
|
||||||
Color.beforeRemote('**', function (ctx, unused, next) {
|
Color.beforeRemote('**', function(ctx, unused, next) {
|
||||||
// Inside LoopBack code, you can read the property from the context
|
// Inside LoopBack code, you can read the property from the context
|
||||||
var ns = loopback.getCurrentContext();
|
var ns = loopback.getCurrentContext();
|
||||||
console.log('Request to host', ns && ns.get('host'));
|
console.log('Request to host', ns && ns.get('host'));
|
||||||
|
|
|
@ -5,41 +5,42 @@ var app = loopback();
|
||||||
|
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
|
|
||||||
var dataSource = loopback.createDataSource('db', {connector: loopback.Memory});
|
var dataSource = loopback.createDataSource('db', { connector: loopback.Memory });
|
||||||
|
|
||||||
var Application = models.Application(dataSource);
|
var Application = models.Application(dataSource);
|
||||||
|
|
||||||
app.model(Application);
|
app.model(Application);
|
||||||
|
|
||||||
|
|
||||||
var data = {pushSettings: [
|
var data = { pushSettings: [
|
||||||
{ "platform": "apns",
|
{ 'platform': 'apns',
|
||||||
"apns": {
|
'apns': {
|
||||||
"pushOptions": {
|
'pushOptions': {
|
||||||
"gateway": "gateway.sandbox.push.apple.com",
|
'gateway': 'gateway.sandbox.push.apple.com',
|
||||||
"cert": "credentials/apns_cert_dev.pem",
|
'cert': 'credentials/apns_cert_dev.pem',
|
||||||
"key": "credentials/apns_key_dev.pem"
|
'key': 'credentials/apns_key_dev.pem',
|
||||||
},
|
},
|
||||||
|
|
||||||
"feedbackOptions": {
|
'feedbackOptions': {
|
||||||
"gateway": "feedback.sandbox.push.apple.com",
|
'gateway': 'feedback.sandbox.push.apple.com',
|
||||||
"cert": "credentials/apns_cert_dev.pem",
|
'cert': 'credentials/apns_cert_dev.pem',
|
||||||
"key": "credentials/apns_key_dev.pem",
|
'key': 'credentials/apns_key_dev.pem',
|
||||||
"batchFeedback": true,
|
'batchFeedback': true,
|
||||||
"interval": 300
|
'interval': 300,
|
||||||
}
|
},
|
||||||
}}
|
}},
|
||||||
]}
|
] };
|
||||||
|
|
||||||
Application.create(data, function(err, data) {
|
Application.create(data, function(err, data) {
|
||||||
console.log('Created: ', data.toObject());
|
console.log('Created: ', data.toObject());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
Application.register('rfeng', 'MyApp', {description: 'My first mobile application'}, function (err, result) {
|
Application.register('rfeng', 'MyApp', { description: 'My first mobile application' },
|
||||||
|
function(err, result) {
|
||||||
|
console.log(result.toObject());
|
||||||
|
|
||||||
|
result.resetKeys(function(err, result) {
|
||||||
console.log(result.toObject());
|
console.log(result.toObject());
|
||||||
|
});
|
||||||
result.resetKeys(function (err, result) {
|
|
||||||
console.log(result.toObject());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
var loopback = require('../../');
|
var loopback = require('../../');
|
||||||
var app = loopback();
|
var app = loopback();
|
||||||
var db = app.dataSource('db', {connector: loopback.Memory});
|
var db = app.dataSource('db', { connector: loopback.Memory });
|
||||||
var Color = app.model('color', {dataSource: 'db', options: {trackChanges: true}});
|
var Color = app.model('color', { dataSource: 'db', options: { trackChanges: true }});
|
||||||
var Color2 = app.model('color2', {dataSource: 'db', options: {trackChanges: true}});
|
var Color2 = app.model('color2', { dataSource: 'db', options: { trackChanges: true }});
|
||||||
var target = Color2;
|
var target = Color2;
|
||||||
var source = Color;
|
var source = Color;
|
||||||
var SPEED = process.env.SPEED || 100;
|
var SPEED = process.env.SPEED || 100;
|
||||||
|
@ -12,60 +12,60 @@ var steps = [
|
||||||
|
|
||||||
createSomeInitialSourceData,
|
createSomeInitialSourceData,
|
||||||
|
|
||||||
replicateSourceToTarget,
|
replicateSourceToTarget,
|
||||||
list.bind(this, source, 'current SOURCE data'),
|
list.bind(this, source, 'current SOURCE data'),
|
||||||
list.bind(this, target, 'current TARGET data'),
|
list.bind(this, target, 'current TARGET data'),
|
||||||
|
|
||||||
updateSomeTargetData,
|
updateSomeTargetData,
|
||||||
|
|
||||||
replicateSourceToTarget,
|
replicateSourceToTarget,
|
||||||
list.bind(this, source, 'current SOURCE data '),
|
list.bind(this, source, 'current SOURCE data '),
|
||||||
list.bind(this, target, 'current TARGET data (includes conflicting update)'),
|
list.bind(this, target, 'current TARGET data (includes conflicting update)'),
|
||||||
|
|
||||||
updateSomeSourceDataCausingAConflict,
|
updateSomeSourceDataCausingAConflict,
|
||||||
|
|
||||||
replicateSourceToTarget,
|
replicateSourceToTarget,
|
||||||
list.bind(this, source, 'current SOURCE data (now has a conflict)'),
|
list.bind(this, source, 'current SOURCE data (now has a conflict)'),
|
||||||
list.bind(this, target, 'current TARGET data (includes conflicting update)'),
|
list.bind(this, target, 'current TARGET data (includes conflicting update)'),
|
||||||
|
|
||||||
resolveAllConflicts,
|
resolveAllConflicts,
|
||||||
|
|
||||||
replicateSourceToTarget,
|
replicateSourceToTarget,
|
||||||
list.bind(this, source, 'current SOURCE data (conflict resolved)'),
|
list.bind(this, source, 'current SOURCE data (conflict resolved)'),
|
||||||
list.bind(this, target, 'current TARGET data (conflict resolved)'),
|
list.bind(this, target, 'current TARGET data (conflict resolved)'),
|
||||||
|
|
||||||
createMoreSourceData,
|
createMoreSourceData,
|
||||||
|
|
||||||
replicateSourceToTarget,
|
replicateSourceToTarget,
|
||||||
list.bind(this, source, 'current SOURCE data'),
|
list.bind(this, source, 'current SOURCE data'),
|
||||||
list.bind(this, target, 'current TARGET data'),
|
list.bind(this, target, 'current TARGET data'),
|
||||||
|
|
||||||
createEvenMoreSourceData,
|
createEvenMoreSourceData,
|
||||||
|
|
||||||
replicateSourceToTarget,
|
replicateSourceToTarget,
|
||||||
list.bind(this, source, 'current SOURCE data'),
|
list.bind(this, source, 'current SOURCE data'),
|
||||||
list.bind(this, target, 'current TARGET data'),
|
list.bind(this, target, 'current TARGET data'),
|
||||||
|
|
||||||
deleteAllSourceData,
|
deleteAllSourceData,
|
||||||
|
|
||||||
replicateSourceToTarget,
|
replicateSourceToTarget,
|
||||||
list.bind(this, source, 'current SOURCE data (empty)'),
|
list.bind(this, source, 'current SOURCE data (empty)'),
|
||||||
list.bind(this, target, 'current TARGET data (empty)'),
|
list.bind(this, target, 'current TARGET data (empty)'),
|
||||||
|
|
||||||
createSomeNewSourceData,
|
createSomeNewSourceData,
|
||||||
|
|
||||||
replicateSourceToTarget,
|
replicateSourceToTarget,
|
||||||
list.bind(this, source, 'current SOURCE data'),
|
list.bind(this, source, 'current SOURCE data'),
|
||||||
list.bind(this, target, 'current TARGET data')
|
list.bind(this, target, 'current TARGET data'),
|
||||||
];
|
];
|
||||||
|
|
||||||
run(steps);
|
run(steps);
|
||||||
|
|
||||||
function createSomeInitialSourceData() {
|
function createSomeInitialSourceData() {
|
||||||
Color.create([
|
Color.create([
|
||||||
{name: 'red'},
|
{ name: 'red' },
|
||||||
{name: 'blue'},
|
{ name: 'blue' },
|
||||||
{name: 'green'}
|
{ name: 'green' },
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ function replicateSourceToTarget() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveAllConflicts() {
|
function resolveAllConflicts() {
|
||||||
if(conflicts.length) {
|
if (conflicts.length) {
|
||||||
conflicts.forEach(function(conflict) {
|
conflicts.forEach(function(conflict) {
|
||||||
conflict.resolve();
|
conflict.resolve();
|
||||||
});
|
});
|
||||||
|
@ -91,11 +91,11 @@ function updateSomeTargetData() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMoreSourceData() {
|
function createMoreSourceData() {
|
||||||
Color.create({name: 'orange'});
|
Color.create({ name: 'orange' });
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEvenMoreSourceData() {
|
function createEvenMoreSourceData() {
|
||||||
Color.create({name: 'black'});
|
Color.create({ name: 'black' });
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSomeSourceDataCausingAConflict() {
|
function updateSomeSourceDataCausingAConflict() {
|
||||||
|
@ -111,9 +111,9 @@ function deleteAllSourceData() {
|
||||||
|
|
||||||
function createSomeNewSourceData() {
|
function createSomeNewSourceData() {
|
||||||
Color.create([
|
Color.create([
|
||||||
{name: 'violet'},
|
{ name: 'violet' },
|
||||||
{name: 'amber'},
|
{ name: 'amber' },
|
||||||
{name: 'olive'}
|
{ name: 'olive' },
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +130,7 @@ function list(model, msg) {
|
||||||
function run(steps) {
|
function run(steps) {
|
||||||
setInterval(function() {
|
setInterval(function() {
|
||||||
var step = steps.shift();
|
var step = steps.shift();
|
||||||
if(step) {
|
if (step) {
|
||||||
console.log(step.name);
|
console.log(step.name);
|
||||||
step();
|
step();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,17 +4,17 @@ var app = loopback();
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
|
|
||||||
|
|
||||||
var dataSource = app.dataSource('db', {adapter: 'memory'});
|
var dataSource = app.dataSource('db', { adapter: 'memory' });
|
||||||
|
|
||||||
var Color = dataSource.define('color', {
|
var Color = dataSource.define('color', {
|
||||||
'name': String
|
'name': String,
|
||||||
});
|
});
|
||||||
|
|
||||||
Color.create({name: 'red'});
|
Color.create({ name: 'red' });
|
||||||
Color.create({name: 'green'});
|
Color.create({ name: 'green' });
|
||||||
Color.create({name: 'blue'});
|
Color.create({ name: 'blue' });
|
||||||
|
|
||||||
Color.all(function () {
|
Color.all(function() {
|
||||||
console.log(arguments);
|
console.log(arguments);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
2
index.js
2
index.js
|
@ -1,7 +1,7 @@
|
||||||
/**
|
/**
|
||||||
* loopback ~ public api
|
* loopback ~ public api
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var loopback = module.exports = require('./lib/loopback');
|
var loopback = module.exports = require('./lib/loopback');
|
||||||
var datasourceJuggler = require('loopback-datasource-juggler');
|
var datasourceJuggler = require('loopback-datasource-juggler');
|
||||||
|
|
||||||
|
|
|
@ -91,7 +91,7 @@ AccessContext.permissionOrder = {
|
||||||
ALLOW: 1,
|
ALLOW: 1,
|
||||||
ALARM: 2,
|
ALARM: 2,
|
||||||
AUDIT: 3,
|
AUDIT: 3,
|
||||||
DENY: 4
|
DENY: 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -326,7 +326,7 @@ app.enableAuth = function(options) {
|
||||||
|
|
||||||
app.model(Model, {
|
app.model(Model, {
|
||||||
dataSource: options.dataSource,
|
dataSource: options.dataSource,
|
||||||
public: m === 'User'
|
public: m === 'User',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -365,20 +365,19 @@ app.enableAuth = function(options) {
|
||||||
} else if (allowed) {
|
} else if (allowed) {
|
||||||
next();
|
next();
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
var messages = {
|
var messages = {
|
||||||
403: {
|
403: {
|
||||||
message: 'Access Denied',
|
message: 'Access Denied',
|
||||||
code: 'ACCESS_DENIED'
|
code: 'ACCESS_DENIED',
|
||||||
},
|
},
|
||||||
404: {
|
404: {
|
||||||
message: ('could not find ' + modelName + ' with id ' + modelId),
|
message: ('could not find ' + modelName + ' with id ' + modelId),
|
||||||
code: 'MODEL_NOT_FOUND'
|
code: 'MODEL_NOT_FOUND',
|
||||||
},
|
},
|
||||||
401: {
|
401: {
|
||||||
message: 'Authorization Required',
|
message: 'Authorization Required',
|
||||||
code: 'AUTHORIZATION_REQUIRED'
|
code: 'AUTHORIZATION_REQUIRED',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var e = new Error(messages[errStatusCode].message || messages[403].message);
|
var e = new Error(messages[errStatusCode].message || messages[403].message);
|
||||||
|
@ -480,7 +479,7 @@ function setSharedMethodSharedProperties(model, app, modelConfigs) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// set sharedMethod.shared using the merged settings
|
// set sharedMethod.shared using the merged settings
|
||||||
var sharedMethods = model.sharedClass.methods({includeDisabled: true});
|
var sharedMethods = model.sharedClass.methods({ includeDisabled: true });
|
||||||
sharedMethods.forEach(function(sharedMethod) {
|
sharedMethods.forEach(function(sharedMethod) {
|
||||||
// use the specific setting if it exists
|
// use the specific setting if it exists
|
||||||
var hasSpecificSetting = settings.hasOwnProperty(sharedMethod.name);
|
var hasSpecificSetting = settings.hasOwnProperty(sharedMethod.name);
|
||||||
|
|
|
@ -47,7 +47,7 @@ module.exports = function(registry) {
|
||||||
|
|
||||||
var dataSourceTypes = {
|
var dataSourceTypes = {
|
||||||
DB: 'db',
|
DB: 'db',
|
||||||
MAIL: 'mail'
|
MAIL: 'mail',
|
||||||
};
|
};
|
||||||
|
|
||||||
registry.Email.autoAttach = dataSourceTypes.MAIL;
|
registry.Email.autoAttach = dataSourceTypes.MAIL;
|
||||||
|
|
|
@ -18,7 +18,6 @@ module.exports = MailConnector;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function MailConnector(settings) {
|
function MailConnector(settings) {
|
||||||
|
|
||||||
assert(typeof settings === 'object', 'cannot initialize MailConnector without a settings object');
|
assert(typeof settings === 'object', 'cannot initialize MailConnector without a settings object');
|
||||||
|
|
||||||
var transports = settings.transports;
|
var transports = settings.transports;
|
||||||
|
@ -156,7 +155,8 @@ Mailer.send = function(options, fn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transport) {
|
if (transport) {
|
||||||
assert(transport.sendMail, 'You must supply an Email.settings.transports containing a valid transport');
|
assert(transport.sendMail,
|
||||||
|
'You must supply an Email.settings.transports containing a valid transport');
|
||||||
transport.sendMail(options, fn);
|
transport.sendMail(options, fn);
|
||||||
} else {
|
} else {
|
||||||
console.warn('Warning: No email transport specified for sending email.' +
|
console.warn('Warning: No email transport specified for sending email.' +
|
||||||
|
|
|
@ -32,7 +32,7 @@ var middlewareModules = {
|
||||||
'favicon': 'serve-favicon',
|
'favicon': 'serve-favicon',
|
||||||
'directory': 'serve-index',
|
'directory': 'serve-index',
|
||||||
// 'static': 'serve-static',
|
// 'static': 'serve-static',
|
||||||
'vhost': 'vhost'
|
'vhost': 'vhost',
|
||||||
};
|
};
|
||||||
|
|
||||||
middlewares.bodyParser = safeRequire('body-parser');
|
middlewares.bodyParser = safeRequire('body-parser');
|
||||||
|
|
|
@ -52,17 +52,17 @@ loopback.registry = new Registry();
|
||||||
|
|
||||||
Object.defineProperties(loopback, {
|
Object.defineProperties(loopback, {
|
||||||
Model: {
|
Model: {
|
||||||
get: function() { return this.registry.getModel('Model'); }
|
get: function() { return this.registry.getModel('Model'); },
|
||||||
},
|
},
|
||||||
PersistedModel: {
|
PersistedModel: {
|
||||||
get: function() { return this.registry.getModel('PersistedModel'); }
|
get: function() { return this.registry.getModel('PersistedModel'); },
|
||||||
},
|
},
|
||||||
defaultDataSources: {
|
defaultDataSources: {
|
||||||
get: function() { return this.registry.defaultDataSources; }
|
get: function() { return this.registry.defaultDataSources; },
|
||||||
},
|
},
|
||||||
modelBuilder: {
|
modelBuilder: {
|
||||||
get: function() { return this.registry.modelBuilder; }
|
get: function() { return this.registry.modelBuilder; },
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -196,7 +196,7 @@ loopback.remoteMethod = function(fn, options) {
|
||||||
fn[key] = options[key];
|
fn[key] = options[key];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
fn.http = fn.http || {verb: 'get'};
|
fn.http = fn.http || { verb: 'get' };
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -213,7 +213,7 @@ loopback.template = function(file) {
|
||||||
var templates = this._templates || (this._templates = {});
|
var templates = this._templates || (this._templates = {});
|
||||||
var str = templates[file] || (templates[file] = fs.readFileSync(file, 'utf8'));
|
var str = templates[file] || (templates[file] = fs.readFileSync(file, 'utf8'));
|
||||||
return ejs.compile(str, {
|
return ejs.compile(str, {
|
||||||
filename: file
|
filename: file,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
141
lib/model.js
141
lib/model.js
|
@ -7,7 +7,6 @@ var SharedClass = require('strong-remoting').SharedClass;
|
||||||
var extend = require('util')._extend;
|
var extend = require('util')._extend;
|
||||||
|
|
||||||
module.exports = function(registry) {
|
module.exports = function(registry) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base class for **all models**.
|
* The base class for **all models**.
|
||||||
*
|
*
|
||||||
|
@ -176,16 +175,16 @@ module.exports = function(registry) {
|
||||||
|
|
||||||
var idDesc = ModelCtor.modelName + ' id';
|
var idDesc = ModelCtor.modelName + ' id';
|
||||||
ModelCtor.sharedCtor.accepts = [
|
ModelCtor.sharedCtor.accepts = [
|
||||||
{arg: 'id', type: 'any', required: true, http: {source: 'path'},
|
{ arg: 'id', type: 'any', required: true, http: { source: 'path' },
|
||||||
description: idDesc}
|
description: idDesc },
|
||||||
// {arg: 'instance', type: 'object', http: {source: 'body'}}
|
// {arg: 'instance', type: 'object', http: {source: 'body'}}
|
||||||
];
|
];
|
||||||
|
|
||||||
ModelCtor.sharedCtor.http = [
|
ModelCtor.sharedCtor.http = [
|
||||||
{path: '/:id'}
|
{ path: '/:id' },
|
||||||
];
|
];
|
||||||
|
|
||||||
ModelCtor.sharedCtor.returns = {root: true};
|
ModelCtor.sharedCtor.returns = { root: true };
|
||||||
|
|
||||||
// before remote hook
|
// before remote hook
|
||||||
ModelCtor.beforeRemote = function(name, fn) {
|
ModelCtor.beforeRemote = function(name, fn) {
|
||||||
|
@ -227,7 +226,6 @@ module.exports = function(registry) {
|
||||||
|
|
||||||
// resolve relation functions
|
// resolve relation functions
|
||||||
sharedClass.resolve(function resolver(define) {
|
sharedClass.resolve(function resolver(define) {
|
||||||
|
|
||||||
var relations = ModelCtor.relations || {};
|
var relations = ModelCtor.relations || {};
|
||||||
|
|
||||||
// get the relations
|
// get the relations
|
||||||
|
@ -250,9 +248,11 @@ module.exports = function(registry) {
|
||||||
|
|
||||||
// handle scopes
|
// handle scopes
|
||||||
var scopes = ModelCtor.scopes || {};
|
var scopes = ModelCtor.scopes || {};
|
||||||
|
/* eslint-disable one-var */
|
||||||
for (var scopeName in scopes) {
|
for (var scopeName in scopes) {
|
||||||
ModelCtor.scopeRemoting(scopeName, scopes[scopeName], define);
|
ModelCtor.scopeRemoting(scopeName, scopes[scopeName], define);
|
||||||
}
|
}
|
||||||
|
/* eslint-enable one-var */
|
||||||
});
|
});
|
||||||
|
|
||||||
return ModelCtor;
|
return ModelCtor;
|
||||||
|
@ -307,7 +307,7 @@ module.exports = function(registry) {
|
||||||
sharedMethod: sharedMethod,
|
sharedMethod: sharedMethod,
|
||||||
modelId: modelId,
|
modelId: modelId,
|
||||||
accessType: this._getAccessTypeForMethod(sharedMethod),
|
accessType: this._getAccessTypeForMethod(sharedMethod),
|
||||||
remotingContext: ctx
|
remotingContext: ctx,
|
||||||
}, function(err, accessRequest) {
|
}, function(err, accessRequest) {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
callback(null, accessRequest.isAllowed());
|
callback(null, accessRequest.isAllowed());
|
||||||
|
@ -323,7 +323,7 @@ module.exports = function(registry) {
|
||||||
|
|
||||||
Model._getAccessTypeForMethod = function(method) {
|
Model._getAccessTypeForMethod = function(method) {
|
||||||
if (typeof method === 'string') {
|
if (typeof method === 'string') {
|
||||||
method = {name: method};
|
method = { name: method };
|
||||||
}
|
}
|
||||||
assert(
|
assert(
|
||||||
typeof method === 'object',
|
typeof method === 'object',
|
||||||
|
@ -353,7 +353,7 @@ module.exports = function(registry) {
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (method.name) {
|
switch (method.name) {
|
||||||
case'create':
|
case 'create':
|
||||||
return ACL.WRITE;
|
return ACL.WRITE;
|
||||||
case 'updateOrCreate':
|
case 'updateOrCreate':
|
||||||
return ACL.WRITE;
|
return ACL.WRITE;
|
||||||
|
@ -442,11 +442,11 @@ module.exports = function(registry) {
|
||||||
var pathName = (relation.options.http && relation.options.http.path) || relationName;
|
var pathName = (relation.options.http && relation.options.http.path) || relationName;
|
||||||
define('__get__' + relationName, {
|
define('__get__' + relationName, {
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
http: {verb: 'get', path: '/' + pathName},
|
http: { verb: 'get', path: '/' + pathName },
|
||||||
accepts: {arg: 'refresh', type: 'boolean', http: {source: 'query'}},
|
accepts: { arg: 'refresh', type: 'boolean', http: { source: 'query' }},
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
description: 'Fetches belongsTo relation ' + relationName + '.',
|
description: 'Fetches belongsTo relation ' + relationName + '.',
|
||||||
returns: {arg: relationName, type: modelName, root: true}
|
returns: { arg: relationName, type: modelName, root: true },
|
||||||
}, fn);
|
}, fn);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -467,37 +467,37 @@ module.exports = function(registry) {
|
||||||
|
|
||||||
define('__get__' + relationName, {
|
define('__get__' + relationName, {
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
http: {verb: 'get', path: '/' + pathName},
|
http: { verb: 'get', path: '/' + pathName },
|
||||||
accepts: {arg: 'refresh', type: 'boolean', http: {source: 'query'}},
|
accepts: { arg: 'refresh', type: 'boolean', http: { source: 'query' }},
|
||||||
description: 'Fetches hasOne relation ' + relationName + '.',
|
description: 'Fetches hasOne relation ' + relationName + '.',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
returns: {arg: relationName, type: relation.modelTo.modelName, root: true},
|
returns: { arg: relationName, type: relation.modelTo.modelName, root: true },
|
||||||
rest: {after: convertNullToNotFoundError.bind(null, toModelName)}
|
rest: { after: convertNullToNotFoundError.bind(null, toModelName) },
|
||||||
});
|
});
|
||||||
|
|
||||||
define('__create__' + relationName, {
|
define('__create__' + relationName, {
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
http: {verb: 'post', path: '/' + pathName},
|
http: { verb: 'post', path: '/' + pathName },
|
||||||
accepts: {arg: 'data', type: toModelName, http: {source: 'body'}},
|
accepts: { arg: 'data', type: toModelName, http: { source: 'body' }},
|
||||||
description: 'Creates a new instance in ' + relationName + ' of this model.',
|
description: 'Creates a new instance in ' + relationName + ' of this model.',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
returns: {arg: 'data', type: toModelName, root: true}
|
returns: { arg: 'data', type: toModelName, root: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
define('__update__' + relationName, {
|
define('__update__' + relationName, {
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
http: {verb: 'put', path: '/' + pathName},
|
http: { verb: 'put', path: '/' + pathName },
|
||||||
accepts: {arg: 'data', type: toModelName, http: {source: 'body'}},
|
accepts: { arg: 'data', type: toModelName, http: { source: 'body' }},
|
||||||
description: 'Update ' + relationName + ' of this model.',
|
description: 'Update ' + relationName + ' of this model.',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
returns: {arg: 'data', type: toModelName, root: true}
|
returns: { arg: 'data', type: toModelName, root: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
define('__destroy__' + relationName, {
|
define('__destroy__' + relationName, {
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
http: {verb: 'delete', path: '/' + pathName},
|
http: { verb: 'delete', path: '/' + pathName },
|
||||||
description: 'Deletes ' + relationName + ' of this model.',
|
description: 'Deletes ' + relationName + ' of this model.',
|
||||||
accessType: 'WRITE'
|
accessType: 'WRITE',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -508,41 +508,41 @@ module.exports = function(registry) {
|
||||||
var findByIdFunc = this.prototype['__findById__' + relationName];
|
var findByIdFunc = this.prototype['__findById__' + relationName];
|
||||||
define('__findById__' + relationName, {
|
define('__findById__' + relationName, {
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
http: {verb: 'get', path: '/' + pathName + '/:fk'},
|
http: { verb: 'get', path: '/' + pathName + '/:fk' },
|
||||||
accepts: {arg: 'fk', type: 'any',
|
accepts: { arg: 'fk', type: 'any',
|
||||||
description: 'Foreign key for ' + relationName, required: true,
|
description: 'Foreign key for ' + relationName, required: true,
|
||||||
http: {source: 'path'}},
|
http: { source: 'path' }},
|
||||||
description: 'Find a related item by id for ' + relationName + '.',
|
description: 'Find a related item by id for ' + relationName + '.',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
returns: {arg: 'result', type: toModelName, root: true},
|
returns: { arg: 'result', type: toModelName, root: true },
|
||||||
rest: {after: convertNullToNotFoundError.bind(null, toModelName)}
|
rest: { after: convertNullToNotFoundError.bind(null, toModelName) },
|
||||||
}, findByIdFunc);
|
}, findByIdFunc);
|
||||||
|
|
||||||
var destroyByIdFunc = this.prototype['__destroyById__' + relationName];
|
var destroyByIdFunc = this.prototype['__destroyById__' + relationName];
|
||||||
define('__destroyById__' + relationName, {
|
define('__destroyById__' + relationName, {
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
http: {verb: 'delete', path: '/' + pathName + '/:fk'},
|
http: { verb: 'delete', path: '/' + pathName + '/:fk' },
|
||||||
accepts: {arg: 'fk', type: 'any',
|
accepts: { arg: 'fk', type: 'any',
|
||||||
description: 'Foreign key for ' + relationName, required: true,
|
description: 'Foreign key for ' + relationName, required: true,
|
||||||
http: {source: 'path'}},
|
http: { source: 'path' }},
|
||||||
description: 'Delete a related item by id for ' + relationName + '.',
|
description: 'Delete a related item by id for ' + relationName + '.',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
returns: []
|
returns: [],
|
||||||
}, destroyByIdFunc);
|
}, destroyByIdFunc);
|
||||||
|
|
||||||
var updateByIdFunc = this.prototype['__updateById__' + relationName];
|
var updateByIdFunc = this.prototype['__updateById__' + relationName];
|
||||||
define('__updateById__' + relationName, {
|
define('__updateById__' + relationName, {
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
http: {verb: 'put', path: '/' + pathName + '/:fk'},
|
http: { verb: 'put', path: '/' + pathName + '/:fk' },
|
||||||
accepts: [
|
accepts: [
|
||||||
{arg: 'fk', type: 'any',
|
{ arg: 'fk', type: 'any',
|
||||||
description: 'Foreign key for ' + relationName, required: true,
|
description: 'Foreign key for ' + relationName, required: true,
|
||||||
http: {source: 'path'}},
|
http: { source: 'path' }},
|
||||||
{arg: 'data', type: toModelName, http: {source: 'body'}}
|
{ arg: 'data', type: toModelName, http: { source: 'body' }},
|
||||||
],
|
],
|
||||||
description: 'Update a related item by id for ' + relationName + '.',
|
description: 'Update a related item by id for ' + relationName + '.',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
returns: {arg: 'result', type: toModelName, root: true}
|
returns: { arg: 'result', type: toModelName, root: true },
|
||||||
}, updateByIdFunc);
|
}, updateByIdFunc);
|
||||||
|
|
||||||
if (relation.modelThrough || relation.type === 'referencesMany') {
|
if (relation.modelThrough || relation.type === 'referencesMany') {
|
||||||
|
@ -551,31 +551,31 @@ module.exports = function(registry) {
|
||||||
var accepts = [];
|
var accepts = [];
|
||||||
if (relation.type === 'hasMany' && relation.modelThrough) {
|
if (relation.type === 'hasMany' && relation.modelThrough) {
|
||||||
// Restrict: only hasManyThrough relation can have additional properties
|
// Restrict: only hasManyThrough relation can have additional properties
|
||||||
accepts.push({arg: 'data', type: modelThrough.modelName, http: {source: 'body'}});
|
accepts.push({ arg: 'data', type: modelThrough.modelName, http: { source: 'body' }});
|
||||||
}
|
}
|
||||||
|
|
||||||
var addFunc = this.prototype['__link__' + relationName];
|
var addFunc = this.prototype['__link__' + relationName];
|
||||||
define('__link__' + relationName, {
|
define('__link__' + relationName, {
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
http: {verb: 'put', path: '/' + pathName + '/rel/:fk'},
|
http: { verb: 'put', path: '/' + pathName + '/rel/:fk' },
|
||||||
accepts: [{arg: 'fk', type: 'any',
|
accepts: [{ arg: 'fk', type: 'any',
|
||||||
description: 'Foreign key for ' + relationName, required: true,
|
description: 'Foreign key for ' + relationName, required: true,
|
||||||
http: {source: 'path'}}].concat(accepts),
|
http: { source: 'path' }}].concat(accepts),
|
||||||
description: 'Add a related item by id for ' + relationName + '.',
|
description: 'Add a related item by id for ' + relationName + '.',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
returns: {arg: relationName, type: modelThrough.modelName, root: true}
|
returns: { arg: relationName, type: modelThrough.modelName, root: true },
|
||||||
}, addFunc);
|
}, addFunc);
|
||||||
|
|
||||||
var removeFunc = this.prototype['__unlink__' + relationName];
|
var removeFunc = this.prototype['__unlink__' + relationName];
|
||||||
define('__unlink__' + relationName, {
|
define('__unlink__' + relationName, {
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
http: {verb: 'delete', path: '/' + pathName + '/rel/:fk'},
|
http: { verb: 'delete', path: '/' + pathName + '/rel/:fk' },
|
||||||
accepts: {arg: 'fk', type: 'any',
|
accepts: { arg: 'fk', type: 'any',
|
||||||
description: 'Foreign key for ' + relationName, required: true,
|
description: 'Foreign key for ' + relationName, required: true,
|
||||||
http: {source: 'path'}},
|
http: { source: 'path' }},
|
||||||
description: 'Remove the ' + relationName + ' relation to an item by id.',
|
description: 'Remove the ' + relationName + ' relation to an item by id.',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
returns: []
|
returns: [],
|
||||||
}, removeFunc);
|
}, removeFunc);
|
||||||
|
|
||||||
// FIXME: [rfeng] How to map a function with callback(err, true|false) to HEAD?
|
// FIXME: [rfeng] How to map a function with callback(err, true|false) to HEAD?
|
||||||
|
@ -583,13 +583,13 @@ module.exports = function(registry) {
|
||||||
var existsFunc = this.prototype['__exists__' + relationName];
|
var existsFunc = this.prototype['__exists__' + relationName];
|
||||||
define('__exists__' + relationName, {
|
define('__exists__' + relationName, {
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
http: {verb: 'head', path: '/' + pathName + '/rel/:fk'},
|
http: { verb: 'head', path: '/' + pathName + '/rel/:fk' },
|
||||||
accepts: {arg: 'fk', type: 'any',
|
accepts: { arg: 'fk', type: 'any',
|
||||||
description: 'Foreign key for ' + relationName, required: true,
|
description: 'Foreign key for ' + relationName, required: true,
|
||||||
http: {source: 'path'}},
|
http: { source: 'path' }},
|
||||||
description: 'Check the existence of ' + relationName + ' relation to an item by id.',
|
description: 'Check the existence of ' + relationName + ' relation to an item by id.',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
returns: {arg: 'exists', type: 'boolean', root: true},
|
returns: { arg: 'exists', type: 'boolean', root: true },
|
||||||
rest: {
|
rest: {
|
||||||
// After hook to map exists to 200/404 for HEAD
|
// After hook to map exists to 200/404 for HEAD
|
||||||
after: function(ctx, cb) {
|
after: function(ctx, cb) {
|
||||||
|
@ -604,8 +604,8 @@ module.exports = function(registry) {
|
||||||
} else {
|
} else {
|
||||||
cb();
|
cb();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}, existsFunc);
|
}, existsFunc);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -628,38 +628,37 @@ module.exports = function(registry) {
|
||||||
|
|
||||||
define('__get__' + scopeName, {
|
define('__get__' + scopeName, {
|
||||||
isStatic: isStatic,
|
isStatic: isStatic,
|
||||||
http: {verb: 'get', path: '/' + pathName},
|
http: { verb: 'get', path: '/' + pathName },
|
||||||
accepts: {arg: 'filter', type: 'object'},
|
accepts: { arg: 'filter', type: 'object' },
|
||||||
description: 'Queries ' + scopeName + ' of ' + this.modelName + '.',
|
description: 'Queries ' + scopeName + ' of ' + this.modelName + '.',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
returns: {arg: scopeName, type: [toModelName], root: true}
|
returns: { arg: scopeName, type: [toModelName], root: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
define('__create__' + scopeName, {
|
define('__create__' + scopeName, {
|
||||||
isStatic: isStatic,
|
isStatic: isStatic,
|
||||||
http: {verb: 'post', path: '/' + pathName},
|
http: { verb: 'post', path: '/' + pathName },
|
||||||
accepts: {arg: 'data', type: toModelName, http: {source: 'body'}},
|
accepts: { arg: 'data', type: toModelName, http: { source: 'body' }},
|
||||||
description: 'Creates a new instance in ' + scopeName + ' of this model.',
|
description: 'Creates a new instance in ' + scopeName + ' of this model.',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
returns: {arg: 'data', type: toModelName, root: true}
|
returns: { arg: 'data', type: toModelName, root: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
define('__delete__' + scopeName, {
|
define('__delete__' + scopeName, {
|
||||||
isStatic: isStatic,
|
isStatic: isStatic,
|
||||||
http: {verb: 'delete', path: '/' + pathName},
|
http: { verb: 'delete', path: '/' + pathName },
|
||||||
description: 'Deletes all ' + scopeName + ' of this model.',
|
description: 'Deletes all ' + scopeName + ' of this model.',
|
||||||
accessType: 'WRITE'
|
accessType: 'WRITE',
|
||||||
});
|
});
|
||||||
|
|
||||||
define('__count__' + scopeName, {
|
define('__count__' + scopeName, {
|
||||||
isStatic: isStatic,
|
isStatic: isStatic,
|
||||||
http: {verb: 'get', path: '/' + pathName + '/count'},
|
http: { verb: 'get', path: '/' + pathName + '/count' },
|
||||||
accepts: {arg: 'where', type: 'object', description: 'Criteria to match model instances'},
|
accepts: { arg: 'where', type: 'object', description: 'Criteria to match model instances' },
|
||||||
description: 'Counts ' + scopeName + ' of ' + this.modelName + '.',
|
description: 'Counts ' + scopeName + ' of ' + this.modelName + '.',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
returns: {arg: 'count', type: 'number'}
|
returns: { arg: 'count', type: 'number' },
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -696,8 +695,7 @@ module.exports = function(registry) {
|
||||||
var paramName = options.paramName || 'nk';
|
var paramName = options.paramName || 'nk';
|
||||||
|
|
||||||
var http = [].concat(sharedToClass.http || [])[0];
|
var http = [].concat(sharedToClass.http || [])[0];
|
||||||
var httpPath;
|
var httpPath, acceptArgs;
|
||||||
var acceptArgs;
|
|
||||||
|
|
||||||
if (relation.multiple) {
|
if (relation.multiple) {
|
||||||
httpPath = pathName + '/:' + paramName;
|
httpPath = pathName + '/:' + paramName;
|
||||||
|
@ -705,8 +703,8 @@ module.exports = function(registry) {
|
||||||
{
|
{
|
||||||
arg: paramName, type: 'any', http: { source: 'path' },
|
arg: paramName, type: 'any', http: { source: 'path' },
|
||||||
description: 'Foreign key for ' + relation.name + '.',
|
description: 'Foreign key for ' + relation.name + '.',
|
||||||
required: true
|
required: true,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
httpPath = pathName;
|
httpPath = pathName;
|
||||||
|
@ -830,9 +828,10 @@ module.exports = function(registry) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Relation `' + relationName + '` does not exist for model `' + this.modelName + '`');
|
var msg = 'Relation `' + relationName +
|
||||||
|
'` does not exist for model `' + this.modelName + '`';
|
||||||
|
throw new Error(msg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -555,28 +555,30 @@ module.exports = function(registry) {
|
||||||
setRemoting(PersistedModel, 'create', {
|
setRemoting(PersistedModel, 'create', {
|
||||||
description: 'Create a new instance of the model and persist it into the data source.',
|
description: 'Create a new instance of the model and persist it into the data source.',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: {arg: 'data', type: 'object', description: 'Model instance data', http: {source: 'body'}},
|
accepts: { arg: 'data', type: 'object', http: { source: 'body' }, description:
|
||||||
returns: {arg: 'data', type: typeName, root: true},
|
'Model instance data' },
|
||||||
http: {verb: 'post', path: '/'}
|
returns: { arg: 'data', type: typeName, root: true },
|
||||||
|
http: { verb: 'post', path: '/' },
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'upsert', {
|
setRemoting(PersistedModel, 'upsert', {
|
||||||
aliases: ['updateOrCreate'],
|
aliases: ['updateOrCreate'],
|
||||||
description: 'Update an existing model instance or insert a new one into the data source.',
|
description: 'Update an existing model instance or insert a new one into the data source.',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: {arg: 'data', type: 'object', description: 'Model instance data', http: {source: 'body'}},
|
accepts: { arg: 'data', type: 'object', http: { source: 'body' }, description:
|
||||||
returns: {arg: 'data', type: typeName, root: true},
|
'Model instance data' },
|
||||||
http: {verb: 'put', path: '/'}
|
returns: { arg: 'data', type: typeName, root: true },
|
||||||
|
http: { verb: 'put', path: '/' },
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'exists', {
|
setRemoting(PersistedModel, 'exists', {
|
||||||
description: 'Check whether a model instance exists in the data source.',
|
description: 'Check whether a model instance exists in the data source.',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
accepts: {arg: 'id', type: 'any', description: 'Model id', required: true},
|
accepts: { arg: 'id', type: 'any', description: 'Model id', required: true },
|
||||||
returns: {arg: 'exists', type: 'boolean'},
|
returns: { arg: 'exists', type: 'boolean' },
|
||||||
http: [
|
http: [
|
||||||
{verb: 'get', path: '/:id/exists'},
|
{ verb: 'get', path: '/:id/exists' },
|
||||||
{verb: 'head', path: '/:id'}
|
{ verb: 'head', path: '/:id' },
|
||||||
],
|
],
|
||||||
rest: {
|
rest: {
|
||||||
// After hook to map exists to 200/404 for HEAD
|
// After hook to map exists to 200/404 for HEAD
|
||||||
|
@ -596,8 +598,8 @@ module.exports = function(registry) {
|
||||||
} else {
|
} else {
|
||||||
cb();
|
cb();
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'findById', {
|
setRemoting(PersistedModel, 'findById', {
|
||||||
|
@ -605,44 +607,46 @@ module.exports = function(registry) {
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
accepts: [
|
accepts: [
|
||||||
{ arg: 'id', type: 'any', description: 'Model id', required: true,
|
{ arg: 'id', type: 'any', description: 'Model id', required: true,
|
||||||
http: {source: 'path'}},
|
http: { source: 'path' }},
|
||||||
{ arg: 'filter', type: 'object',
|
{ arg: 'filter', type: 'object',
|
||||||
description: 'Filter defining fields and include'}
|
description: 'Filter defining fields and include' },
|
||||||
],
|
],
|
||||||
returns: {arg: 'data', type: typeName, root: true},
|
returns: { arg: 'data', type: typeName, root: true },
|
||||||
http: {verb: 'get', path: '/:id'},
|
http: { verb: 'get', path: '/:id' },
|
||||||
rest: {after: convertNullToNotFoundError}
|
rest: { after: convertNullToNotFoundError },
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'find', {
|
setRemoting(PersistedModel, 'find', {
|
||||||
description: 'Find all instances of the model matched by filter from the data source.',
|
description: 'Find all instances of the model matched by filter from the data source.',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
accepts: {arg: 'filter', type: 'object', description: 'Filter defining fields, where, include, order, offset, and limit'},
|
accepts: { arg: 'filter', type: 'object', description:
|
||||||
returns: {arg: 'data', type: [typeName], root: true},
|
'Filter defining fields, where, include, order, offset, and limit' },
|
||||||
http: {verb: 'get', path: '/'}
|
returns: { arg: 'data', type: [typeName], root: true },
|
||||||
|
http: { verb: 'get', path: '/' },
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'findOne', {
|
setRemoting(PersistedModel, 'findOne', {
|
||||||
description: 'Find first instance of the model matched by filter from the data source.',
|
description: 'Find first instance of the model matched by filter from the data source.',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
accepts: {arg: 'filter', type: 'object', description: 'Filter defining fields, where, include, order, offset, and limit'},
|
accepts: { arg: 'filter', type: 'object', description:
|
||||||
returns: {arg: 'data', type: typeName, root: true},
|
'Filter defining fields, where, include, order, offset, and limit' },
|
||||||
http: {verb: 'get', path: '/findOne'},
|
returns: { arg: 'data', type: typeName, root: true },
|
||||||
rest: {after: convertNullToNotFoundError}
|
http: { verb: 'get', path: '/findOne' },
|
||||||
|
rest: { after: convertNullToNotFoundError },
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'destroyAll', {
|
setRemoting(PersistedModel, 'destroyAll', {
|
||||||
description: 'Delete all matching records.',
|
description: 'Delete all matching records.',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: {arg: 'where', type: 'object', description: 'filter.where object'},
|
accepts: { arg: 'where', type: 'object', description: 'filter.where object' },
|
||||||
returns: {
|
returns: {
|
||||||
arg: 'count',
|
arg: 'count',
|
||||||
type: 'object',
|
type: 'object',
|
||||||
description: 'The number of instances deleted',
|
description: 'The number of instances deleted',
|
||||||
root: true
|
root: true,
|
||||||
},
|
},
|
||||||
http: {verb: 'del', path: '/'},
|
http: { verb: 'del', path: '/' },
|
||||||
shared: false
|
shared: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'updateAll', {
|
setRemoting(PersistedModel, 'updateAll', {
|
||||||
|
@ -650,44 +654,44 @@ module.exports = function(registry) {
|
||||||
description: 'Update instances of the model matched by where from the data source.',
|
description: 'Update instances of the model matched by where from the data source.',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: [
|
accepts: [
|
||||||
{arg: 'where', type: 'object', http: {source: 'query'},
|
{ arg: 'where', type: 'object', http: { source: 'query' },
|
||||||
description: 'Criteria to match model instances'},
|
description: 'Criteria to match model instances' },
|
||||||
{arg: 'data', type: 'object', http: {source: 'body'},
|
{ arg: 'data', type: 'object', http: { source: 'body' },
|
||||||
description: 'An object of model property name/value pairs'},
|
description: 'An object of model property name/value pairs' },
|
||||||
],
|
],
|
||||||
returns: {
|
returns: {
|
||||||
arg: 'count',
|
arg: 'count',
|
||||||
description: 'The number of instances updated',
|
description: 'The number of instances updated',
|
||||||
type: 'object',
|
type: 'object',
|
||||||
root: true
|
root: true,
|
||||||
},
|
},
|
||||||
http: {verb: 'post', path: '/update'}
|
http: { verb: 'post', path: '/update' },
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'deleteById', {
|
setRemoting(PersistedModel, 'deleteById', {
|
||||||
aliases: ['destroyById', 'removeById'],
|
aliases: ['destroyById', 'removeById'],
|
||||||
description: 'Delete a model instance by id from the data source.',
|
description: 'Delete a model instance by id from the data source.',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: {arg: 'id', type: 'any', description: 'Model id', required: true,
|
accepts: { arg: 'id', type: 'any', description: 'Model id', required: true,
|
||||||
http: {source: 'path'}},
|
http: { source: 'path' }},
|
||||||
http: {verb: 'del', path: '/:id'},
|
http: { verb: 'del', path: '/:id' },
|
||||||
returns: {arg: 'count', type: 'object', root: true}
|
returns: { arg: 'count', type: 'object', root: true },
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'count', {
|
setRemoting(PersistedModel, 'count', {
|
||||||
description: 'Count instances of the model matched by where from the data source.',
|
description: 'Count instances of the model matched by where from the data source.',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
accepts: {arg: 'where', type: 'object', description: 'Criteria to match model instances'},
|
accepts: { arg: 'where', type: 'object', description: 'Criteria to match model instances' },
|
||||||
returns: {arg: 'count', type: 'number'},
|
returns: { arg: 'count', type: 'number' },
|
||||||
http: {verb: 'get', path: '/count'}
|
http: { verb: 'get', path: '/count' },
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel.prototype, 'updateAttributes', {
|
setRemoting(PersistedModel.prototype, 'updateAttributes', {
|
||||||
description: 'Update attributes for a model instance and persist it into the data source.',
|
description: 'Update attributes for a model instance and persist it into the data source.',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: {arg: 'data', type: 'object', http: {source: 'body'}, description: 'An object of model property name/value pairs'},
|
accepts: { arg: 'data', type: 'object', http: { source: 'body' }, description: 'An object of model property name/value pairs' },
|
||||||
returns: {arg: 'data', type: typeName, root: true},
|
returns: { arg: 'data', type: typeName, root: true },
|
||||||
http: {verb: 'put', path: '/'}
|
http: { verb: 'put', path: '/' },
|
||||||
});
|
});
|
||||||
|
|
||||||
if (options.trackChanges || options.enableRemoteReplication) {
|
if (options.trackChanges || options.enableRemoteReplication) {
|
||||||
|
@ -695,12 +699,12 @@ module.exports = function(registry) {
|
||||||
description: 'Get a set of deltas and conflicts since the given checkpoint.',
|
description: 'Get a set of deltas and conflicts since the given checkpoint.',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
accepts: [
|
accepts: [
|
||||||
{arg: 'since', type: 'number', description: 'Find deltas since this checkpoint'},
|
{ arg: 'since', type: 'number', description: 'Find deltas since this checkpoint' },
|
||||||
{arg: 'remoteChanges', type: 'array', description: 'an array of change objects',
|
{ arg: 'remoteChanges', type: 'array', description: 'an array of change objects',
|
||||||
http: {source: 'body'}}
|
http: { source: 'body' }},
|
||||||
],
|
],
|
||||||
returns: {arg: 'result', type: 'object', root: true},
|
returns: { arg: 'result', type: 'object', root: true },
|
||||||
http: {verb: 'post', path: '/diff'}
|
http: { verb: 'post', path: '/diff' },
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'changes', {
|
setRemoting(PersistedModel, 'changes', {
|
||||||
|
@ -708,11 +712,13 @@ module.exports = function(registry) {
|
||||||
'Provide a filter object to reduce the number of results returned.',
|
'Provide a filter object to reduce the number of results returned.',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
accepts: [
|
accepts: [
|
||||||
{arg: 'since', type: 'number', description: 'Only return changes since this checkpoint'},
|
{ arg: 'since', type: 'number', description:
|
||||||
{arg: 'filter', type: 'object', description: 'Only include changes that match this filter'}
|
'Only return changes since this checkpoint' },
|
||||||
|
{ arg: 'filter', type: 'object', description:
|
||||||
|
'Only include changes that match this filter' },
|
||||||
],
|
],
|
||||||
returns: {arg: 'changes', type: 'array', root: true},
|
returns: { arg: 'changes', type: 'array', root: true },
|
||||||
http: {verb: 'get', path: '/changes'}
|
http: { verb: 'get', path: '/changes' },
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'checkpoint', {
|
setRemoting(PersistedModel, 'checkpoint', {
|
||||||
|
@ -722,15 +728,15 @@ module.exports = function(registry) {
|
||||||
// We need to allow this method for users that don't have full
|
// We need to allow this method for users that don't have full
|
||||||
// WRITE permissions.
|
// WRITE permissions.
|
||||||
accessType: 'REPLICATE',
|
accessType: 'REPLICATE',
|
||||||
returns: {arg: 'checkpoint', type: 'object', root: true},
|
returns: { arg: 'checkpoint', type: 'object', root: true },
|
||||||
http: {verb: 'post', path: '/checkpoint'}
|
http: { verb: 'post', path: '/checkpoint' },
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'currentCheckpoint', {
|
setRemoting(PersistedModel, 'currentCheckpoint', {
|
||||||
description: 'Get the current checkpoint.',
|
description: 'Get the current checkpoint.',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
returns: {arg: 'checkpoint', type: 'object', root: true},
|
returns: { arg: 'checkpoint', type: 'object', root: true },
|
||||||
http: {verb: 'get', path: '/checkpoint'}
|
http: { verb: 'get', path: '/checkpoint' },
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'createUpdates', {
|
setRemoting(PersistedModel, 'createUpdates', {
|
||||||
|
@ -739,16 +745,16 @@ module.exports = function(registry) {
|
||||||
// It is called by the replication algorithm to compile a list
|
// It is called by the replication algorithm to compile a list
|
||||||
// of changes to apply on the target.
|
// of changes to apply on the target.
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
accepts: {arg: 'deltas', type: 'array', http: {source: 'body'}},
|
accepts: { arg: 'deltas', type: 'array', http: { source: 'body' }},
|
||||||
returns: {arg: 'updates', type: 'array', root: true},
|
returns: { arg: 'updates', type: 'array', root: true },
|
||||||
http: {verb: 'post', path: '/create-updates'}
|
http: { verb: 'post', path: '/create-updates' },
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'bulkUpdate', {
|
setRemoting(PersistedModel, 'bulkUpdate', {
|
||||||
description: 'Run multiple updates at once. Note: this is not atomic.',
|
description: 'Run multiple updates at once. Note: this is not atomic.',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: {arg: 'updates', type: 'array'},
|
accepts: { arg: 'updates', type: 'array' },
|
||||||
http: {verb: 'post', path: '/bulk-update'}
|
http: { verb: 'post', path: '/bulk-update' },
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'findLastChange', {
|
setRemoting(PersistedModel, 'findLastChange', {
|
||||||
|
@ -756,30 +762,30 @@ module.exports = function(registry) {
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
accepts: {
|
accepts: {
|
||||||
arg: 'id', type: 'any', required: true, http: { source: 'path' },
|
arg: 'id', type: 'any', required: true, http: { source: 'path' },
|
||||||
description: 'Model id'
|
description: 'Model id',
|
||||||
},
|
},
|
||||||
returns: { arg: 'result', type: this.Change.modelName, root: true },
|
returns: { arg: 'result', type: this.Change.modelName, root: true },
|
||||||
http: { verb: 'get', path: '/:id/changes/last' }
|
http: { verb: 'get', path: '/:id/changes/last' },
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'updateLastChange', {
|
setRemoting(PersistedModel, 'updateLastChange', {
|
||||||
description: [
|
description: [
|
||||||
'Update the properties of the most recent change record',
|
'Update the properties of the most recent change record',
|
||||||
'kept for this instance.'
|
'kept for this instance.',
|
||||||
],
|
],
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: [
|
accepts: [
|
||||||
{
|
{
|
||||||
arg: 'id', type: 'any', required: true, http: { source: 'path' },
|
arg: 'id', type: 'any', required: true, http: { source: 'path' },
|
||||||
description: 'Model id'
|
description: 'Model id',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
arg: 'data', type: 'object', http: {source: 'body'},
|
arg: 'data', type: 'object', http: { source: 'body' },
|
||||||
description: 'An object of Change property name/value pairs'
|
description: 'An object of Change property name/value pairs',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
returns: { arg: 'result', type: this.Change.modelName, root: true },
|
returns: { arg: 'result', type: this.Change.modelName, root: true },
|
||||||
http: { verb: 'put', path: '/:id/changes/last' }
|
http: { verb: 'put', path: '/:id/changes/last' },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -789,14 +795,14 @@ module.exports = function(registry) {
|
||||||
setRemoting(PersistedModel, 'rectifyAllChanges', {
|
setRemoting(PersistedModel, 'rectifyAllChanges', {
|
||||||
description: 'Rectify all Model changes.',
|
description: 'Rectify all Model changes.',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
http: {verb: 'post', path: '/rectify-all'}
|
http: { verb: 'post', path: '/rectify-all' },
|
||||||
});
|
});
|
||||||
|
|
||||||
setRemoting(PersistedModel, 'rectifyChange', {
|
setRemoting(PersistedModel, 'rectifyChange', {
|
||||||
description: 'Tell loopback that a change to the model with the given id has occurred.',
|
description: 'Tell loopback that a change to the model with the given id has occurred.',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
accepts: {arg: 'id', type: 'any', http: {source: 'path'}},
|
accepts: { arg: 'id', type: 'any', http: { source: 'path' }},
|
||||||
http: {verb: 'post', path: '/:id/rectify-change'}
|
http: { verb: 'post', path: '/:id/rectify-change' },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -804,18 +810,18 @@ module.exports = function(registry) {
|
||||||
description: 'Create a change stream.',
|
description: 'Create a change stream.',
|
||||||
accessType: 'READ',
|
accessType: 'READ',
|
||||||
http: [
|
http: [
|
||||||
{verb: 'post', path: '/change-stream'},
|
{ verb: 'post', path: '/change-stream' },
|
||||||
{verb: 'get', path: '/change-stream'}
|
{ verb: 'get', path: '/change-stream' },
|
||||||
],
|
],
|
||||||
accepts: {
|
accepts: {
|
||||||
arg: 'options',
|
arg: 'options',
|
||||||
type: 'object'
|
type: 'object',
|
||||||
},
|
},
|
||||||
returns: {
|
returns: {
|
||||||
arg: 'changes',
|
arg: 'changes',
|
||||||
type: 'ReadableStream',
|
type: 'ReadableStream',
|
||||||
json: true
|
json: true,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -870,14 +876,14 @@ module.exports = function(registry) {
|
||||||
// TODO(ritch) this whole thing could be optimized a bit more
|
// TODO(ritch) this whole thing could be optimized a bit more
|
||||||
Change.find({ where: {
|
Change.find({ where: {
|
||||||
checkpoint: { gte: since },
|
checkpoint: { gte: since },
|
||||||
modelName: this.modelName
|
modelName: this.modelName,
|
||||||
}}, function(err, changes) {
|
}}, function(err, changes) {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
if (!Array.isArray(changes) || changes.length === 0) return callback(null, []);
|
if (!Array.isArray(changes) || changes.length === 0) return callback(null, []);
|
||||||
var ids = changes.map(function(change) {
|
var ids = changes.map(function(change) {
|
||||||
return change.getModelId();
|
return change.getModelId();
|
||||||
});
|
});
|
||||||
filter.where[idName] = {inq: ids};
|
filter.where[idName] = { inq: ids };
|
||||||
model.find(filter, function(err, models) {
|
model.find(filter, function(err, models) {
|
||||||
if (err) return callback(err);
|
if (err) return callback(err);
|
||||||
var modelIds = models.map(function(m) {
|
var modelIds = models.map(function(m) {
|
||||||
|
@ -1004,16 +1010,14 @@ module.exports = function(registry) {
|
||||||
'You must enable change tracking before replicating'
|
'You must enable change tracking before replicating'
|
||||||
);
|
);
|
||||||
|
|
||||||
var diff;
|
var diff, updates, newSourceCp, newTargetCp;
|
||||||
var updates;
|
|
||||||
var newSourceCp, newTargetCp;
|
|
||||||
|
|
||||||
var tasks = [
|
var tasks = [
|
||||||
checkpoints,
|
checkpoints,
|
||||||
getSourceChanges,
|
getSourceChanges,
|
||||||
getDiffFromTarget,
|
getDiffFromTarget,
|
||||||
createSourceUpdates,
|
createSourceUpdates,
|
||||||
bulkUpdate
|
bulkUpdate,
|
||||||
];
|
];
|
||||||
|
|
||||||
async.waterfall(tasks, done);
|
async.waterfall(tasks, done);
|
||||||
|
@ -1136,7 +1140,7 @@ module.exports = function(registry) {
|
||||||
deltas.forEach(function(change) {
|
deltas.forEach(function(change) {
|
||||||
change = new Change(change);
|
change = new Change(change);
|
||||||
var type = change.type();
|
var type = change.type();
|
||||||
var update = {type: type, change: change};
|
var update = { type: type, change: change };
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Change.CREATE:
|
case Change.CREATE:
|
||||||
case Change.UPDATE:
|
case Change.UPDATE:
|
||||||
|
@ -1521,7 +1525,7 @@ module.exports = function(registry) {
|
||||||
this.Change = BaseChangeModel.extend(this.modelName + '-change',
|
this.Change = BaseChangeModel.extend(this.modelName + '-change',
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
trackModel: this
|
trackModel: this,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1575,7 +1579,7 @@ module.exports = function(registry) {
|
||||||
|
|
||||||
PersistedModel.findLastChange = function(id, cb) {
|
PersistedModel.findLastChange = function(id, cb) {
|
||||||
var Change = this.getChangeModel();
|
var Change = this.getChangeModel();
|
||||||
Change.findOne({ where: { modelId: id } }, cb);
|
Change.findOne({ where: { modelId: id }}, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
PersistedModel.updateLastChange = function(id, data, cb) {
|
PersistedModel.updateLastChange = function(id, data, cb) {
|
||||||
|
@ -1611,7 +1615,7 @@ module.exports = function(registry) {
|
||||||
|
|
||||||
var idName = this.getIdName();
|
var idName = this.getIdName();
|
||||||
var Model = this;
|
var Model = this;
|
||||||
var changes = new PassThrough({objectMode: true});
|
var changes = new PassThrough({ objectMode: true });
|
||||||
var writeable = true;
|
var writeable = true;
|
||||||
|
|
||||||
changes.destroy = function() {
|
changes.destroy = function() {
|
||||||
|
@ -1661,7 +1665,7 @@ module.exports = function(registry) {
|
||||||
var change = {
|
var change = {
|
||||||
target: target,
|
target: target,
|
||||||
where: where,
|
where: where,
|
||||||
data: data
|
data: data,
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
|
|
|
@ -89,7 +89,6 @@ function Registry() {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Registry.prototype.createModel = function(name, properties, options) {
|
Registry.prototype.createModel = function(name, properties, options) {
|
||||||
|
|
||||||
if (arguments.length === 1 && typeof name === 'object') {
|
if (arguments.length === 1 && typeof name === 'object') {
|
||||||
var config = name;
|
var config = name;
|
||||||
name = config.name;
|
name = config.name;
|
||||||
|
@ -208,7 +207,7 @@ Registry.prototype.configureModel = function(ModelCtor, config) {
|
||||||
'super': true,
|
'super': true,
|
||||||
relations: true,
|
relations: true,
|
||||||
acls: true,
|
acls: true,
|
||||||
dataSource: true
|
dataSource: true,
|
||||||
};
|
};
|
||||||
if (typeof config.options === 'object' && config.options !== null) {
|
if (typeof config.options === 'object' && config.options !== null) {
|
||||||
for (var p in config.options) {
|
for (var p in config.options) {
|
||||||
|
@ -390,7 +389,7 @@ Registry.prototype.memory = function(name) {
|
||||||
|
|
||||||
if (!memory) {
|
if (!memory) {
|
||||||
memory = this._memoryDataSources[name] = this.createDataSource({
|
memory = this._memoryDataSources[name] = this.createDataSource({
|
||||||
connector: 'memory'
|
connector: 'memory',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -265,7 +265,7 @@ proto.lazyrouter = function() {
|
||||||
|
|
||||||
self._requestHandlingPhases = [
|
self._requestHandlingPhases = [
|
||||||
'initial', 'session', 'auth', 'parse',
|
'initial', 'session', 'auth', 'parse',
|
||||||
'routes', 'files', 'final'
|
'routes', 'files', 'final',
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -63,13 +63,13 @@
|
||||||
"browserify": "^10.0.0",
|
"browserify": "^10.0.0",
|
||||||
"chai": "^2.1.1",
|
"chai": "^2.1.1",
|
||||||
"es5-shim": "^4.1.0",
|
"es5-shim": "^4.1.0",
|
||||||
|
"eslint-config-loopback": "^1.0.0",
|
||||||
"grunt": "^0.4.5",
|
"grunt": "^0.4.5",
|
||||||
"grunt-browserify": "^3.5.0",
|
"grunt-browserify": "^3.5.0",
|
||||||
"grunt-cli": "^0.1.13",
|
"grunt-cli": "^0.1.13",
|
||||||
"grunt-contrib-jshint": "^0.11.0",
|
|
||||||
"grunt-contrib-uglify": "^0.9.1",
|
"grunt-contrib-uglify": "^0.9.1",
|
||||||
"grunt-contrib-watch": "^0.6.1",
|
"grunt-contrib-watch": "^0.6.1",
|
||||||
"grunt-jscs": "^1.5.0",
|
"grunt-eslint": "^18.0.0",
|
||||||
"grunt-karma": "^0.10.1",
|
"grunt-karma": "^0.10.1",
|
||||||
"grunt-mocha-test": "^0.12.7",
|
"grunt-mocha-test": "^0.12.7",
|
||||||
"karma": "^0.12.31",
|
"karma": "^0.12.31",
|
||||||
|
|
|
@ -4,7 +4,6 @@ var cls = require('continuation-local-storage');
|
||||||
var domain = require('domain');
|
var domain = require('domain');
|
||||||
|
|
||||||
module.exports = function(loopback) {
|
module.exports = function(loopback) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the current context object. The context is preserved
|
* Get the current context object. The context is preserved
|
||||||
* across async calls, it behaves like a thread-local storage.
|
* across async calls, it behaves like a thread-local storage.
|
||||||
|
|
|
@ -44,7 +44,7 @@ function context(options) {
|
||||||
// Run the code in the context of the namespace
|
// Run the code in the context of the namespace
|
||||||
if (enableHttpContext) {
|
if (enableHttpContext) {
|
||||||
// Set up the transport context
|
// Set up the transport context
|
||||||
ns.set('http', {req: req, res: res});
|
ns.set('http', { req: req, res: res });
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,7 +23,7 @@ function status() {
|
||||||
return function(req, res) {
|
return function(req, res) {
|
||||||
res.send({
|
res.send({
|
||||||
started: started,
|
started: started,
|
||||||
uptime: (Date.now() - Number(started)) / 1000
|
uptime: (Date.now() - Number(started)) / 1000,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
/*jshint -W030 */
|
|
||||||
|
|
||||||
var loopback = require('../');
|
var loopback = require('../');
|
||||||
var lt = require('./helpers/loopback-testing-helper');
|
var lt = require('./helpers/loopback-testing-helper');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var ACCESS_CONTROL_APP = path.join(__dirname, 'fixtures', 'access-control');
|
var ACCESS_CONTROL_APP = path.join(__dirname, 'fixtures', 'access-control');
|
||||||
var app = require(path.join(ACCESS_CONTROL_APP, 'server/server.js'));
|
var app = require(path.join(ACCESS_CONTROL_APP, 'server/server.js'));
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var USER = {email: 'test@test.test', password: 'test'};
|
var USER = { email: 'test@test.test', password: 'test' };
|
||||||
var CURRENT_USER = {email: 'current@test.test', password: 'test'};
|
var CURRENT_USER = { email: 'current@test.test', password: 'test' };
|
||||||
var debug = require('debug')('loopback:test:access-control.integration');
|
var debug = require('debug')('loopback:test:access-control.integration');
|
||||||
|
|
||||||
describe('access control - integration', function() {
|
describe('access control - integration', function() {
|
||||||
|
|
||||||
lt.beforeEach.withApp(app);
|
lt.beforeEach.withApp(app);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -61,7 +58,6 @@ describe('access control - integration', function() {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
describe('/users', function() {
|
describe('/users', function() {
|
||||||
|
|
||||||
lt.beforeEach.givenModel('user', USER, 'randomUser');
|
lt.beforeEach.givenModel('user', USER, 'randomUser');
|
||||||
|
|
||||||
lt.it.shouldBeDeniedWhenCalledAnonymously('GET', '/api/users');
|
lt.it.shouldBeDeniedWhenCalledAnonymously('GET', '/api/users');
|
||||||
|
@ -128,7 +124,7 @@ describe('access control - integration', function() {
|
||||||
userCounter = userCounter ? ++userCounter : 1;
|
userCounter = userCounter ? ++userCounter : 1;
|
||||||
return {
|
return {
|
||||||
email: 'new-' + userCounter + '@test.test',
|
email: 'new-' + userCounter + '@test.test',
|
||||||
password: 'test'
|
password: 'test',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -200,13 +196,13 @@ describe('access control - integration', function() {
|
||||||
// Create an account under the given user
|
// Create an account under the given user
|
||||||
app.models.account.create({
|
app.models.account.create({
|
||||||
userId: self.user.id,
|
userId: self.user.id,
|
||||||
balance: 100
|
balance: 100,
|
||||||
}, function(err, act) {
|
}, function(err, act) {
|
||||||
self.url = '/api/accounts/' + act.id;
|
self.url = '/api/accounts/' + act.id;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
lt.describe.whenCalledRemotely('PUT', '/api/accounts/:id', function() {
|
lt.describe.whenCalledRemotely('PUT', '/api/accounts/:id', function() {
|
||||||
lt.it.shouldBeAllowed();
|
lt.it.shouldBeAllowed();
|
||||||
});
|
});
|
||||||
|
@ -226,5 +222,4 @@ describe('access control - integration', function() {
|
||||||
return '/api/accounts/' + this.account.id;
|
return '/api/accounts/' + this.account.id;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
var loopback = require('../');
|
var loopback = require('../');
|
||||||
var extend = require('util')._extend;
|
var extend = require('util')._extend;
|
||||||
var Token = loopback.AccessToken.extend('MyToken');
|
var Token = loopback.AccessToken.extend('MyToken');
|
||||||
var ds = loopback.createDataSource({connector: loopback.Memory});
|
var ds = loopback.createDataSource({ connector: loopback.Memory });
|
||||||
Token.attachTo(ds);
|
Token.attachTo(ds);
|
||||||
var ACL = loopback.ACL;
|
var ACL = loopback.ACL;
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ describe('loopback.token(options)', function() {
|
||||||
var tokenId = this.token.id;
|
var tokenId = this.token.id;
|
||||||
var app = createTestApp(
|
var app = createTestApp(
|
||||||
this.token,
|
this.token,
|
||||||
{ token: { searchDefaultTokenKeys: false } },
|
{ token: { searchDefaultTokenKeys: false }},
|
||||||
done);
|
done);
|
||||||
var agent = request.agent(app);
|
var agent = request.agent(app);
|
||||||
|
|
||||||
|
@ -144,7 +144,7 @@ describe('loopback.token(options)', function() {
|
||||||
.set('authorization', id)
|
.set('authorization', id)
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
assert.deepEqual(res.body, {userId: userId});
|
assert.deepEqual(res.body, { userId: userId });
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -159,7 +159,7 @@ describe('loopback.token(options)', function() {
|
||||||
.set('authorization', id)
|
.set('authorization', id)
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
assert.deepEqual(res.body, {userId: userId, state: 1});
|
assert.deepEqual(res.body, { userId: userId, state: 1 });
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -174,7 +174,7 @@ describe('loopback.token(options)', function() {
|
||||||
.set('authorization', id)
|
.set('authorization', id)
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
assert.deepEqual(res.body, {userId: userId, state: 1});
|
assert.deepEqual(res.body, { userId: userId, state: 1 });
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -227,7 +227,7 @@ describe('AccessToken', function() {
|
||||||
it('supports two-arg variant with no options', function(done) {
|
it('supports two-arg variant with no options', function(done) {
|
||||||
var expectedTokenId = this.token.id;
|
var expectedTokenId = this.token.id;
|
||||||
var req = mockRequest({
|
var req = mockRequest({
|
||||||
headers: { 'authorization': expectedTokenId }
|
headers: { 'authorization': expectedTokenId },
|
||||||
});
|
});
|
||||||
|
|
||||||
Token.findForRequest(req, function(err, token) {
|
Token.findForRequest(req, function(err, token) {
|
||||||
|
@ -247,7 +247,7 @@ describe('AccessToken', function() {
|
||||||
|
|
||||||
// express helpers
|
// express helpers
|
||||||
param: function(name) { return this._params[name]; },
|
param: function(name) { return this._params[name]; },
|
||||||
header: function(name) { return this.headers[name]; }
|
header: function(name) { return this.headers[name]; },
|
||||||
},
|
},
|
||||||
opts);
|
opts);
|
||||||
}
|
}
|
||||||
|
@ -274,7 +274,7 @@ describe('app.enableAuth()', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prevent remote call with app setting status on denied ACL', function(done) {
|
it('prevent remote call with app setting status on denied ACL', function(done) {
|
||||||
createTestAppAndRequest(this.token, {app:{aclErrorStatus:403}}, done)
|
createTestAppAndRequest(this.token, { app: { aclErrorStatus: 403 }}, done)
|
||||||
.del('/tests/123')
|
.del('/tests/123')
|
||||||
.expect(403)
|
.expect(403)
|
||||||
.set('authorization', this.token.id)
|
.set('authorization', this.token.id)
|
||||||
|
@ -290,7 +290,7 @@ describe('app.enableAuth()', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prevent remote call with app setting status on denied ACL', function(done) {
|
it('prevent remote call with app setting status on denied ACL', function(done) {
|
||||||
createTestAppAndRequest(this.token, {model:{aclErrorStatus:404}}, done)
|
createTestAppAndRequest(this.token, { model: { aclErrorStatus: 404 }}, done)
|
||||||
.del('/tests/123')
|
.del('/tests/123')
|
||||||
.expect(404)
|
.expect(404)
|
||||||
.set('authorization', this.token.id)
|
.set('authorization', this.token.id)
|
||||||
|
@ -328,7 +328,7 @@ describe('app.enableAuth()', function() {
|
||||||
};
|
};
|
||||||
TestModel.remoteMethod('getToken', {
|
TestModel.remoteMethod('getToken', {
|
||||||
returns: { arg: 'token', type: 'object' },
|
returns: { arg: 'token', type: 'object' },
|
||||||
http: { verb: 'GET', path: '/token' }
|
http: { verb: 'GET', path: '/token' },
|
||||||
});
|
});
|
||||||
|
|
||||||
var app = loopback();
|
var app = loopback();
|
||||||
|
@ -355,7 +355,7 @@ describe('app.enableAuth()', function() {
|
||||||
|
|
||||||
function createTestingToken(done) {
|
function createTestingToken(done) {
|
||||||
var test = this;
|
var test = this;
|
||||||
Token.create({userId: '123'}, function(err, token) {
|
Token.create({ userId: '123' }, function(err, token) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
test.token = token;
|
test.token = token;
|
||||||
done();
|
done();
|
||||||
|
@ -376,7 +376,7 @@ function createTestApp(testToken, settings, done) {
|
||||||
var modelSettings = settings.model || {};
|
var modelSettings = settings.model || {};
|
||||||
var tokenSettings = extend({
|
var tokenSettings = extend({
|
||||||
model: Token,
|
model: Token,
|
||||||
currentUserLiteral: 'me'
|
currentUserLiteral: 'me',
|
||||||
}, settings.token);
|
}, settings.token);
|
||||||
|
|
||||||
var app = loopback();
|
var app = loopback();
|
||||||
|
@ -384,8 +384,8 @@ function createTestApp(testToken, settings, done) {
|
||||||
app.use(loopback.cookieParser('secret'));
|
app.use(loopback.cookieParser('secret'));
|
||||||
app.use(loopback.token(tokenSettings));
|
app.use(loopback.token(tokenSettings));
|
||||||
app.get('/token', function(req, res) {
|
app.get('/token', function(req, res) {
|
||||||
res.cookie('authorization', testToken.id, {signed: true});
|
res.cookie('authorization', testToken.id, { signed: true });
|
||||||
res.cookie('access_token', testToken.id, {signed: true});
|
res.cookie('access_token', testToken.id, { signed: true });
|
||||||
res.end();
|
res.end();
|
||||||
});
|
});
|
||||||
app.get('/', function(req, res) {
|
app.get('/', function(req, res) {
|
||||||
|
@ -401,7 +401,7 @@ function createTestApp(testToken, settings, done) {
|
||||||
res.status(req.accessToken ? 200 : 401).end();
|
res.status(req.accessToken ? 200 : 401).end();
|
||||||
});
|
});
|
||||||
app.use('/users/:uid', function(req, res) {
|
app.use('/users/:uid', function(req, res) {
|
||||||
var result = {userId: req.params.uid};
|
var result = { userId: req.params.uid };
|
||||||
if (req.query.state) {
|
if (req.query.state) {
|
||||||
result.state = req.query.state;
|
result.state = req.query.state;
|
||||||
} else if (req.url !== '/') {
|
} else if (req.url !== '/') {
|
||||||
|
@ -423,9 +423,9 @@ function createTestApp(testToken, settings, done) {
|
||||||
principalId: '$everyone',
|
principalId: '$everyone',
|
||||||
accessType: ACL.ALL,
|
accessType: ACL.ALL,
|
||||||
permission: ACL.DENY,
|
permission: ACL.DENY,
|
||||||
property: 'deleteById'
|
property: 'deleteById',
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.keys(modelSettings).forEach(function(key) {
|
Object.keys(modelSettings).forEach(function(key) {
|
||||||
|
|
270
test/acl.test.js
270
test/acl.test.js
|
@ -14,12 +14,12 @@ function checkResult(err, result) {
|
||||||
|
|
||||||
var ds = null;
|
var ds = null;
|
||||||
before(function() {
|
before(function() {
|
||||||
ds = loopback.createDataSource({connector: loopback.Memory});
|
ds = loopback.createDataSource({ connector: loopback.Memory });
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('security scopes', function() {
|
describe('security scopes', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
var ds = this.ds = loopback.createDataSource({connector: loopback.Memory});
|
var ds = this.ds = loopback.createDataSource({ connector: loopback.Memory });
|
||||||
testModel = loopback.PersistedModel.extend('testModel');
|
testModel = loopback.PersistedModel.extend('testModel');
|
||||||
ACL.attachTo(ds);
|
ACL.attachTo(ds);
|
||||||
Role.attachTo(ds);
|
Role.attachTo(ds);
|
||||||
|
@ -30,45 +30,53 @@ describe('security scopes', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow access to models for the given scope by wildcard', function() {
|
it('should allow access to models for the given scope by wildcard', function() {
|
||||||
Scope.create({name: 'userScope', description: 'access user information'}, function(err, scope) {
|
Scope.create({ name: 'userScope', description: 'access user information' },
|
||||||
ACL.create({principalType: ACL.SCOPE, principalId: scope.id, model: 'User', property: ACL.ALL,
|
function(err, scope) {
|
||||||
accessType: ACL.ALL, permission: ACL.ALLOW},
|
ACL.create({
|
||||||
function(err, resource) {
|
principalType: ACL.SCOPE, principalId: scope.id,
|
||||||
|
model: 'User', property: ACL.ALL,
|
||||||
|
accessType: ACL.ALL, permission: ACL.ALLOW,
|
||||||
|
}, function(err, resource) {
|
||||||
Scope.checkPermission('userScope', 'User', ACL.ALL, ACL.ALL, checkResult);
|
Scope.checkPermission('userScope', 'User', ACL.ALL, ACL.ALL, checkResult);
|
||||||
Scope.checkPermission('userScope', 'User', 'name', ACL.ALL, checkResult);
|
Scope.checkPermission('userScope', 'User', 'name', ACL.ALL, checkResult);
|
||||||
Scope.checkPermission('userScope', 'User', 'name', ACL.READ, checkResult);
|
Scope.checkPermission('userScope', 'User', 'name', ACL.READ, checkResult);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow access to models for the given scope', function() {
|
it('should allow access to models for the given scope', function() {
|
||||||
Scope.create({name: 'testModelScope', description: 'access testModel information'}, function(err, scope) {
|
Scope.create({ name: 'testModelScope', description: 'access testModel information' },
|
||||||
ACL.create({principalType: ACL.SCOPE, principalId: scope.id,
|
function(err, scope) {
|
||||||
model: 'testModel', property: 'name', accessType: ACL.READ, permission: ACL.ALLOW},
|
ACL.create({
|
||||||
function(err, resource) {
|
principalType: ACL.SCOPE, principalId: scope.id,
|
||||||
ACL.create({principalType: ACL.SCOPE, principalId: scope.id,
|
model: 'testModel', property: 'name',
|
||||||
model: 'testModel', property: 'name', accessType: ACL.WRITE, permission: ACL.DENY},
|
accessType: ACL.READ, permission: ACL.ALLOW,
|
||||||
function(err, resource) {
|
}, function(err, resource) {
|
||||||
// console.log(resource);
|
ACL.create({ principalType: ACL.SCOPE, principalId: scope.id,
|
||||||
Scope.checkPermission('testModelScope', 'testModel', ACL.ALL, ACL.ALL, function(err, perm) {
|
model: 'testModel', property: 'name',
|
||||||
assert(perm.permission === ACL.DENY); // because name.WRITE == DENY
|
accessType: ACL.WRITE, permission: ACL.DENY,
|
||||||
});
|
}, function(err, resource) {
|
||||||
Scope.checkPermission('testModelScope', 'testModel', 'name', ACL.ALL, function(err, perm) {
|
// console.log(resource);
|
||||||
assert(perm.permission === ACL.DENY); // because name.WRITE == DENY
|
Scope.checkPermission('testModelScope', 'testModel', ACL.ALL, ACL.ALL,
|
||||||
});
|
function(err, perm) {
|
||||||
Scope.checkPermission('testModelScope', 'testModel', 'name', ACL.READ, function(err, perm) {
|
assert(perm.permission === ACL.DENY); // because name.WRITE == DENY
|
||||||
assert(perm.permission === ACL.ALLOW);
|
});
|
||||||
});
|
Scope.checkPermission('testModelScope', 'testModel', 'name', ACL.ALL,
|
||||||
Scope.checkPermission('testModelScope', 'testModel', 'name', ACL.WRITE, function(err, perm) {
|
function(err, perm) {
|
||||||
assert(perm.permission === ACL.DENY);
|
assert(perm.permission === ACL.DENY); // because name.WRITE == DENY
|
||||||
});
|
});
|
||||||
|
Scope.checkPermission('testModelScope', 'testModel', 'name', ACL.READ,
|
||||||
|
function(err, perm) {
|
||||||
|
assert(perm.permission === ACL.ALLOW);
|
||||||
|
});
|
||||||
|
Scope.checkPermission('testModelScope', 'testModel', 'name', ACL.WRITE,
|
||||||
|
function(err, perm) {
|
||||||
|
assert(perm.permission === ACL.DENY);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('security ACLs', function() {
|
describe('security ACLs', function() {
|
||||||
|
@ -79,26 +87,26 @@ describe('security ACLs', function() {
|
||||||
'accessType': '*',
|
'accessType': '*',
|
||||||
'permission': 'DENY',
|
'permission': 'DENY',
|
||||||
'principalType': 'ROLE',
|
'principalType': 'ROLE',
|
||||||
'principalId': '$everyone'
|
'principalId': '$everyone',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'model': 'account',
|
'model': 'account',
|
||||||
'accessType': '*',
|
'accessType': '*',
|
||||||
'permission': 'ALLOW',
|
'permission': 'ALLOW',
|
||||||
'principalType': 'ROLE',
|
'principalType': 'ROLE',
|
||||||
'principalId': '$owner'
|
'principalId': '$owner',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'model': 'account',
|
'model': 'account',
|
||||||
'accessType': 'READ',
|
'accessType': 'READ',
|
||||||
'permission': 'ALLOW',
|
'permission': 'ALLOW',
|
||||||
'principalType': 'ROLE',
|
'principalType': 'ROLE',
|
||||||
'principalId': '$everyone'
|
'principalId': '$everyone',
|
||||||
}];
|
}];
|
||||||
var req = {
|
var req = {
|
||||||
model: 'account',
|
model: 'account',
|
||||||
property: 'find',
|
property: 'find',
|
||||||
accessType: 'WRITE'
|
accessType: 'WRITE',
|
||||||
};
|
};
|
||||||
|
|
||||||
acls = acls.map(function(a) { return new ACL(a); });
|
acls = acls.map(function(a) { return new ACL(a); });
|
||||||
|
@ -108,17 +116,19 @@ describe('security ACLs', function() {
|
||||||
property: 'find',
|
property: 'find',
|
||||||
accessType: 'WRITE',
|
accessType: 'WRITE',
|
||||||
permission: 'ALLOW',
|
permission: 'ALLOW',
|
||||||
methodNames: []});
|
methodNames: [] });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow access to models for the given principal by wildcard', function() {
|
it('should allow access to models for the given principal by wildcard', function() {
|
||||||
// jscs:disable validateIndentation
|
// jscs:disable validateIndentation
|
||||||
ACL.create({principalType: ACL.USER, principalId: 'u001', model: 'User', property: ACL.ALL,
|
ACL.create({
|
||||||
accessType: ACL.ALL, permission: ACL.ALLOW}, function(err, acl) {
|
principalType: ACL.USER, principalId: 'u001', model: 'User', property: ACL.ALL,
|
||||||
|
accessType: ACL.ALL, permission: ACL.ALLOW,
|
||||||
ACL.create({principalType: ACL.USER, principalId: 'u001', model: 'User', property: ACL.ALL,
|
}, function(err, acl) {
|
||||||
accessType: ACL.READ, permission: ACL.DENY}, function(err, acl) {
|
ACL.create({
|
||||||
|
principalType: ACL.USER, principalId: 'u001', model: 'User', property: ACL.ALL,
|
||||||
|
accessType: ACL.READ, permission: ACL.DENY,
|
||||||
|
}, function(err, acl) {
|
||||||
ACL.checkPermission(ACL.USER, 'u001', 'User', 'name', ACL.READ, function(err, perm) {
|
ACL.checkPermission(ACL.USER, 'u001', 'User', 'name', ACL.READ, function(err, perm) {
|
||||||
assert(perm.permission === ACL.DENY);
|
assert(perm.permission === ACL.DENY);
|
||||||
});
|
});
|
||||||
|
@ -126,52 +136,55 @@ describe('security ACLs', function() {
|
||||||
ACL.checkPermission(ACL.USER, 'u001', 'User', 'name', ACL.ALL, function(err, perm) {
|
ACL.checkPermission(ACL.USER, 'u001', 'User', 'name', ACL.ALL, function(err, perm) {
|
||||||
assert(perm.permission === ACL.DENY);
|
assert(perm.permission === ACL.DENY);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should allow access to models by exception', function() {
|
it('should allow access to models by exception', function() {
|
||||||
ACL.create({principalType: ACL.USER, principalId: 'u001', model: 'testModel', property: ACL.ALL,
|
ACL.create({
|
||||||
accessType: ACL.ALL, permission: ACL.DENY}, function(err, acl) {
|
principalType: ACL.USER, principalId: 'u001', model: 'testModel', property: ACL.ALL,
|
||||||
|
accessType: ACL.ALL, permission: ACL.DENY,
|
||||||
ACL.create({principalType: ACL.USER, principalId: 'u001', model: 'testModel', property: ACL.ALL,
|
}, function(err, acl) {
|
||||||
accessType: ACL.READ, permission: ACL.ALLOW}, function(err, acl) {
|
ACL.create({
|
||||||
|
principalType: ACL.USER, principalId: 'u001', model: 'testModel', property: ACL.ALL,
|
||||||
ACL.create({principalType: ACL.USER, principalId: 'u002', model: 'testModel', property: ACL.ALL,
|
accessType: ACL.READ, permission: ACL.ALLOW,
|
||||||
accessType: ACL.EXECUTE, permission: ACL.ALLOW}, function(err, acl) {
|
}, function(err, acl) {
|
||||||
|
ACL.create({
|
||||||
ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.READ, function(err, perm) {
|
principalType: ACL.USER, principalId: 'u002', model: 'testModel', property: ACL.ALL,
|
||||||
|
accessType: ACL.EXECUTE, permission: ACL.ALLOW,
|
||||||
|
}, function(err, acl) {
|
||||||
|
ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.READ,
|
||||||
|
function(err, perm) {
|
||||||
assert(perm.permission === ACL.ALLOW);
|
assert(perm.permission === ACL.ALLOW);
|
||||||
});
|
});
|
||||||
|
|
||||||
ACL.checkPermission(ACL.USER, 'u001', 'testModel', ACL.ALL, ACL.READ, function(err, perm) {
|
ACL.checkPermission(ACL.USER, 'u001', 'testModel', ACL.ALL, ACL.READ,
|
||||||
|
function(err, perm) {
|
||||||
assert(perm.permission === ACL.ALLOW);
|
assert(perm.permission === ACL.ALLOW);
|
||||||
});
|
});
|
||||||
|
|
||||||
ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.WRITE, function(err, perm) {
|
ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.WRITE,
|
||||||
|
function(err, perm) {
|
||||||
assert(perm.permission === ACL.DENY);
|
assert(perm.permission === ACL.DENY);
|
||||||
});
|
});
|
||||||
|
|
||||||
ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.ALL, function(err, perm) {
|
ACL.checkPermission(ACL.USER, 'u001', 'testModel', 'name', ACL.ALL,
|
||||||
|
function(err, perm) {
|
||||||
assert(perm.permission === ACL.DENY);
|
assert(perm.permission === ACL.DENY);
|
||||||
});
|
});
|
||||||
|
|
||||||
ACL.checkPermission(ACL.USER, 'u002', 'testModel', 'name', ACL.WRITE, function(err, perm) {
|
ACL.checkPermission(ACL.USER, 'u002', 'testModel', 'name', ACL.WRITE,
|
||||||
|
function(err, perm) {
|
||||||
assert(perm.permission === ACL.ALLOW);
|
assert(perm.permission === ACL.ALLOW);
|
||||||
});
|
});
|
||||||
|
|
||||||
ACL.checkPermission(ACL.USER, 'u002', 'testModel', 'name', ACL.READ, function(err, perm) {
|
ACL.checkPermission(ACL.USER, 'u002', 'testModel', 'name', ACL.READ,
|
||||||
|
function(err, perm) {
|
||||||
assert(perm.permission === ACL.ALLOW);
|
assert(perm.permission === ACL.ALLOW);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should honor defaultPermission from the model', function() {
|
it('should honor defaultPermission from the model', function() {
|
||||||
|
@ -179,19 +192,23 @@ describe('security ACLs', function() {
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
acls: [
|
acls: [
|
||||||
{principalType: ACL.USER, principalId: 'u001', accessType: ACL.WRITE, permission: ACL.DENY},
|
{ principalType: ACL.USER, principalId: 'u001',
|
||||||
{principalType: ACL.USER, principalId: 'u001', accessType: ACL.ALL, permission: ACL.ALLOW}
|
accessType: ACL.WRITE, permission: ACL.DENY },
|
||||||
]
|
{ principalType: ACL.USER, principalId: 'u001',
|
||||||
}
|
accessType: ACL.ALL, permission: ACL.ALLOW },
|
||||||
|
],
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
acls: [
|
acls: [
|
||||||
{principalType: ACL.USER, principalId: 'u001', accessType: ACL.ALL, permission: ACL.ALLOW}
|
{ principalType: ACL.USER, principalId: 'u001',
|
||||||
]
|
accessType: ACL.ALL, permission: ACL.ALLOW },
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
Customer.settings.defaultPermission = ACL.DENY;
|
Customer.settings.defaultPermission = ACL.DENY;
|
||||||
|
|
||||||
ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.WRITE, function(err, perm) {
|
ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.WRITE,
|
||||||
|
function(err, perm) {
|
||||||
assert(perm.permission === ACL.DENY);
|
assert(perm.permission === ACL.DENY);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -202,7 +219,6 @@ describe('security ACLs', function() {
|
||||||
ACL.checkPermission(ACL.USER, 'u002', 'Customer', 'name', ACL.WRITE, function(err, perm) {
|
ACL.checkPermission(ACL.USER, 'u002', 'Customer', 'name', ACL.WRITE, function(err, perm) {
|
||||||
assert(perm.permission === ACL.DENY);
|
assert(perm.permission === ACL.DENY);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should honor static ACLs from the model', function() {
|
it('should honor static ACLs from the model', function() {
|
||||||
|
@ -210,16 +226,21 @@ describe('security ACLs', function() {
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
acls: [
|
acls: [
|
||||||
{principalType: ACL.USER, principalId: 'u001', accessType: ACL.WRITE, permission: ACL.DENY},
|
{ principalType: ACL.USER, principalId: 'u001',
|
||||||
{principalType: ACL.USER, principalId: 'u001', accessType: ACL.ALL, permission: ACL.ALLOW}
|
accessType: ACL.WRITE, permission: ACL.DENY },
|
||||||
]
|
{ principalType: ACL.USER, principalId: 'u001',
|
||||||
}
|
accessType: ACL.ALL, permission: ACL.ALLOW },
|
||||||
|
],
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
acls: [
|
acls: [
|
||||||
{principalType: ACL.USER, principalId: 'u001', accessType: ACL.ALL, permission: ACL.ALLOW},
|
{ principalType: ACL.USER, principalId: 'u001',
|
||||||
{principalType: ACL.USER, principalId: 'u002', accessType: ACL.EXECUTE, permission: ACL.ALLOW},
|
accessType: ACL.ALL, permission: ACL.ALLOW },
|
||||||
{principalType: ACL.USER, principalId: 'u003', accessType: ACL.EXECUTE, permission: ACL.DENY}
|
{ principalType: ACL.USER, principalId: 'u002',
|
||||||
]
|
accessType: ACL.EXECUTE, permission: ACL.ALLOW },
|
||||||
|
{ principalType: ACL.USER, principalId: 'u003',
|
||||||
|
accessType: ACL.EXECUTE, permission: ACL.DENY },
|
||||||
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -228,26 +249,30 @@ describe('security ACLs', function() {
|
||||||
];
|
];
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.WRITE, function(err, perm) {
|
ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.WRITE,
|
||||||
|
function(err, perm) {
|
||||||
assert(perm.permission === ACL.DENY);
|
assert(perm.permission === ACL.DENY);
|
||||||
});
|
});
|
||||||
|
|
||||||
ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.READ, function(err, perm) {
|
ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.READ,
|
||||||
|
function(err, perm) {
|
||||||
assert(perm.permission === ACL.ALLOW);
|
assert(perm.permission === ACL.ALLOW);
|
||||||
});
|
});
|
||||||
|
|
||||||
ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.ALL, function(err, perm) {
|
ACL.checkPermission(ACL.USER, 'u001', 'Customer', 'name', ACL.ALL,
|
||||||
|
function(err, perm) {
|
||||||
assert(perm.permission === ACL.ALLOW);
|
assert(perm.permission === ACL.ALLOW);
|
||||||
});
|
});
|
||||||
|
|
||||||
ACL.checkPermission(ACL.USER, 'u002', 'Customer', 'name', ACL.READ, function(err, perm) {
|
ACL.checkPermission(ACL.USER, 'u002', 'Customer', 'name', ACL.READ,
|
||||||
|
function(err, perm) {
|
||||||
assert(perm.permission === ACL.ALLOW);
|
assert(perm.permission === ACL.ALLOW);
|
||||||
});
|
});
|
||||||
|
|
||||||
ACL.checkPermission(ACL.USER, 'u003', 'Customer', 'name', ACL.WRITE, function(err, perm) {
|
ACL.checkPermission(ACL.USER, 'u003', 'Customer', 'name', ACL.WRITE,
|
||||||
|
function(err, perm) {
|
||||||
assert(perm.permission === ACL.DENY);
|
assert(perm.permission === ACL.DENY);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should filter static ACLs by model/property', function() {
|
it('should filter static ACLs by model/property', function() {
|
||||||
|
@ -255,21 +280,21 @@ describe('security ACLs', function() {
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
acls: [
|
acls: [
|
||||||
{principalType: ACL.USER, principalId: 'u001',
|
{ principalType: ACL.USER, principalId: 'u001',
|
||||||
accessType: ACL.WRITE, permission: ACL.DENY},
|
accessType: ACL.WRITE, permission: ACL.DENY },
|
||||||
{principalType: ACL.USER, principalId: 'u001',
|
{ principalType: ACL.USER, principalId: 'u001',
|
||||||
accessType: ACL.ALL, permission: ACL.ALLOW}
|
accessType: ACL.ALL, permission: ACL.ALLOW },
|
||||||
]
|
],
|
||||||
}
|
},
|
||||||
}, {
|
}, {
|
||||||
acls: [
|
acls: [
|
||||||
{principalType: ACL.USER, principalId: 'u001', property: 'name',
|
{ principalType: ACL.USER, principalId: 'u001', property: 'name',
|
||||||
accessType: ACL.ALL, permission: ACL.ALLOW},
|
accessType: ACL.ALL, permission: ACL.ALLOW },
|
||||||
{principalType: ACL.USER, principalId: 'u002', property: 'findOne',
|
{ principalType: ACL.USER, principalId: 'u002', property: 'findOne',
|
||||||
accessType: ACL.ALL, permission: ACL.ALLOW},
|
accessType: ACL.ALL, permission: ACL.ALLOW },
|
||||||
{principalType: ACL.USER, principalId: 'u003', property: ['findOne', 'findById'],
|
{ principalType: ACL.USER, principalId: 'u003', property: ['findOne', 'findById'],
|
||||||
accessType: ACL.ALL, permission: ACL.ALLOW}
|
accessType: ACL.ALL, permission: ACL.ALLOW },
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
var staticACLs = ACL.getStaticACLs('Model1', 'name');
|
var staticACLs = ACL.getStaticACLs('Model1', 'name');
|
||||||
|
@ -288,8 +313,7 @@ describe('security ACLs', function() {
|
||||||
var log = function() {};
|
var log = function() {};
|
||||||
|
|
||||||
// Create
|
// Create
|
||||||
User.create({name: 'Raymond', email: 'x@y.com', password: 'foobar'}, function(err, user) {
|
User.create({ name: 'Raymond', email: 'x@y.com', password: 'foobar' }, function(err, user) {
|
||||||
|
|
||||||
log('User: ', user.toObject());
|
log('User: ', user.toObject());
|
||||||
|
|
||||||
var userId = user.id;
|
var userId = user.id;
|
||||||
|
@ -299,56 +323,62 @@ describe('security ACLs', function() {
|
||||||
name: {
|
name: {
|
||||||
type: String,
|
type: String,
|
||||||
acls: [
|
acls: [
|
||||||
{principalType: ACL.USER, principalId: userId, accessType: ACL.WRITE, permission: ACL.DENY},
|
{ principalType: ACL.USER, principalId: userId,
|
||||||
{principalType: ACL.USER, principalId: userId, accessType: ACL.ALL, permission: ACL.ALLOW}
|
accessType: ACL.WRITE, permission: ACL.DENY },
|
||||||
]
|
{ principalType: ACL.USER, principalId: userId,
|
||||||
}
|
accessType: ACL.ALL, permission: ACL.ALLOW },
|
||||||
|
],
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
acls: [
|
acls: [
|
||||||
{principalType: ACL.USER, principalId: userId, accessType: ACL.ALL, permission: ACL.ALLOW}
|
{ principalType: ACL.USER, principalId: userId,
|
||||||
|
accessType: ACL.ALL, permission: ACL.ALLOW },
|
||||||
],
|
],
|
||||||
defaultPermission: 'DENY'
|
defaultPermission: 'DENY',
|
||||||
});
|
});
|
||||||
|
|
||||||
ACL.create({principalType: ACL.USER, principalId: userId, model: 'Customer', property: ACL.ALL,
|
ACL.create({
|
||||||
accessType: ACL.ALL, permission: ACL.ALLOW}, function(err, acl) {
|
principalType: ACL.USER, principalId: userId,
|
||||||
|
model: 'Customer', property: ACL.ALL,
|
||||||
|
accessType: ACL.ALL, permission: ACL.ALLOW,
|
||||||
|
}, function(err, acl) {
|
||||||
log('ACL 1: ', acl.toObject());
|
log('ACL 1: ', acl.toObject());
|
||||||
|
|
||||||
Role.create({name: 'MyRole'}, function(err, myRole) {
|
Role.create({ name: 'MyRole' }, function(err, myRole) {
|
||||||
log('Role: ', myRole.toObject());
|
log('Role: ', myRole.toObject());
|
||||||
|
|
||||||
myRole.principals.create({principalType: RoleMapping.USER, principalId: userId}, function(err, p) {
|
myRole.principals.create({ principalType: RoleMapping.USER, principalId: userId },
|
||||||
|
function(err, p) {
|
||||||
log('Principal added to role: ', p.toObject());
|
log('Principal added to role: ', p.toObject());
|
||||||
|
|
||||||
ACL.create({principalType: ACL.ROLE, principalId: 'MyRole', model: 'Customer', property: ACL.ALL,
|
ACL.create({
|
||||||
accessType: ACL.READ, permission: ACL.DENY}, function(err, acl) {
|
principalType: ACL.ROLE, principalId: 'MyRole',
|
||||||
|
model: 'Customer', property: ACL.ALL,
|
||||||
|
accessType: ACL.READ, permission: ACL.DENY,
|
||||||
|
}, function(err, acl) {
|
||||||
log('ACL 2: ', acl.toObject());
|
log('ACL 2: ', acl.toObject());
|
||||||
|
|
||||||
ACL.checkAccessForContext({
|
ACL.checkAccessForContext({
|
||||||
principals: [
|
principals: [
|
||||||
{type: ACL.USER, id: userId}
|
{ type: ACL.USER, id: userId },
|
||||||
],
|
],
|
||||||
model: 'Customer',
|
model: 'Customer',
|
||||||
property: 'name',
|
property: 'name',
|
||||||
accessType: ACL.READ
|
accessType: ACL.READ,
|
||||||
}, function(err, access) {
|
}, function(err, access) {
|
||||||
assert(!err && access.permission === ACL.ALLOW);
|
assert(!err && access.permission === ACL.ALLOW);
|
||||||
});
|
});
|
||||||
|
|
||||||
ACL.checkAccessForContext({
|
ACL.checkAccessForContext({
|
||||||
principals: [
|
principals: [
|
||||||
{type: ACL.ROLE, id: Role.EVERYONE}
|
{ type: ACL.ROLE, id: Role.EVERYONE },
|
||||||
],
|
],
|
||||||
model: 'Customer',
|
model: 'Customer',
|
||||||
property: 'name',
|
property: 'name',
|
||||||
accessType: ACL.READ
|
accessType: ACL.READ,
|
||||||
}, function(err, access) {
|
}, function(err, access) {
|
||||||
assert(!err && access.permission === ACL.DENY);
|
assert(!err && access.permission === ACL.DENY);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -363,11 +393,11 @@ describe('access check', function() {
|
||||||
app = loopback();
|
app = loopback();
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
app.enableAuth();
|
app.enableAuth();
|
||||||
app.dataSource('test', {connector: 'memory'});
|
app.dataSource('test', { connector: 'memory' });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should occur before other remote hooks', function(done) {
|
it('should occur before other remote hooks', function(done) {
|
||||||
var MyTestModel = app.model('MyTestModel', {base: 'PersistedModel', dataSource: 'test'});
|
var MyTestModel = app.model('MyTestModel', { base: 'PersistedModel', dataSource: 'test' });
|
||||||
var checkAccessCalled = false;
|
var checkAccessCalled = false;
|
||||||
var beforeHookCalled = false;
|
var beforeHookCalled = false;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/*jshint -W030 */
|
|
||||||
|
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
|
||||||
|
@ -13,8 +11,7 @@ var it = require('./util/it');
|
||||||
|
|
||||||
describe('app', function() {
|
describe('app', function() {
|
||||||
describe.onServer('.middleware(phase, handler)', function() {
|
describe.onServer('.middleware(phase, handler)', function() {
|
||||||
var app;
|
var app, steps;
|
||||||
var steps;
|
|
||||||
|
|
||||||
beforeEach(function setup() {
|
beforeEach(function setup() {
|
||||||
app = loopback();
|
app = loopback();
|
||||||
|
@ -24,7 +21,7 @@ describe('app', function() {
|
||||||
it('runs middleware in phases', function(done) {
|
it('runs middleware in phases', function(done) {
|
||||||
var PHASES = [
|
var PHASES = [
|
||||||
'initial', 'session', 'auth', 'parse',
|
'initial', 'session', 'auth', 'parse',
|
||||||
'routes', 'files', 'final'
|
'routes', 'files', 'final',
|
||||||
];
|
];
|
||||||
|
|
||||||
PHASES.forEach(function(name) {
|
PHASES.forEach(function(name) {
|
||||||
|
@ -36,7 +33,7 @@ describe('app', function() {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(steps).to.eql([
|
expect(steps).to.eql([
|
||||||
'initial', 'session', 'auth', 'parse',
|
'initial', 'session', 'auth', 'parse',
|
||||||
'main', 'routes', 'files', 'final'
|
'main', 'routes', 'files', 'final',
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -233,8 +230,7 @@ describe('app', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('exposes express helpers on req and res objects', function(done) {
|
it('exposes express helpers on req and res objects', function(done) {
|
||||||
var req;
|
var req, res;
|
||||||
var res;
|
|
||||||
|
|
||||||
app.middleware('initial', function(rq, rs, next) {
|
app.middleware('initial', function(rq, rs, next) {
|
||||||
req = rq;
|
req = rq;
|
||||||
|
@ -250,7 +246,7 @@ describe('app', function() {
|
||||||
'param',
|
'param',
|
||||||
'params',
|
'params',
|
||||||
'query',
|
'query',
|
||||||
'res'
|
'res',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(getObjectAndPrototypeKeys(res), 'response').to.include.members([
|
expect(getObjectAndPrototypeKeys(res), 'response').to.include.members([
|
||||||
|
@ -262,7 +258,7 @@ describe('app', function() {
|
||||||
'req',
|
'req',
|
||||||
'send',
|
'send',
|
||||||
'sendFile',
|
'sendFile',
|
||||||
'set'
|
'set',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
|
@ -316,13 +312,12 @@ describe('app', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('correctly mounts express apps', function(done) {
|
it('correctly mounts express apps', function(done) {
|
||||||
var data;
|
var data, mountWasEmitted;
|
||||||
var mountWasEmitted;
|
|
||||||
var subapp = express();
|
var subapp = express();
|
||||||
subapp.use(function(req, res, next) {
|
subapp.use(function(req, res, next) {
|
||||||
data = {
|
data = {
|
||||||
mountpath: req.app.mountpath,
|
mountpath: req.app.mountpath,
|
||||||
parent: req.app.parent
|
parent: req.app.parent,
|
||||||
};
|
};
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
@ -335,14 +330,13 @@ describe('app', function() {
|
||||||
expect(mountWasEmitted, 'mountWasEmitted').to.be.true;
|
expect(mountWasEmitted, 'mountWasEmitted').to.be.true;
|
||||||
expect(data).to.eql({
|
expect(data).to.eql({
|
||||||
mountpath: '/mountpath',
|
mountpath: '/mountpath',
|
||||||
parent: app
|
parent: app,
|
||||||
});
|
});
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('restores req & res on return from mounted express app', function(done) {
|
it('restores req & res on return from mounted express app', function(done) {
|
||||||
// jshint proto:true
|
|
||||||
var expected = {};
|
var expected = {};
|
||||||
var actual = {};
|
var actual = {};
|
||||||
|
|
||||||
|
@ -414,28 +408,28 @@ describe('app', function() {
|
||||||
app.middlewareFromConfig(handlerFactory, {
|
app.middlewareFromConfig(handlerFactory, {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
phase: 'session',
|
phase: 'session',
|
||||||
params: expectedConfig
|
params: expectedConfig,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Config as a value (single arg)
|
// Config as a value (single arg)
|
||||||
app.middlewareFromConfig(handlerFactory, {
|
app.middlewareFromConfig(handlerFactory, {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
phase: 'session:before',
|
phase: 'session:before',
|
||||||
params: 'before'
|
params: 'before',
|
||||||
});
|
});
|
||||||
|
|
||||||
// Config as a list of args
|
// Config as a list of args
|
||||||
app.middlewareFromConfig(handlerFactory, {
|
app.middlewareFromConfig(handlerFactory, {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
phase: 'session:after',
|
phase: 'session:after',
|
||||||
params: ['after', 2]
|
params: ['after', 2],
|
||||||
});
|
});
|
||||||
|
|
||||||
// Disabled by configuration
|
// Disabled by configuration
|
||||||
app.middlewareFromConfig(handlerFactory, {
|
app.middlewareFromConfig(handlerFactory, {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
phase: 'initial',
|
phase: 'initial',
|
||||||
params: null
|
params: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
// This should be triggered with matching verbs
|
// This should be triggered with matching verbs
|
||||||
|
@ -443,7 +437,7 @@ describe('app', function() {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
phase: 'routes:before',
|
phase: 'routes:before',
|
||||||
methods: ['get', 'head'],
|
methods: ['get', 'head'],
|
||||||
params: {x: 1}
|
params: { x: 1 },
|
||||||
});
|
});
|
||||||
|
|
||||||
// This should be skipped as the verb doesn't match
|
// This should be skipped as the verb doesn't match
|
||||||
|
@ -451,7 +445,7 @@ describe('app', function() {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
phase: 'routes:before',
|
phase: 'routes:before',
|
||||||
methods: ['post'],
|
methods: ['post'],
|
||||||
params: {x: 2}
|
params: { x: 2 },
|
||||||
});
|
});
|
||||||
|
|
||||||
executeMiddlewareHandlers(app, function(err) {
|
executeMiddlewareHandlers(app, function(err) {
|
||||||
|
@ -460,7 +454,7 @@ describe('app', function() {
|
||||||
['before'],
|
['before'],
|
||||||
[expectedConfig],
|
[expectedConfig],
|
||||||
['after', 2],
|
['after', 2],
|
||||||
[{x: 1}]
|
[{ x: 1 }],
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -477,7 +471,7 @@ describe('app', function() {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
phase: 'initial',
|
phase: 'initial',
|
||||||
paths: ['/scope', /^\/(a|b)/]
|
paths: ['/scope', /^\/(a|b)/],
|
||||||
});
|
});
|
||||||
|
|
||||||
async.eachSeries(
|
async.eachSeries(
|
||||||
|
@ -508,7 +502,7 @@ describe('app', function() {
|
||||||
'first',
|
'first',
|
||||||
'initial', // this was the original first phase
|
'initial', // this was the original first phase
|
||||||
'routes',
|
'routes',
|
||||||
'subapps'
|
'subapps',
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -519,7 +513,7 @@ describe('app', function() {
|
||||||
'auth', 'routes',
|
'auth', 'routes',
|
||||||
'subapps', // add
|
'subapps', // add
|
||||||
'final',
|
'final',
|
||||||
'last' // add
|
'last', // add
|
||||||
]);
|
]);
|
||||||
verifyMiddlewarePhases([
|
verifyMiddlewarePhases([
|
||||||
'initial',
|
'initial',
|
||||||
|
@ -527,7 +521,7 @@ describe('app', function() {
|
||||||
'auth', 'routes',
|
'auth', 'routes',
|
||||||
'subapps', // new
|
'subapps', // new
|
||||||
'files', 'final',
|
'files', 'final',
|
||||||
'last' // new
|
'last', // new
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -555,15 +549,14 @@ describe('app', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('app.model(Model)', function() {
|
describe('app.model(Model)', function() {
|
||||||
var app;
|
var app, db;
|
||||||
var db;
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
app = loopback();
|
app = loopback();
|
||||||
db = loopback.createDataSource({connector: loopback.Memory});
|
db = loopback.createDataSource({ connector: loopback.Memory });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Expose a `Model` to remote clients', function() {
|
it('Expose a `Model` to remote clients', function() {
|
||||||
var Color = PersistedModel.extend('color', {name: String});
|
var Color = PersistedModel.extend('color', { name: String });
|
||||||
app.model(Color);
|
app.model(Color);
|
||||||
Color.attachTo(db);
|
Color.attachTo(db);
|
||||||
|
|
||||||
|
@ -571,22 +564,22 @@ describe('app', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uses singlar name as app.remoteObjects() key', function() {
|
it('uses singlar name as app.remoteObjects() key', function() {
|
||||||
var Color = PersistedModel.extend('color', {name: String});
|
var Color = PersistedModel.extend('color', { name: String });
|
||||||
app.model(Color);
|
app.model(Color);
|
||||||
Color.attachTo(db);
|
Color.attachTo(db);
|
||||||
expect(app.remoteObjects()).to.eql({ color: Color });
|
expect(app.remoteObjects()).to.eql({ color: Color });
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uses singular name as shared class name', function() {
|
it('uses singular name as shared class name', function() {
|
||||||
var Color = PersistedModel.extend('color', {name: String});
|
var Color = PersistedModel.extend('color', { name: String });
|
||||||
app.model(Color);
|
app.model(Color);
|
||||||
Color.attachTo(db);
|
Color.attachTo(db);
|
||||||
var classes = app.remotes().classes().map(function(c) {return c.name;});
|
var classes = app.remotes().classes().map(function(c) { return c.name; });
|
||||||
expect(classes).to.contain('color');
|
expect(classes).to.contain('color');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('registers existing models to app.models', function() {
|
it('registers existing models to app.models', function() {
|
||||||
var Color = db.createModel('color', {name: String});
|
var Color = db.createModel('color', { name: String });
|
||||||
app.model(Color);
|
app.model(Color);
|
||||||
expect(Color.app).to.be.equal(app);
|
expect(Color.app).to.be.equal(app);
|
||||||
expect(Color.shared).to.equal(true);
|
expect(Color.shared).to.equal(true);
|
||||||
|
@ -595,7 +588,7 @@ describe('app', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('emits a `modelRemoted` event', function() {
|
it('emits a `modelRemoted` event', function() {
|
||||||
var Color = PersistedModel.extend('color', {name: String});
|
var Color = PersistedModel.extend('color', { name: String });
|
||||||
Color.shared = true;
|
Color.shared = true;
|
||||||
var remotedClass;
|
var remotedClass;
|
||||||
app.on('modelRemoted', function(sharedClass) {
|
app.on('modelRemoted', function(sharedClass) {
|
||||||
|
@ -610,7 +603,7 @@ describe('app', function() {
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
request(app).get('/colors').expect(404, function(err, res) {
|
request(app).get('/colors').expect(404, function(err, res) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
var Color = PersistedModel.extend('color', {name: String});
|
var Color = PersistedModel.extend('color', { name: String });
|
||||||
app.model(Color);
|
app.model(Color);
|
||||||
Color.attachTo(db);
|
Color.attachTo(db);
|
||||||
request(app).get('/colors').expect(200, done);
|
request(app).get('/colors').expect(200, done);
|
||||||
|
@ -628,7 +621,6 @@ describe('app', function() {
|
||||||
it('should not require dataSource', function() {
|
it('should not require dataSource', function() {
|
||||||
app.model('MyTestModel', {});
|
app.model('MyTestModel', {});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('app.model(name, config)', function() {
|
describe('app.model(name, config)', function() {
|
||||||
|
@ -637,13 +629,13 @@ describe('app', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
app = loopback();
|
app = loopback();
|
||||||
app.dataSource('db', {
|
app.dataSource('db', {
|
||||||
connector: 'memory'
|
connector: 'memory',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Sugar for defining a fully built model', function() {
|
it('Sugar for defining a fully built model', function() {
|
||||||
app.model('foo', {
|
app.model('foo', {
|
||||||
dataSource: 'db'
|
dataSource: 'db',
|
||||||
});
|
});
|
||||||
|
|
||||||
var Foo = app.models.foo;
|
var Foo = app.models.foo;
|
||||||
|
@ -655,7 +647,7 @@ describe('app', function() {
|
||||||
it('interprets extra first-level keys as options', function() {
|
it('interprets extra first-level keys as options', function() {
|
||||||
app.model('foo', {
|
app.model('foo', {
|
||||||
dataSource: 'db',
|
dataSource: 'db',
|
||||||
base: 'User'
|
base: 'User',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(app.models.foo.definition.settings.base).to.equal('User');
|
expect(app.models.foo.definition.settings.base).to.equal('User');
|
||||||
|
@ -666,8 +658,8 @@ describe('app', function() {
|
||||||
dataSource: 'db',
|
dataSource: 'db',
|
||||||
base: 'User',
|
base: 'User',
|
||||||
options: {
|
options: {
|
||||||
base: 'Application'
|
base: 'Application',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(app.models.foo.definition.settings.base).to.equal('Application');
|
expect(app.models.foo.definition.settings.base).to.equal('Application');
|
||||||
|
@ -676,7 +668,7 @@ describe('app', function() {
|
||||||
it('honors config.public options', function() {
|
it('honors config.public options', function() {
|
||||||
app.model('foo', {
|
app.model('foo', {
|
||||||
dataSource: 'db',
|
dataSource: 'db',
|
||||||
public: false
|
public: false,
|
||||||
});
|
});
|
||||||
expect(app.models.foo.app).to.equal(app);
|
expect(app.models.foo.app).to.equal(app);
|
||||||
expect(app.models.foo.shared).to.equal(false);
|
expect(app.models.foo.shared).to.equal(false);
|
||||||
|
@ -684,12 +676,11 @@ describe('app', function() {
|
||||||
|
|
||||||
it('defaults config.public to be true', function() {
|
it('defaults config.public to be true', function() {
|
||||||
app.model('foo', {
|
app.model('foo', {
|
||||||
dataSource: 'db'
|
dataSource: 'db',
|
||||||
});
|
});
|
||||||
expect(app.models.foo.app).to.equal(app);
|
expect(app.models.foo.app).to.equal(app);
|
||||||
expect(app.models.foo.shared).to.equal(true);
|
expect(app.models.foo.shared).to.equal(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('app.model(ModelCtor, config)', function() {
|
describe('app.model(ModelCtor, config)', function() {
|
||||||
|
@ -943,8 +934,7 @@ describe('app', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('normalizeHttpPath option', function() {
|
describe('normalizeHttpPath option', function() {
|
||||||
var app;
|
var app, db;
|
||||||
var db;
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
app = loopback();
|
app = loopback();
|
||||||
db = loopback.createDataSource({ connector: loopback.Memory });
|
db = loopback.createDataSource({ connector: loopback.Memory });
|
||||||
|
@ -955,7 +945,7 @@ describe('app', function() {
|
||||||
'UserAccount',
|
'UserAccount',
|
||||||
{ name: String },
|
{ name: String },
|
||||||
{
|
{
|
||||||
remoting: { normalizeHttpPath: true }
|
remoting: { normalizeHttpPath: true },
|
||||||
});
|
});
|
||||||
app.model(UserAccount);
|
app.model(UserAccount);
|
||||||
UserAccount.attachTo(db);
|
UserAccount.attachTo(db);
|
||||||
|
|
|
@ -2,11 +2,11 @@ describe('PersistedModel.createChangeStream()', function() {
|
||||||
describe('configured to source changes locally', function() {
|
describe('configured to source changes locally', function() {
|
||||||
before(function() {
|
before(function() {
|
||||||
var test = this;
|
var test = this;
|
||||||
var app = loopback({localRegistry: true});
|
var app = loopback({ localRegistry: true });
|
||||||
var ds = app.dataSource('ds', {connector: 'memory'});
|
var ds = app.dataSource('ds', { connector: 'memory' });
|
||||||
this.Score = app.model('Score', {
|
this.Score = app.model('Score', {
|
||||||
dataSource: 'ds',
|
dataSource: 'ds',
|
||||||
changeDataSource: false // use only local observers
|
changeDataSource: false, // use only local observers
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -20,13 +20,13 @@ describe('PersistedModel.createChangeStream()', function() {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
Score.create({team: 'foo'});
|
Score.create({ team: 'foo' });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect update', function(done) {
|
it('should detect update', function(done) {
|
||||||
var Score = this.Score;
|
var Score = this.Score;
|
||||||
Score.create({team: 'foo'}, function(err, newScore) {
|
Score.create({ team: 'foo' }, function(err, newScore) {
|
||||||
Score.createChangeStream(function(err, changes) {
|
Score.createChangeStream(function(err, changes) {
|
||||||
changes.on('data', function(change) {
|
changes.on('data', function(change) {
|
||||||
expect(change.type).to.equal('update');
|
expect(change.type).to.equal('update');
|
||||||
|
@ -34,7 +34,7 @@ describe('PersistedModel.createChangeStream()', function() {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
newScore.updateAttributes({
|
newScore.updateAttributes({
|
||||||
bat: 'baz'
|
bat: 'baz',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -42,7 +42,7 @@ describe('PersistedModel.createChangeStream()', function() {
|
||||||
|
|
||||||
it('should detect delete', function(done) {
|
it('should detect delete', function(done) {
|
||||||
var Score = this.Score;
|
var Score = this.Score;
|
||||||
Score.create({team: 'foo'}, function(err, newScore) {
|
Score.create({ team: 'foo' }, function(err, newScore) {
|
||||||
Score.createChangeStream(function(err, changes) {
|
Score.createChangeStream(function(err, changes) {
|
||||||
changes.on('data', function(change) {
|
changes.on('data', function(change) {
|
||||||
expect(change.type).to.equal('remove');
|
expect(change.type).to.equal('remove');
|
||||||
|
@ -60,17 +60,17 @@ describe('PersistedModel.createChangeStream()', function() {
|
||||||
describe.skip('configured to source changes using pubsub', function() {
|
describe.skip('configured to source changes using pubsub', function() {
|
||||||
before(function() {
|
before(function() {
|
||||||
var test = this;
|
var test = this;
|
||||||
var app = loopback({localRegistry: true});
|
var app = loopback({ localRegistry: true });
|
||||||
var db = app.dataSource('ds', {connector: 'memory'});
|
var db = app.dataSource('ds', { connector: 'memory' });
|
||||||
var ps = app.dataSource('ps', {
|
var ps = app.dataSource('ps', {
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
port: '12345',
|
port: '12345',
|
||||||
connector: 'pubsub',
|
connector: 'pubsub',
|
||||||
pubsubAdapter: 'mqtt'
|
pubsubAdapter: 'mqtt',
|
||||||
});
|
});
|
||||||
this.Score = app.model('Score', {
|
this.Score = app.model('Score', {
|
||||||
dataSource: 'db',
|
dataSource: 'db',
|
||||||
changeDataSource: 'ps'
|
changeDataSource: 'ps',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,20 +1,19 @@
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var expect = require('chai').expect;
|
var expect = require('chai').expect;
|
||||||
|
|
||||||
var Change;
|
var Change, TestModel;
|
||||||
var TestModel;
|
|
||||||
|
|
||||||
describe('Change', function() {
|
describe('Change', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
var memory = loopback.createDataSource({
|
var memory = loopback.createDataSource({
|
||||||
connector: loopback.Memory
|
connector: loopback.Memory,
|
||||||
});
|
});
|
||||||
TestModel = loopback.PersistedModel.extend('ChangeTestModel',
|
TestModel = loopback.PersistedModel.extend('ChangeTestModel',
|
||||||
{
|
{
|
||||||
id: { id: true, type: 'string', defaultFn: 'guid' }
|
id: { id: true, type: 'string', defaultFn: 'guid' },
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
trackChanges: true
|
trackChanges: true,
|
||||||
});
|
});
|
||||||
this.modelName = TestModel.modelName;
|
this.modelName = TestModel.modelName;
|
||||||
TestModel.attachTo(memory);
|
TestModel.attachTo(memory);
|
||||||
|
@ -24,7 +23,7 @@ describe('Change', function() {
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
var test = this;
|
var test = this;
|
||||||
test.data = {
|
test.data = {
|
||||||
foo: 'bar'
|
foo: 'bar',
|
||||||
};
|
};
|
||||||
TestModel.create(test.data, function(err, model) {
|
TestModel.create(test.data, function(err, model) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
|
@ -46,7 +45,7 @@ describe('Change', function() {
|
||||||
var change = new Change({
|
var change = new Change({
|
||||||
rev: 'abc',
|
rev: 'abc',
|
||||||
modelName: 'foo',
|
modelName: 'foo',
|
||||||
modelId: 'bar'
|
modelId: 'bar',
|
||||||
});
|
});
|
||||||
|
|
||||||
var hash = Change.hash([change.modelName, change.modelId].join('-'));
|
var hash = Change.hash([change.modelName, change.modelId].join('-'));
|
||||||
|
@ -115,7 +114,6 @@ describe('Change', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Change.findOrCreateChange(modelName, modelId, callback)', function() {
|
describe('Change.findOrCreateChange(modelName, modelId, callback)', function() {
|
||||||
|
|
||||||
describe('when a change doesnt exist', function() {
|
describe('when a change doesnt exist', function() {
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
var test = this;
|
var test = this;
|
||||||
|
@ -162,7 +160,7 @@ describe('Change', function() {
|
||||||
var test = this;
|
var test = this;
|
||||||
Change.create({
|
Change.create({
|
||||||
modelName: test.modelName,
|
modelName: test.modelName,
|
||||||
modelId: test.modelId
|
modelId: test.modelId,
|
||||||
}, function(err, change) {
|
}, function(err, change) {
|
||||||
test.existingChange = change;
|
test.existingChange = change;
|
||||||
done();
|
done();
|
||||||
|
@ -192,7 +190,7 @@ describe('Change', function() {
|
||||||
Change.findOrCreate(
|
Change.findOrCreate(
|
||||||
{
|
{
|
||||||
modelName: this.modelName,
|
modelName: this.modelName,
|
||||||
modelId: this.modelId
|
modelId: this.modelId,
|
||||||
},
|
},
|
||||||
function(err, ch) {
|
function(err, ch) {
|
||||||
change = ch;
|
change = ch;
|
||||||
|
@ -228,7 +226,7 @@ describe('Change', function() {
|
||||||
expect(change.prev, 'prev').to.equal(originalRev);
|
expect(change.prev, 'prev').to.equal(originalRev);
|
||||||
expect(change.rev, 'rev').to.equal(test.revisionForModel);
|
expect(change.rev, 'rev').to.equal(test.revisionForModel);
|
||||||
next();
|
next();
|
||||||
}
|
},
|
||||||
], done);
|
], done);
|
||||||
|
|
||||||
function rectify(next) {
|
function rectify(next) {
|
||||||
|
@ -299,7 +297,7 @@ describe('Change', function() {
|
||||||
var test = this;
|
var test = this;
|
||||||
var change = new Change({
|
var change = new Change({
|
||||||
modelName: this.modelName,
|
modelName: this.modelName,
|
||||||
modelId: this.modelId
|
modelId: this.modelId,
|
||||||
});
|
});
|
||||||
|
|
||||||
change.currentRevision(function(err, rev) {
|
change.currentRevision(function(err, rev) {
|
||||||
|
@ -314,7 +312,7 @@ describe('Change', function() {
|
||||||
var test = this;
|
var test = this;
|
||||||
var change = new Change({
|
var change = new Change({
|
||||||
modelName: this.modelName,
|
modelName: this.modelName,
|
||||||
modelId: this.modelId
|
modelId: this.modelId,
|
||||||
});
|
});
|
||||||
|
|
||||||
change.currentRevision()
|
change.currentRevision()
|
||||||
|
@ -341,14 +339,14 @@ describe('Change', function() {
|
||||||
var a = {
|
var a = {
|
||||||
b: {
|
b: {
|
||||||
b: ['c', 'd'],
|
b: ['c', 'd'],
|
||||||
c: ['d', 'e']
|
c: ['d', 'e'],
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
var b = {
|
var b = {
|
||||||
b: {
|
b: {
|
||||||
c: ['d', 'e'],
|
c: ['d', 'e'],
|
||||||
b: ['c', 'd']
|
b: ['c', 'd'],
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
var aRev = Change.revisionForInst(a);
|
var aRev = Change.revisionForInst(a);
|
||||||
|
@ -360,20 +358,20 @@ describe('Change', function() {
|
||||||
describe('change.type()', function() {
|
describe('change.type()', function() {
|
||||||
it('CREATE', function() {
|
it('CREATE', function() {
|
||||||
var change = new Change({
|
var change = new Change({
|
||||||
rev: this.revisionForModel
|
rev: this.revisionForModel,
|
||||||
});
|
});
|
||||||
assert.equal(Change.CREATE, change.type());
|
assert.equal(Change.CREATE, change.type());
|
||||||
});
|
});
|
||||||
it('UPDATE', function() {
|
it('UPDATE', function() {
|
||||||
var change = new Change({
|
var change = new Change({
|
||||||
rev: this.revisionForModel,
|
rev: this.revisionForModel,
|
||||||
prev: this.revisionForModel
|
prev: this.revisionForModel,
|
||||||
});
|
});
|
||||||
assert.equal(Change.UPDATE, change.type());
|
assert.equal(Change.UPDATE, change.type());
|
||||||
});
|
});
|
||||||
it('DELETE', function() {
|
it('DELETE', function() {
|
||||||
var change = new Change({
|
var change = new Change({
|
||||||
prev: this.revisionForModel
|
prev: this.revisionForModel,
|
||||||
});
|
});
|
||||||
assert.equal(Change.DELETE, change.type());
|
assert.equal(Change.DELETE, change.type());
|
||||||
});
|
});
|
||||||
|
@ -386,7 +384,7 @@ describe('Change', function() {
|
||||||
describe('change.getModelCtor()', function() {
|
describe('change.getModelCtor()', function() {
|
||||||
it('should get the correct model class', function() {
|
it('should get the correct model class', function() {
|
||||||
var change = new Change({
|
var change = new Change({
|
||||||
modelName: this.modelName
|
modelName: this.modelName,
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(change.getModelCtor(), TestModel);
|
assert.equal(change.getModelCtor(), TestModel);
|
||||||
|
@ -396,11 +394,11 @@ describe('Change', function() {
|
||||||
describe('change.equals(otherChange)', function() {
|
describe('change.equals(otherChange)', function() {
|
||||||
it('should return true when the change is equal', function() {
|
it('should return true when the change is equal', function() {
|
||||||
var change = new Change({
|
var change = new Change({
|
||||||
rev: this.revisionForModel
|
rev: this.revisionForModel,
|
||||||
});
|
});
|
||||||
|
|
||||||
var otherChange = new Change({
|
var otherChange = new Change({
|
||||||
rev: this.revisionForModel
|
rev: this.revisionForModel,
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(change.equals(otherChange), true);
|
assert.equal(change.equals(otherChange), true);
|
||||||
|
@ -415,7 +413,7 @@ describe('Change', function() {
|
||||||
|
|
||||||
var otherChange = new Change({
|
var otherChange = new Change({
|
||||||
rev: undefined,
|
rev: undefined,
|
||||||
prev: REV
|
prev: REV,
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(change.type(), Change.DELETE);
|
assert.equal(change.type(), Change.DELETE);
|
||||||
|
@ -428,11 +426,11 @@ describe('Change', function() {
|
||||||
describe('change.isBasedOn(otherChange)', function() {
|
describe('change.isBasedOn(otherChange)', function() {
|
||||||
it('should return true when the change is based on the other', function() {
|
it('should return true when the change is based on the other', function() {
|
||||||
var change = new Change({
|
var change = new Change({
|
||||||
prev: this.revisionForModel
|
prev: this.revisionForModel,
|
||||||
});
|
});
|
||||||
|
|
||||||
var otherChange = new Change({
|
var otherChange = new Change({
|
||||||
rev: this.revisionForModel
|
rev: this.revisionForModel,
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(change.isBasedOn(otherChange), true);
|
assert.equal(change.isBasedOn(otherChange), true);
|
||||||
|
@ -442,20 +440,20 @@ describe('Change', function() {
|
||||||
describe('Change.diff(modelName, since, remoteChanges, callback)', function() {
|
describe('Change.diff(modelName, since, remoteChanges, callback)', function() {
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
Change.create([
|
Change.create([
|
||||||
{rev: 'foo', modelName: this.modelName, modelId: 9, checkpoint: 1},
|
{ rev: 'foo', modelName: this.modelName, modelId: 9, checkpoint: 1 },
|
||||||
{rev: 'bar', modelName: this.modelName, modelId: 10, checkpoint: 1},
|
{ rev: 'bar', modelName: this.modelName, modelId: 10, checkpoint: 1 },
|
||||||
{rev: 'bat', modelName: this.modelName, modelId: 11, checkpoint: 1},
|
{ rev: 'bat', modelName: this.modelName, modelId: 11, checkpoint: 1 },
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return delta and conflict lists', function(done) {
|
it('should return delta and conflict lists', function(done) {
|
||||||
var remoteChanges = [
|
var remoteChanges = [
|
||||||
// an update => should result in a delta
|
// an update => should result in a delta
|
||||||
{rev: 'foo2', prev: 'foo', modelName: this.modelName, modelId: 9, checkpoint: 1},
|
{ rev: 'foo2', prev: 'foo', modelName: this.modelName, modelId: 9, checkpoint: 1 },
|
||||||
// no change => should not result in a delta / conflict
|
// no change => should not result in a delta / conflict
|
||||||
{rev: 'bar', prev: 'bar', modelName: this.modelName, modelId: 10, checkpoint: 1},
|
{ rev: 'bar', prev: 'bar', modelName: this.modelName, modelId: 10, checkpoint: 1 },
|
||||||
// a conflict => should result in a conflict
|
// a conflict => should result in a conflict
|
||||||
{rev: 'bat2', prev: 'bat0', modelName: this.modelName, modelId: 11, checkpoint: 1},
|
{ rev: 'bat2', prev: 'bat0', modelName: this.modelName, modelId: 11, checkpoint: 1 },
|
||||||
];
|
];
|
||||||
|
|
||||||
Change.diff(this.modelName, 0, remoteChanges, function(err, diff) {
|
Change.diff(this.modelName, 0, remoteChanges, function(err, diff) {
|
||||||
|
@ -469,11 +467,11 @@ describe('Change', function() {
|
||||||
it('should return delta and conflict lists - promise variant', function(done) {
|
it('should return delta and conflict lists - promise variant', function(done) {
|
||||||
var remoteChanges = [
|
var remoteChanges = [
|
||||||
// an update => should result in a delta
|
// an update => should result in a delta
|
||||||
{rev: 'foo2', prev: 'foo', modelName: this.modelName, modelId: 9, checkpoint: 1},
|
{ rev: 'foo2', prev: 'foo', modelName: this.modelName, modelId: 9, checkpoint: 1 },
|
||||||
// no change => should not result in a delta / conflict
|
// no change => should not result in a delta / conflict
|
||||||
{rev: 'bar', prev: 'bar', modelName: this.modelName, modelId: 10, checkpoint: 1},
|
{ rev: 'bar', prev: 'bar', modelName: this.modelName, modelId: 10, checkpoint: 1 },
|
||||||
// a conflict => should result in a conflict
|
// a conflict => should result in a conflict
|
||||||
{rev: 'bat2', prev: 'bat0', modelName: this.modelName, modelId: 11, checkpoint: 1},
|
{ rev: 'bat2', prev: 'bat0', modelName: this.modelName, modelId: 11, checkpoint: 1 },
|
||||||
];
|
];
|
||||||
|
|
||||||
Change.diff(this.modelName, 0, remoteChanges)
|
Change.diff(this.modelName, 0, remoteChanges)
|
||||||
|
@ -491,7 +489,7 @@ describe('Change', function() {
|
||||||
prev: 'foo',
|
prev: 'foo',
|
||||||
modelName: this.modelName,
|
modelName: this.modelName,
|
||||||
modelId: '9',
|
modelId: '9',
|
||||||
checkpoint: 2
|
checkpoint: 2,
|
||||||
};
|
};
|
||||||
Change.diff(this.modelName, 0, [updateRecord], function(err, diff) {
|
Change.diff(this.modelName, 0, [updateRecord], function(err, diff) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
|
@ -516,7 +514,7 @@ describe('Change', function() {
|
||||||
prev: 'foo-prev',
|
prev: 'foo-prev',
|
||||||
modelName: this.modelName,
|
modelName: this.modelName,
|
||||||
modelId: '9',
|
modelId: '9',
|
||||||
checkpoint: 2
|
checkpoint: 2,
|
||||||
};
|
};
|
||||||
// IMPORTANT: the diff call excludes the local change
|
// IMPORTANT: the diff call excludes the local change
|
||||||
// with rev=foo CP=1
|
// with rev=foo CP=1
|
||||||
|
@ -543,7 +541,7 @@ describe('Change', function() {
|
||||||
prev: 'new-prev',
|
prev: 'new-prev',
|
||||||
modelName: this.modelName,
|
modelName: this.modelName,
|
||||||
modelId: 'new-id',
|
modelId: 'new-id',
|
||||||
checkpoint: 2
|
checkpoint: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
Change.diff(this.modelName, 0, [updateRecord], function(err, diff) {
|
Change.diff(this.modelName, 0, [updateRecord], function(err, diff) {
|
||||||
|
|
|
@ -8,7 +8,7 @@ describe('Checkpoint', function() {
|
||||||
describe('bumpLastSeq() and current()', function() {
|
describe('bumpLastSeq() and current()', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
var memory = loopback.createDataSource({
|
var memory = loopback.createDataSource({
|
||||||
connector: loopback.Memory
|
connector: loopback.Memory,
|
||||||
});
|
});
|
||||||
Checkpoint.attachTo(memory);
|
Checkpoint.attachTo(memory);
|
||||||
});
|
});
|
||||||
|
@ -23,14 +23,14 @@ describe('Checkpoint', function() {
|
||||||
expect(seq).to.equal(3);
|
expect(seq).to.equal(3);
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should be no race condition for current() when calling in parallel', function(done) {
|
it('Should be no race condition for current() when calling in parallel', function(done) {
|
||||||
async.parallel([
|
async.parallel([
|
||||||
function(next) { Checkpoint.current(next); },
|
function(next) { Checkpoint.current(next); },
|
||||||
function(next) { Checkpoint.current(next); }
|
function(next) { Checkpoint.current(next); },
|
||||||
], function(err, list) {
|
], function(err, list) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
Checkpoint.find(function(err, data) {
|
Checkpoint.find(function(err, data) {
|
||||||
|
@ -44,7 +44,7 @@ describe('Checkpoint', function() {
|
||||||
it('Should be no race condition for bumpLastSeq() when calling in parallel', function(done) {
|
it('Should be no race condition for bumpLastSeq() when calling in parallel', function(done) {
|
||||||
async.parallel([
|
async.parallel([
|
||||||
function(next) { Checkpoint.bumpLastSeq(next); },
|
function(next) { Checkpoint.bumpLastSeq(next); },
|
||||||
function(next) { Checkpoint.bumpLastSeq(next); }
|
function(next) { Checkpoint.bumpLastSeq(next); },
|
||||||
], function(err, list) {
|
], function(err, list) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
Checkpoint.find(function(err, data) {
|
Checkpoint.find(function(err, data) {
|
||||||
|
@ -57,14 +57,15 @@ describe('Checkpoint', function() {
|
||||||
expect(data[0].seq).to.equal(2);
|
expect(data[0].seq).to.equal(2);
|
||||||
// In this particular case, since the new last seq is always 2, both results
|
// In this particular case, since the new last seq is always 2, both results
|
||||||
// should be 2.
|
// should be 2.
|
||||||
expect(list.map(function(it) {return it.seq;}))
|
expect(list.map(function(it) { return it.seq; }))
|
||||||
.to.eql([2, 2]);
|
.to.eql([2, 2]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Checkpoint.current() for non existing checkpoint should initialize checkpoint', function(done) {
|
it('Checkpoint.current() for non existing checkpoint should initialize checkpoint',
|
||||||
|
function(done) {
|
||||||
Checkpoint.current(function(err, seq) {
|
Checkpoint.current(function(err, seq) {
|
||||||
expect(seq).to.equal(1);
|
expect(seq).to.equal(1);
|
||||||
done(err);
|
done(err);
|
||||||
|
|
|
@ -3,7 +3,7 @@ describe('DataSource', function() {
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
memory = loopback.createDataSource({
|
memory = loopback.createDataSource({
|
||||||
connector: loopback.Memory
|
connector: loopback.Memory,
|
||||||
});
|
});
|
||||||
|
|
||||||
assertValidDataSource(memory);
|
assertValidDataSource(memory);
|
||||||
|
@ -11,7 +11,7 @@ describe('DataSource', function() {
|
||||||
|
|
||||||
describe('dataSource.createModel(name, properties, settings)', function() {
|
describe('dataSource.createModel(name, properties, settings)', function() {
|
||||||
it('Define a model and attach it to a `DataSource`', function() {
|
it('Define a model and attach it to a `DataSource`', function() {
|
||||||
var Color = memory.createModel('color', {name: String});
|
var Color = memory.createModel('color', { name: String });
|
||||||
assert.isFunc(Color, 'find');
|
assert.isFunc(Color, 'find');
|
||||||
assert.isFunc(Color, 'findById');
|
assert.isFunc(Color, 'findById');
|
||||||
assert.isFunc(Color, 'findOne');
|
assert.isFunc(Color, 'findOne');
|
||||||
|
@ -36,13 +36,13 @@ describe('DataSource', function() {
|
||||||
|
|
||||||
it('should honor settings.base', function() {
|
it('should honor settings.base', function() {
|
||||||
var Base = memory.createModel('base');
|
var Base = memory.createModel('base');
|
||||||
var Color = memory.createModel('color', {name: String}, {base: Base});
|
var Color = memory.createModel('color', { name: String }, { base: Base });
|
||||||
assert(Color.prototype instanceof Base);
|
assert(Color.prototype instanceof Base);
|
||||||
assert.equal(Color.base, Base);
|
assert.equal(Color.base, Base);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use loopback.PersistedModel as the base for DBs', function() {
|
it('should use loopback.PersistedModel as the base for DBs', function() {
|
||||||
var Color = memory.createModel('color', {name: String});
|
var Color = memory.createModel('color', { name: String });
|
||||||
assert(Color.prototype instanceof loopback.PersistedModel);
|
assert(Color.prototype instanceof loopback.PersistedModel);
|
||||||
assert.equal(Color.base, loopback.PersistedModel);
|
assert.equal(Color.base, loopback.PersistedModel);
|
||||||
});
|
});
|
||||||
|
@ -56,14 +56,13 @@ describe('DataSource', function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
var ds = loopback.createDataSource({
|
var ds = loopback.createDataSource({
|
||||||
connector: new Connector()
|
connector: new Connector(),
|
||||||
});
|
});
|
||||||
|
|
||||||
var Color = ds.createModel('color', {name: String});
|
var Color = ds.createModel('color', { name: String });
|
||||||
assert(Color.prototype instanceof Color.registry.getModel('Model'));
|
assert(Color.prototype instanceof Color.registry.getModel('Model'));
|
||||||
assert.equal(Color.base.modelName, 'PersistedModel');
|
assert.equal(Color.base.modelName, 'PersistedModel');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe.skip('PersistedModel Methods', function() {
|
describe.skip('PersistedModel Methods', function() {
|
||||||
|
@ -103,7 +102,9 @@ describe('DataSource', function() {
|
||||||
var fn = scope[name];
|
var fn = scope[name];
|
||||||
var actuallyEnabled = Model.getRemoteMethod(name);
|
var actuallyEnabled = Model.getRemoteMethod(name);
|
||||||
assert(fn, name + ' should be defined!');
|
assert(fn, name + ' should be defined!');
|
||||||
assert(actuallyEnabled === isRemoteEnabled, name + ' ' + (isRemoteEnabled ? 'should' : 'should not') + ' be remote enabled');
|
assert(actuallyEnabled === isRemoteEnabled,
|
||||||
|
name + ' ' + (isRemoteEnabled ? 'should' : 'should not') +
|
||||||
|
' be remote enabled');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,14 +9,14 @@ describe('RemoteConnector', function() {
|
||||||
// setup the remote connector
|
// setup the remote connector
|
||||||
var ds = loopback.createDataSource({
|
var ds = loopback.createDataSource({
|
||||||
url: 'http://127.0.0.1:3000/api',
|
url: 'http://127.0.0.1:3000/api',
|
||||||
connector: loopback.Remote
|
connector: loopback.Remote,
|
||||||
});
|
});
|
||||||
TestModel.attachTo(ds);
|
TestModel.attachTo(ds);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to call create', function(done) {
|
it('should be able to call create', function(done) {
|
||||||
TestModel.create({
|
TestModel.create({
|
||||||
foo: 'bar'
|
foo: 'bar',
|
||||||
}, function(err, inst) {
|
}, function(err, inst) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
assert(inst.id);
|
assert(inst.id);
|
||||||
|
@ -26,7 +26,7 @@ describe('RemoteConnector', function() {
|
||||||
|
|
||||||
it('should be able to call save', function(done) {
|
it('should be able to call save', function(done) {
|
||||||
var m = new TestModel({
|
var m = new TestModel({
|
||||||
foo: 'bar'
|
foo: 'bar',
|
||||||
});
|
});
|
||||||
m.save(function(err, data) {
|
m.save(function(err, data) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
|
|
|
@ -3,7 +3,7 @@ var loopback = require('../../');
|
||||||
var models = require('../fixtures/e2e/models');
|
var models = require('../fixtures/e2e/models');
|
||||||
var TestModel = models.TestModel;
|
var TestModel = models.TestModel;
|
||||||
var LocalTestModel = TestModel.extend('LocalTestModel', {}, {
|
var LocalTestModel = TestModel.extend('LocalTestModel', {}, {
|
||||||
trackChanges: true
|
trackChanges: true,
|
||||||
});
|
});
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ describe('Replication', function() {
|
||||||
// setup the remote connector
|
// setup the remote connector
|
||||||
var ds = loopback.createDataSource({
|
var ds = loopback.createDataSource({
|
||||||
url: 'http://127.0.0.1:3000/api',
|
url: 'http://127.0.0.1:3000/api',
|
||||||
connector: loopback.Remote
|
connector: loopback.Remote,
|
||||||
});
|
});
|
||||||
TestModel.attachTo(ds);
|
TestModel.attachTo(ds);
|
||||||
var memory = loopback.memory();
|
var memory = loopback.memory();
|
||||||
|
@ -23,11 +23,11 @@ describe('Replication', function() {
|
||||||
var RANDOM = Math.random();
|
var RANDOM = Math.random();
|
||||||
|
|
||||||
LocalTestModel.create({
|
LocalTestModel.create({
|
||||||
n: RANDOM
|
n: RANDOM,
|
||||||
}, function(err, created) {
|
}, function(err, created) {
|
||||||
LocalTestModel.replicate(0, TestModel, function() {
|
LocalTestModel.replicate(0, TestModel, function() {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
TestModel.findOne({n: RANDOM}, function(err, found) {
|
TestModel.findOne({ n: RANDOM }, function(err, found) {
|
||||||
assert.equal(created.id, found.id);
|
assert.equal(created.id, found.id);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,34 +5,33 @@ var MailConnector = require('../lib/connectors/mail');
|
||||||
|
|
||||||
describe('Email connector', function() {
|
describe('Email connector', function() {
|
||||||
it('should set up SMTP', function() {
|
it('should set up SMTP', function() {
|
||||||
var connector = new MailConnector({transports: [
|
var connector = new MailConnector({ transports: [
|
||||||
{type: 'smtp', service: 'gmail'}
|
{ type: 'smtp', service: 'gmail' },
|
||||||
]});
|
] });
|
||||||
assert(connector.transportForName('smtp'));
|
assert(connector.transportForName('smtp'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set up DIRECT', function() {
|
it('should set up DIRECT', function() {
|
||||||
var connector = new MailConnector({transports: [
|
var connector = new MailConnector({ transports: [
|
||||||
{type: 'direct', name: 'localhost'}
|
{ type: 'direct', name: 'localhost' },
|
||||||
]});
|
] });
|
||||||
assert(connector.transportForName('direct'));
|
assert(connector.transportForName('direct'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set up STUB', function() {
|
it('should set up STUB', function() {
|
||||||
var connector = new MailConnector({transports: [
|
var connector = new MailConnector({ transports: [
|
||||||
{type: 'stub', service: 'gmail'}
|
{ type: 'stub', service: 'gmail' },
|
||||||
]});
|
] });
|
||||||
assert(connector.transportForName('stub'));
|
assert(connector.transportForName('stub'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set up a single transport for SMTP' , function() {
|
it('should set up a single transport for SMTP', function() {
|
||||||
var connector = new MailConnector({transport:
|
var connector = new MailConnector({ transport:
|
||||||
{type: 'smtp', service: 'gmail'}
|
{ type: 'smtp', service: 'gmail' },
|
||||||
});
|
});
|
||||||
|
|
||||||
assert(connector.transportForName('smtp'));
|
assert(connector.transportForName('smtp'));
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Email and SMTP', function() {
|
describe('Email and SMTP', function() {
|
||||||
|
@ -53,7 +52,7 @@ describe('Email and SMTP', function() {
|
||||||
from: 'from@from.com',
|
from: 'from@from.com',
|
||||||
subject: 'subject',
|
subject: 'subject',
|
||||||
text: 'text',
|
text: 'text',
|
||||||
html: '<h1>html</h1>'
|
html: '<h1>html</h1>',
|
||||||
};
|
};
|
||||||
|
|
||||||
MyEmail.send(options, function(err, mail) {
|
MyEmail.send(options, function(err, mail) {
|
||||||
|
@ -71,7 +70,7 @@ describe('Email and SMTP', function() {
|
||||||
from: 'from@from.com',
|
from: 'from@from.com',
|
||||||
subject: 'subject',
|
subject: 'subject',
|
||||||
text: 'text',
|
text: 'text',
|
||||||
html: '<h1>html</h1>'
|
html: '<h1>html</h1>',
|
||||||
});
|
});
|
||||||
|
|
||||||
message.send(function(err, mail) {
|
message.send(function(err, mail) {
|
||||||
|
|
|
@ -4,9 +4,7 @@ var assert = require('assert');
|
||||||
var request = require('supertest');
|
var request = require('supertest');
|
||||||
|
|
||||||
describe('loopback.errorHandler(options)', function() {
|
describe('loopback.errorHandler(options)', function() {
|
||||||
|
|
||||||
it('should return default middleware when options object is not present', function(done) {
|
it('should return default middleware when options object is not present', function(done) {
|
||||||
|
|
||||||
//arrange
|
//arrange
|
||||||
var app = loopback();
|
var app = loopback();
|
||||||
app.use(loopback.urlNotFound());
|
app.use(loopback.urlNotFound());
|
||||||
|
@ -22,7 +20,6 @@ describe('loopback.errorHandler(options)', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should delete stack when options.includeStack is false', function(done) {
|
it('should delete stack when options.includeStack is false', function(done) {
|
||||||
|
|
||||||
//arrange
|
//arrange
|
||||||
var app = loopback();
|
var app = loopback();
|
||||||
app.use(loopback.urlNotFound());
|
app.use(loopback.urlNotFound());
|
||||||
|
@ -47,7 +44,7 @@ describe('loopback.errorHandler(options)', function() {
|
||||||
includeStack: false,
|
includeStack: false,
|
||||||
log: function customLogger(err, str, req) {
|
log: function customLogger(err, str, req) {
|
||||||
errorLogged = err;
|
errorLogged = err;
|
||||||
}
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
//act
|
//act
|
||||||
|
@ -58,6 +55,5 @@ describe('loopback.errorHandler(options)', function() {
|
||||||
.to.have.property('message', 'Cannot GET /url-does-not-exist');
|
.to.have.property('message', 'Cannot GET /url-does-not-exist');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,7 +5,7 @@ var app = module.exports = loopback();
|
||||||
boot(app, __dirname);
|
boot(app, __dirname);
|
||||||
|
|
||||||
var apiPath = '/api';
|
var apiPath = '/api';
|
||||||
app.use(loopback.token({model: app.models.accessToken}));
|
app.use(loopback.token({ model: app.models.accessToken }));
|
||||||
app.use(apiPath, loopback.rest());
|
app.use(apiPath, loopback.rest());
|
||||||
|
|
||||||
app.use(loopback.urlNotFound());
|
app.use(loopback.urlNotFound());
|
||||||
|
|
|
@ -2,5 +2,5 @@ var loopback = require('../../../../index');
|
||||||
var PersistedModel = loopback.PersistedModel;
|
var PersistedModel = loopback.PersistedModel;
|
||||||
|
|
||||||
exports.TestModel = PersistedModel.extend('TestModel', {}, {
|
exports.TestModel = PersistedModel.extend('TestModel', {}, {
|
||||||
trackChanges: true
|
trackChanges: true,
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,7 +3,7 @@ var boot = require('loopback-boot');
|
||||||
var app = module.exports = loopback();
|
var app = module.exports = loopback();
|
||||||
app.enableAuth();
|
app.enableAuth();
|
||||||
boot(app, __dirname);
|
boot(app, __dirname);
|
||||||
app.use(loopback.token({model: app.models.AccessToken}));
|
app.use(loopback.token({ model: app.models.AccessToken }));
|
||||||
var apiPath = '/api';
|
var apiPath = '/api';
|
||||||
app.use(apiPath, loopback.rest());
|
app.use(apiPath, loopback.rest());
|
||||||
app.use(loopback.urlNotFound());
|
app.use(loopback.urlNotFound());
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
describe('GeoPoint', function() {
|
describe('GeoPoint', function() {
|
||||||
describe('geoPoint.distanceTo(geoPoint, options)', function() {
|
describe('geoPoint.distanceTo(geoPoint, options)', function() {
|
||||||
it('Get the distance to another `GeoPoint`', function() {
|
it('Get the distance to another `GeoPoint`', function() {
|
||||||
var here = new GeoPoint({lat: 10, lng: 10});
|
var here = new GeoPoint({ lat: 10, lng: 10 });
|
||||||
var there = new GeoPoint({lat: 5, lng: 5});
|
var there = new GeoPoint({ lat: 5, lng: 5 });
|
||||||
var distance = here.distanceTo(there, {type: 'meters'});
|
var distance = here.distanceTo(there, { type: 'meters' });
|
||||||
|
|
||||||
assert.equal(Math.floor(distance), 782777);
|
assert.equal(Math.floor(distance), 782777);
|
||||||
});
|
});
|
||||||
|
@ -11,9 +11,9 @@ describe('GeoPoint', function() {
|
||||||
|
|
||||||
describe('GeoPoint.distanceBetween(a, b, options)', function() {
|
describe('GeoPoint.distanceBetween(a, b, options)', function() {
|
||||||
it('Get the distance between two points', function() {
|
it('Get the distance between two points', function() {
|
||||||
var here = new GeoPoint({lat: 10, lng: 10});
|
var here = new GeoPoint({ lat: 10, lng: 10 });
|
||||||
var there = new GeoPoint({lat: 5, lng: 5});
|
var there = new GeoPoint({ lat: 5, lng: 5 });
|
||||||
var distance = GeoPoint.distanceBetween(here, there, {type: 'feet'});
|
var distance = GeoPoint.distanceBetween(here, there, { type: 'feet' });
|
||||||
|
|
||||||
assert.equal(Math.floor(distance), 2568169);
|
assert.equal(Math.floor(distance), 2568169);
|
||||||
});
|
});
|
||||||
|
@ -43,11 +43,11 @@ describe('GeoPoint', function() {
|
||||||
});
|
});
|
||||||
it('Create as Model property', function() {
|
it('Create as Model property', function() {
|
||||||
var Model = loopback.createModel('geo-model', {
|
var Model = loopback.createModel('geo-model', {
|
||||||
geo: {type: 'GeoPoint'}
|
geo: { type: 'GeoPoint' },
|
||||||
});
|
});
|
||||||
|
|
||||||
var m = new Model({
|
var m = new Model({
|
||||||
geo: '1.222,3.444'
|
geo: '1.222,3.444',
|
||||||
});
|
});
|
||||||
|
|
||||||
assert(m.geo instanceof GeoPoint);
|
assert(m.geo instanceof GeoPoint);
|
||||||
|
|
|
@ -4,7 +4,7 @@ var _beforeEach = {};
|
||||||
var helpers = {
|
var helpers = {
|
||||||
describe: _describe,
|
describe: _describe,
|
||||||
it: _it,
|
it: _it,
|
||||||
beforeEach: _beforeEach
|
beforeEach: _beforeEach,
|
||||||
};
|
};
|
||||||
module.exports = helpers;
|
module.exports = helpers;
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ _beforeEach.givenAnUnauthenticatedToken = function(attrs, optionalHandler) {
|
||||||
};
|
};
|
||||||
|
|
||||||
_beforeEach.givenAnAnonymousToken = function(attrs, optionalHandler) {
|
_beforeEach.givenAnAnonymousToken = function(attrs, optionalHandler) {
|
||||||
_beforeEach.givenModel('accessToken', {id: '$anonymous'}, optionalHandler);
|
_beforeEach.givenModel('accessToken', { id: '$anonymous' }, optionalHandler);
|
||||||
};
|
};
|
||||||
|
|
||||||
_describe.whenCalledRemotely = function(verb, url, data, cb) {
|
_describe.whenCalledRemotely = function(verb, url, data, cb) {
|
||||||
|
@ -145,7 +145,9 @@ _describe.whenCalledRemotely = function(verb, url, data, cb) {
|
||||||
if (methodForVerb === 'delete') methodForVerb = 'del';
|
if (methodForVerb === 'delete') methodForVerb = 'del';
|
||||||
|
|
||||||
if (this.request === undefined) {
|
if (this.request === undefined) {
|
||||||
throw new Error('App is not specified. Please use lt.beforeEach.withApp to specify the app.');
|
var msg = 'App is not specified. ' +
|
||||||
|
'Please use lt.beforeEach.withApp to specify the app.';
|
||||||
|
throw new Error(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.http = this.request[methodForVerb](this.url);
|
this.http = this.request[methodForVerb](this.url);
|
||||||
|
|
|
@ -5,7 +5,7 @@ describe('hidden properties', function() {
|
||||||
var app = this.app = loopback();
|
var app = this.app = loopback();
|
||||||
var Product = this.Product = loopback.PersistedModel.extend('product',
|
var Product = this.Product = loopback.PersistedModel.extend('product',
|
||||||
{},
|
{},
|
||||||
{hidden: ['secret']}
|
{ hidden: ['secret'] }
|
||||||
);
|
);
|
||||||
Product.attachTo(loopback.memory());
|
Product.attachTo(loopback.memory());
|
||||||
|
|
||||||
|
@ -18,11 +18,11 @@ describe('hidden properties', function() {
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
|
|
||||||
Category.create({
|
Category.create({
|
||||||
name: 'my category'
|
name: 'my category',
|
||||||
}, function(err, category) {
|
}, function(err, category) {
|
||||||
category.products.create({
|
category.products.create({
|
||||||
name: 'pencil',
|
name: 'pencil',
|
||||||
secret: 'a secret'
|
secret: 'a secret',
|
||||||
}, done);
|
}, done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -24,7 +24,7 @@ describe('loopback application', function() {
|
||||||
function setupAppWithStreamingMethod() {
|
function setupAppWithStreamingMethod() {
|
||||||
app.dataSource('db', {
|
app.dataSource('db', {
|
||||||
connector: loopback.Memory,
|
connector: loopback.Memory,
|
||||||
defaultForType: 'db'
|
defaultForType: 'db',
|
||||||
});
|
});
|
||||||
var db = app.datasources.db;
|
var db = app.datasources.db;
|
||||||
|
|
||||||
|
@ -52,9 +52,9 @@ describe('loopback application', function() {
|
||||||
loopback.remoteMethod(Streamer.read, {
|
loopback.remoteMethod(Streamer.read, {
|
||||||
http: { method: 'post' },
|
http: { method: 'post' },
|
||||||
accepts: [
|
accepts: [
|
||||||
{ arg: 'req', type: 'Object', http: { source: 'req' } },
|
{ arg: 'req', type: 'Object', http: { source: 'req' }},
|
||||||
{ arg: 'res', type: 'Object', http: { source: 'res' } }
|
{ arg: 'res', type: 'Object', http: { source: 'res' }},
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
app.enableAuth();
|
app.enableAuth();
|
||||||
|
|
|
@ -26,7 +26,7 @@ module.exports = function(config) {
|
||||||
'test/replication.test.js',
|
'test/replication.test.js',
|
||||||
'test/change.test.js',
|
'test/change.test.js',
|
||||||
'test/checkpoint.test.js',
|
'test/checkpoint.test.js',
|
||||||
'test/app.test.js'
|
'test/app.test.js',
|
||||||
],
|
],
|
||||||
|
|
||||||
// list of files / patterns to exclude
|
// list of files / patterns to exclude
|
||||||
|
@ -52,7 +52,7 @@ module.exports = function(config) {
|
||||||
// - PhantomJS
|
// - PhantomJS
|
||||||
// - IE (only Windows)
|
// - IE (only Windows)
|
||||||
browsers: [
|
browsers: [
|
||||||
'Chrome'
|
'Chrome',
|
||||||
],
|
],
|
||||||
|
|
||||||
// Which plugins to enable
|
// Which plugins to enable
|
||||||
|
@ -61,16 +61,16 @@ module.exports = function(config) {
|
||||||
'karma-mocha',
|
'karma-mocha',
|
||||||
'karma-phantomjs-launcher',
|
'karma-phantomjs-launcher',
|
||||||
'karma-chrome-launcher',
|
'karma-chrome-launcher',
|
||||||
'karma-junit-reporter'
|
'karma-junit-reporter',
|
||||||
],
|
],
|
||||||
|
|
||||||
// If browser does not capture in given timeout [ms], kill it
|
// If browser does not capture in given timeout [ms], kill it
|
||||||
captureTimeout: 60000,
|
captureTimeout: 60000,
|
||||||
|
|
||||||
// to avoid DISCONNECTED messages
|
// to avoid DISCONNECTED messages
|
||||||
browserDisconnectTimeout : 10000, // default 2000
|
browserDisconnectTimeout: 10000, // default 2000
|
||||||
browserDisconnectTolerance : 1, // default 0
|
browserDisconnectTolerance: 1, // default 0
|
||||||
browserNoActivityTimeout : 60000, //default 10000
|
browserNoActivityTimeout: 60000, //default 10000
|
||||||
|
|
||||||
// Continuous Integration mode
|
// Continuous Integration mode
|
||||||
// if true, it capture browsers, run tests and exit
|
// if true, it capture browsers, run tests and exit
|
||||||
|
@ -97,7 +97,7 @@ module.exports = function(config) {
|
||||||
'passport',
|
'passport',
|
||||||
'passport-local',
|
'passport-local',
|
||||||
'superagent',
|
'superagent',
|
||||||
'supertest'
|
'supertest',
|
||||||
],
|
],
|
||||||
// transform: ['coffeeify'],
|
// transform: ['coffeeify'],
|
||||||
debug: true,
|
debug: true,
|
||||||
|
@ -106,6 +106,6 @@ module.exports = function(config) {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Add browserify to preprocessors
|
// Add browserify to preprocessors
|
||||||
preprocessors: {'test/*': ['browserify']}
|
preprocessors: { 'test/*': ['browserify'] },
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -105,7 +105,7 @@ describe('loopback', function() {
|
||||||
'urlNotFound',
|
'urlNotFound',
|
||||||
'urlencoded',
|
'urlencoded',
|
||||||
'version',
|
'version',
|
||||||
'vhost'
|
'vhost',
|
||||||
];
|
];
|
||||||
|
|
||||||
var actual = Object.getOwnPropertyNames(loopback);
|
var actual = Object.getOwnPropertyNames(loopback);
|
||||||
|
@ -135,7 +135,7 @@ describe('loopback', function() {
|
||||||
describe('loopback.createDataSource(options)', 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({
|
var dataSource = loopback.createDataSource({
|
||||||
connector: loopback.Memory
|
connector: loopback.Memory,
|
||||||
});
|
});
|
||||||
assert(dataSource.connector);
|
assert(dataSource.connector);
|
||||||
});
|
});
|
||||||
|
@ -144,7 +144,7 @@ describe('loopback', function() {
|
||||||
describe('data source created by loopback', function() {
|
describe('data source created by loopback', function() {
|
||||||
it('should create model extending Model by default', function() {
|
it('should create model extending Model by default', function() {
|
||||||
var dataSource = loopback.createDataSource({
|
var dataSource = loopback.createDataSource({
|
||||||
connector: loopback.Memory
|
connector: loopback.Memory,
|
||||||
});
|
});
|
||||||
var m1 = dataSource.createModel('m1', {});
|
var m1 = dataSource.createModel('m1', {});
|
||||||
assert(m1.prototype instanceof loopback.Model);
|
assert(m1.prototype instanceof loopback.Model);
|
||||||
|
@ -161,14 +161,14 @@ describe('loopback', function() {
|
||||||
describe('loopback.autoAttach', function() {
|
describe('loopback.autoAttach', function() {
|
||||||
it('doesn\'t overwrite model with datasource configured', function() {
|
it('doesn\'t overwrite model with datasource configured', function() {
|
||||||
var ds1 = loopback.createDataSource('db1', {
|
var ds1 = loopback.createDataSource('db1', {
|
||||||
connector: loopback.Memory
|
connector: loopback.Memory,
|
||||||
});
|
});
|
||||||
|
|
||||||
// setup default data sources
|
// setup default data sources
|
||||||
loopback.setDefaultDataSourceForType('db', ds1);
|
loopback.setDefaultDataSourceForType('db', ds1);
|
||||||
|
|
||||||
var ds2 = loopback.createDataSource('db2', {
|
var ds2 = loopback.createDataSource('db2', {
|
||||||
connector: loopback.Memory
|
connector: loopback.Memory,
|
||||||
});
|
});
|
||||||
|
|
||||||
var model1 = ds2.createModel('m1', {});
|
var model1 = ds2.createModel('m1', {});
|
||||||
|
@ -186,7 +186,7 @@ describe('loopback', function() {
|
||||||
|
|
||||||
describe('loopback.remoteMethod(Model, fn, [options]);', 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});
|
var Product = loopback.createModel('product', { price: Number });
|
||||||
|
|
||||||
Product.stats = function(fn) {
|
Product.stats = function(fn) {
|
||||||
// ...
|
// ...
|
||||||
|
@ -195,8 +195,8 @@ describe('loopback', function() {
|
||||||
loopback.remoteMethod(
|
loopback.remoteMethod(
|
||||||
Product.stats,
|
Product.stats,
|
||||||
{
|
{
|
||||||
returns: {arg: 'stats', type: 'array'},
|
returns: { arg: 'stats', type: 'array' },
|
||||||
http: {path: '/info', verb: 'get'}
|
http: { path: '/info', verb: 'get' },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -213,14 +213,14 @@ describe('loopback', function() {
|
||||||
it('should extend from options.base', function() {
|
it('should extend from options.base', function() {
|
||||||
var MyModel = loopback.createModel('MyModel', {}, {
|
var MyModel = loopback.createModel('MyModel', {}, {
|
||||||
foo: {
|
foo: {
|
||||||
bar: 'bat'
|
bar: 'bat',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
var MyCustomModel = loopback.createModel('MyCustomModel', {}, {
|
var MyCustomModel = loopback.createModel('MyCustomModel', {}, {
|
||||||
base: 'MyModel',
|
base: 'MyModel',
|
||||||
foo: {
|
foo: {
|
||||||
bat: 'baz'
|
bat: 'baz',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
assert(MyCustomModel.super_ === MyModel);
|
assert(MyCustomModel.super_ === MyModel);
|
||||||
assert.deepEqual(MyCustomModel.settings.foo, { bar: 'bat', bat: 'baz' });
|
assert.deepEqual(MyCustomModel.settings.foo, { bar: 'bat', bat: 'baz' });
|
||||||
|
@ -232,14 +232,14 @@ describe('loopback', function() {
|
||||||
it('should be able to get model by name', function() {
|
it('should be able to get model by name', function() {
|
||||||
var MyModel = loopback.createModel('MyModel', {}, {
|
var MyModel = loopback.createModel('MyModel', {}, {
|
||||||
foo: {
|
foo: {
|
||||||
bar: 'bat'
|
bar: 'bat',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
var MyCustomModel = loopback.createModel('MyCustomModel', {}, {
|
var MyCustomModel = loopback.createModel('MyCustomModel', {}, {
|
||||||
base: 'MyModel',
|
base: 'MyModel',
|
||||||
foo: {
|
foo: {
|
||||||
bat: 'baz'
|
bat: 'baz',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
assert(loopback.getModel('MyModel') === MyModel);
|
assert(loopback.getModel('MyModel') === MyModel);
|
||||||
assert(loopback.getModel('MyCustomModel') === MyCustomModel);
|
assert(loopback.getModel('MyCustomModel') === MyCustomModel);
|
||||||
|
@ -249,14 +249,14 @@ describe('loopback', function() {
|
||||||
it('should be able to get model by type', function() {
|
it('should be able to get model by type', function() {
|
||||||
var MyModel = loopback.createModel('MyModel', {}, {
|
var MyModel = loopback.createModel('MyModel', {}, {
|
||||||
foo: {
|
foo: {
|
||||||
bar: 'bat'
|
bar: 'bat',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
var MyCustomModel = loopback.createModel('MyCustomModel', {}, {
|
var MyCustomModel = loopback.createModel('MyCustomModel', {}, {
|
||||||
base: 'MyModel',
|
base: 'MyModel',
|
||||||
foo: {
|
foo: {
|
||||||
bat: 'baz'
|
bat: 'baz',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
assert(loopback.getModelByType(MyModel) === MyCustomModel);
|
assert(loopback.getModelByType(MyModel) === MyCustomModel);
|
||||||
assert(loopback.getModelByType(MyCustomModel) === MyCustomModel);
|
assert(loopback.getModelByType(MyCustomModel) === MyCustomModel);
|
||||||
|
@ -273,13 +273,13 @@ describe('loopback', function() {
|
||||||
methods: {
|
methods: {
|
||||||
staticMethod: {
|
staticMethod: {
|
||||||
isStatic: true,
|
isStatic: true,
|
||||||
http: { path: '/static' }
|
http: { path: '/static' },
|
||||||
},
|
},
|
||||||
instanceMethod: {
|
instanceMethod: {
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
http: { path: '/instance' }
|
http: { path: '/instance' },
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var methodNames = TestModel.sharedClass.methods().map(function(m) {
|
var methodNames = TestModel.sharedClass.methods().map(function(m) {
|
||||||
|
@ -288,7 +288,7 @@ describe('loopback', function() {
|
||||||
|
|
||||||
expect(methodNames).to.include.members([
|
expect(methodNames).to.include.members([
|
||||||
'staticMethod',
|
'staticMethod',
|
||||||
'prototype.instanceMethod'
|
'prototype.instanceMethod',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -296,7 +296,7 @@ describe('loopback', function() {
|
||||||
describe('loopback.createModel(config)', function() {
|
describe('loopback.createModel(config)', function() {
|
||||||
it('creates the model', function() {
|
it('creates the model', function() {
|
||||||
var model = loopback.createModel({
|
var model = loopback.createModel({
|
||||||
name: uniqueModelName
|
name: uniqueModelName,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(model.prototype).to.be.instanceof(loopback.Model);
|
expect(model.prototype).to.be.instanceof(loopback.Model);
|
||||||
|
@ -305,7 +305,7 @@ describe('loopback', function() {
|
||||||
it('interprets extra first-level keys as options', function() {
|
it('interprets extra first-level keys as options', function() {
|
||||||
var model = loopback.createModel({
|
var model = loopback.createModel({
|
||||||
name: uniqueModelName,
|
name: uniqueModelName,
|
||||||
base: 'User'
|
base: 'User',
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(model.prototype).to.be.instanceof(loopback.User);
|
expect(model.prototype).to.be.instanceof(loopback.User);
|
||||||
|
@ -316,8 +316,8 @@ describe('loopback', function() {
|
||||||
name: uniqueModelName,
|
name: uniqueModelName,
|
||||||
base: 'User',
|
base: 'User',
|
||||||
options: {
|
options: {
|
||||||
base: 'Application'
|
base: 'Application',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(model.prototype).to.be.instanceof(loopback.Application);
|
expect(model.prototype).to.be.instanceof(loopback.Application);
|
||||||
|
@ -333,9 +333,9 @@ describe('loopback', function() {
|
||||||
relations: {
|
relations: {
|
||||||
owner: {
|
owner: {
|
||||||
type: 'belongsTo',
|
type: 'belongsTo',
|
||||||
model: 'User'
|
model: 'User',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(model.settings.relations).to.have.property('owner');
|
expect(model.settings.relations).to.have.property('owner');
|
||||||
|
@ -346,23 +346,23 @@ describe('loopback', function() {
|
||||||
relations: {
|
relations: {
|
||||||
owner: {
|
owner: {
|
||||||
type: 'belongsTo',
|
type: 'belongsTo',
|
||||||
model: 'User'
|
model: 'User',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
loopback.configureModel(model, {
|
loopback.configureModel(model, {
|
||||||
dataSource: false,
|
dataSource: false,
|
||||||
relations: {
|
relations: {
|
||||||
owner: {
|
owner: {
|
||||||
model: 'Application'
|
model: 'Application',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(model.settings.relations.owner).to.eql({
|
expect(model.settings.relations.owner).to.eql({
|
||||||
type: 'belongsTo',
|
type: 'belongsTo',
|
||||||
model: 'Application'
|
model: 'Application',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -375,9 +375,9 @@ describe('loopback', function() {
|
||||||
relations: {
|
relations: {
|
||||||
owner: {
|
owner: {
|
||||||
type: 'belongsTo',
|
type: 'belongsTo',
|
||||||
model: 'User'
|
model: 'User',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var owner = model.prototype.owner;
|
var owner = model.prototype.owner;
|
||||||
|
@ -393,9 +393,9 @@ describe('loopback', function() {
|
||||||
accessType: 'EXECUTE',
|
accessType: 'EXECUTE',
|
||||||
principalType: 'ROLE',
|
principalType: 'ROLE',
|
||||||
principalId: '$everyone',
|
principalId: '$everyone',
|
||||||
permission: 'DENY'
|
permission: 'DENY',
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
loopback.configureModel(model, {
|
loopback.configureModel(model, {
|
||||||
|
@ -406,9 +406,9 @@ describe('loopback', function() {
|
||||||
accessType: 'EXECUTE',
|
accessType: 'EXECUTE',
|
||||||
principalType: 'ROLE',
|
principalType: 'ROLE',
|
||||||
principalId: 'admin',
|
principalId: 'admin',
|
||||||
permission: 'ALLOW'
|
permission: 'ALLOW',
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(model.settings.acls).eql([
|
expect(model.settings.acls).eql([
|
||||||
|
@ -417,15 +417,15 @@ describe('loopback', function() {
|
||||||
accessType: 'EXECUTE',
|
accessType: 'EXECUTE',
|
||||||
principalType: 'ROLE',
|
principalType: 'ROLE',
|
||||||
principalId: '$everyone',
|
principalId: '$everyone',
|
||||||
permission: 'DENY'
|
permission: 'DENY',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
property: 'find',
|
property: 'find',
|
||||||
accessType: 'EXECUTE',
|
accessType: 'EXECUTE',
|
||||||
principalType: 'ROLE',
|
principalType: 'ROLE',
|
||||||
principalId: 'admin',
|
principalId: 'admin',
|
||||||
permission: 'ALLOW'
|
permission: 'ALLOW',
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -437,9 +437,9 @@ describe('loopback', function() {
|
||||||
accessType: 'EXECUTE',
|
accessType: 'EXECUTE',
|
||||||
principalType: 'ROLE',
|
principalType: 'ROLE',
|
||||||
principalId: '$everyone',
|
principalId: '$everyone',
|
||||||
permission: 'DENY'
|
permission: 'DENY',
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
loopback.configureModel(model, {
|
loopback.configureModel(model, {
|
||||||
|
@ -450,9 +450,9 @@ describe('loopback', function() {
|
||||||
accessType: 'EXECUTE',
|
accessType: 'EXECUTE',
|
||||||
principalType: 'ROLE',
|
principalType: 'ROLE',
|
||||||
principalId: '$everyone',
|
principalId: '$everyone',
|
||||||
permission: 'ALLOW'
|
permission: 'ALLOW',
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(model.settings.acls).eql([
|
expect(model.settings.acls).eql([
|
||||||
|
@ -461,15 +461,15 @@ describe('loopback', function() {
|
||||||
accessType: 'EXECUTE',
|
accessType: 'EXECUTE',
|
||||||
principalType: 'ROLE',
|
principalType: 'ROLE',
|
||||||
principalId: '$everyone',
|
principalId: '$everyone',
|
||||||
permission: 'ALLOW'
|
permission: 'ALLOW',
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('updates existing settings', function() {
|
it('updates existing settings', function() {
|
||||||
var model = loopback.Model.extend(uniqueModelName, {}, {
|
var model = loopback.Model.extend(uniqueModelName, {}, {
|
||||||
ttl: 10,
|
ttl: 10,
|
||||||
emailVerificationRequired: false
|
emailVerificationRequired: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
var baseName = model.settings.base.name;
|
var baseName = model.settings.base.name;
|
||||||
|
@ -479,8 +479,8 @@ describe('loopback', function() {
|
||||||
options: {
|
options: {
|
||||||
ttl: 20,
|
ttl: 20,
|
||||||
realmRequired: true,
|
realmRequired: true,
|
||||||
base: 'X'
|
base: 'X',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(model.settings).to.have.property('ttl', 20);
|
expect(model.settings).to.have.property('ttl', 20);
|
||||||
|
@ -499,13 +499,13 @@ describe('loopback', function() {
|
||||||
methods: {
|
methods: {
|
||||||
staticMethod: {
|
staticMethod: {
|
||||||
isStatic: true,
|
isStatic: true,
|
||||||
http: { path: '/static' }
|
http: { path: '/static' },
|
||||||
},
|
},
|
||||||
instanceMethod: {
|
instanceMethod: {
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
http: { path: '/instance' }
|
http: { path: '/instance' },
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var methodNames = TestModel.sharedClass.methods().map(function(m) {
|
var methodNames = TestModel.sharedClass.methods().map(function(m) {
|
||||||
|
@ -514,7 +514,7 @@ describe('loopback', function() {
|
||||||
|
|
||||||
expect(methodNames).to.include.members([
|
expect(methodNames).to.include.members([
|
||||||
'staticMethod',
|
'staticMethod',
|
||||||
'prototype.instanceMethod'
|
'prototype.instanceMethod',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -538,7 +538,7 @@ describe('loopback', function() {
|
||||||
'ACL',
|
'ACL',
|
||||||
'Scope',
|
'Scope',
|
||||||
'Change',
|
'Change',
|
||||||
'Checkpoint'
|
'Checkpoint',
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(Object.keys(loopback)).to.include.members(expectedModelNames);
|
expect(Object.keys(loopback)).to.include.members(expectedModelNames);
|
||||||
|
@ -551,8 +551,7 @@ describe('loopback', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe.onServer('loopback.getCurrentContext', function() {
|
describe.onServer('loopback.getCurrentContext', function() {
|
||||||
var runInOtherDomain;
|
var runInOtherDomain, runnerInterval;
|
||||||
var runnerInterval;
|
|
||||||
|
|
||||||
before(function setupRunInOtherDomain() {
|
before(function setupRunInOtherDomain() {
|
||||||
var emitterInOtherDomain = new EventEmitter();
|
var emitterInOtherDomain = new EventEmitter();
|
||||||
|
@ -594,7 +593,7 @@ describe('loopback', function() {
|
||||||
TestModel.remoteMethod('test', {
|
TestModel.remoteMethod('test', {
|
||||||
accepts: { arg: 'inst', type: uniqueModelName },
|
accepts: { arg: 'inst', type: uniqueModelName },
|
||||||
returns: { root: true },
|
returns: { root: true },
|
||||||
http: { path: '/test', verb: 'get' }
|
http: { path: '/test', verb: 'get' },
|
||||||
});
|
});
|
||||||
|
|
||||||
// after remote hook
|
// after remote hook
|
||||||
|
@ -602,7 +601,7 @@ describe('loopback', function() {
|
||||||
var tmpCtx = loopback.getCurrentContext();
|
var tmpCtx = loopback.getCurrentContext();
|
||||||
if (tmpCtx) {
|
if (tmpCtx) {
|
||||||
ctxx.result.data = tmpCtx.get('data');
|
ctxx.result.data = tmpCtx.get('data');
|
||||||
}else {
|
} else {
|
||||||
ctxx.result.data = 'context not available';
|
ctxx.result.data = 'context not available';
|
||||||
}
|
}
|
||||||
next();
|
next();
|
||||||
|
@ -645,9 +644,9 @@ describe('loopback', function() {
|
||||||
dataSource: null,
|
dataSource: null,
|
||||||
methods: {
|
methods: {
|
||||||
staticMethod: {
|
staticMethod: {
|
||||||
http: { path: '/static' }
|
http: { path: '/static' },
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var methodNames = getAllMethodNamesWithoutClassName(TestModel);
|
var methodNames = getAllMethodNamesWithoutClassName(TestModel);
|
||||||
|
@ -661,9 +660,9 @@ describe('loopback', function() {
|
||||||
dataSource: null,
|
dataSource: null,
|
||||||
methods: {
|
methods: {
|
||||||
'prototype.instanceMethod': {
|
'prototype.instanceMethod': {
|
||||||
http: { path: '/instance' }
|
http: { path: '/instance' },
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var methodNames = getAllMethodNamesWithoutClassName(TestModel);
|
var methodNames = getAllMethodNamesWithoutClassName(TestModel);
|
||||||
|
@ -673,15 +672,17 @@ describe('loopback', function() {
|
||||||
|
|
||||||
it('throws an error when "isStatic:true" and method name starts with "prototype."', function() {
|
it('throws an error when "isStatic:true" and method name starts with "prototype."', function() {
|
||||||
var TestModel = loopback.createModel(uniqueModelName);
|
var TestModel = loopback.createModel(uniqueModelName);
|
||||||
expect(function() { loopback.configureModel(TestModel, {
|
expect(function() {
|
||||||
dataSource: null,
|
loopback.configureModel(TestModel, {
|
||||||
methods: {
|
dataSource: null,
|
||||||
'prototype.instanceMethod': {
|
methods: {
|
||||||
isStatic: true,
|
'prototype.instanceMethod': {
|
||||||
http: { path: '/instance' }
|
isStatic: true,
|
||||||
}
|
http: { path: '/instance' },
|
||||||
}
|
},
|
||||||
});}).to.throw(Error, new Error('Remoting metadata for' + TestModel.modelName +
|
},
|
||||||
|
});
|
||||||
|
}).to.throw(Error, new Error('Remoting metadata for' + TestModel.modelName +
|
||||||
' "isStatic" does not match new method name-based style.'));
|
' "isStatic" does not match new method name-based style.'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -692,9 +693,9 @@ describe('loopback', function() {
|
||||||
methods: {
|
methods: {
|
||||||
staticMethod: {
|
staticMethod: {
|
||||||
isStatic: true,
|
isStatic: true,
|
||||||
http: { path: '/static' }
|
http: { path: '/static' },
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var methodNames = getAllMethodNamesWithoutClassName(TestModel);
|
var methodNames = getAllMethodNamesWithoutClassName(TestModel);
|
||||||
|
@ -709,9 +710,9 @@ describe('loopback', function() {
|
||||||
methods: {
|
methods: {
|
||||||
'prototype.instanceMethod': {
|
'prototype.instanceMethod': {
|
||||||
isStatic: false,
|
isStatic: false,
|
||||||
http: { path: '/instance' }
|
http: { path: '/instance' },
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
var methodNames = getAllMethodNamesWithoutClassName(TestModel);
|
var methodNames = getAllMethodNamesWithoutClassName(TestModel);
|
||||||
|
|
|
@ -7,22 +7,22 @@ describe('Memory Connector', function() {
|
||||||
// or create it using the standard
|
// or create it using the standard
|
||||||
// data source creation api
|
// data source creation api
|
||||||
memory = loopback.createDataSource({
|
memory = loopback.createDataSource({
|
||||||
connector: loopback.Memory
|
connector: loopback.Memory,
|
||||||
});
|
});
|
||||||
|
|
||||||
// create a model using the
|
// create a model using the
|
||||||
// memory data source
|
// memory data source
|
||||||
var properties = {
|
var properties = {
|
||||||
name: String,
|
name: String,
|
||||||
price: Number
|
price: Number,
|
||||||
};
|
};
|
||||||
|
|
||||||
var Product = memory.createModel('product', properties);
|
var Product = memory.createModel('product', properties);
|
||||||
|
|
||||||
Product.create([
|
Product.create([
|
||||||
{name: 'apple', price: 0.79},
|
{ name: 'apple', price: 0.79 },
|
||||||
{name: 'pear', price: 1.29},
|
{ name: 'pear', price: 1.29 },
|
||||||
{name: 'orange', price: 0.59},
|
{ name: 'orange', price: 0.59 },
|
||||||
], count);
|
], count);
|
||||||
|
|
||||||
function count() {
|
function count() {
|
||||||
|
|
|
@ -7,7 +7,7 @@ describe('Application', function() {
|
||||||
|
|
||||||
it('honors `application.register` - promise variant', function(done) {
|
it('honors `application.register` - promise variant', function(done) {
|
||||||
Application.register('rfeng', 'MyTestApp',
|
Application.register('rfeng', 'MyTestApp',
|
||||||
{description: 'My test application'}, function(err, result) {
|
{ description: 'My test application' }, function(err, result) {
|
||||||
var app = result;
|
var app = result;
|
||||||
assert.equal(app.owner, 'rfeng');
|
assert.equal(app.owner, 'rfeng');
|
||||||
assert.equal(app.name, 'MyTestApp');
|
assert.equal(app.name, 'MyTestApp');
|
||||||
|
@ -18,7 +18,7 @@ describe('Application', function() {
|
||||||
|
|
||||||
it('honors `application.register` - promise variant', function(done) {
|
it('honors `application.register` - promise variant', function(done) {
|
||||||
Application.register('rfeng', 'MyTestApp',
|
Application.register('rfeng', 'MyTestApp',
|
||||||
{description: 'My test application'})
|
{ description: 'My test application' })
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
var app = result;
|
var app = result;
|
||||||
assert.equal(app.owner, 'rfeng');
|
assert.equal(app.owner, 'rfeng');
|
||||||
|
@ -32,9 +32,9 @@ describe('Application', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Create a new application', function(done) {
|
it('Create a new application', function(done) {
|
||||||
Application.create({owner: 'rfeng',
|
Application.create({ owner: 'rfeng',
|
||||||
name: 'MyApp1',
|
name: 'MyApp1',
|
||||||
description: 'My first mobile application'}, function(err, result) {
|
description: 'My first mobile application' }, function(err, result) {
|
||||||
var app = result;
|
var app = result;
|
||||||
assert.equal(app.owner, 'rfeng');
|
assert.equal(app.owner, 'rfeng');
|
||||||
assert.equal(app.name, 'MyApp1');
|
assert.equal(app.name, 'MyApp1');
|
||||||
|
@ -52,7 +52,7 @@ describe('Application', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Create a new application with push settings', function(done) {
|
it('Create a new application with push settings', function(done) {
|
||||||
Application.create({owner: 'rfeng',
|
Application.create({ owner: 'rfeng',
|
||||||
name: 'MyAppWithPush',
|
name: 'MyAppWithPush',
|
||||||
description: 'My push mobile application',
|
description: 'My push mobile application',
|
||||||
pushSettings: {
|
pushSettings: {
|
||||||
|
@ -62,18 +62,18 @@ describe('Application', function() {
|
||||||
keyData: 'key',
|
keyData: 'key',
|
||||||
pushOptions: {
|
pushOptions: {
|
||||||
gateway: 'gateway.sandbox.push.apple.com',
|
gateway: 'gateway.sandbox.push.apple.com',
|
||||||
port: 2195
|
port: 2195,
|
||||||
},
|
},
|
||||||
feedbackOptions: {
|
feedbackOptions: {
|
||||||
gateway: 'feedback.sandbox.push.apple.com',
|
gateway: 'feedback.sandbox.push.apple.com',
|
||||||
port: 2196,
|
port: 2196,
|
||||||
interval: 300,
|
interval: 300,
|
||||||
batchFeedback: true
|
batchFeedback: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
gcm: {
|
gcm: {
|
||||||
serverApiKey: 'serverKey'
|
serverApiKey: 'serverKey',
|
||||||
}
|
},
|
||||||
}},
|
}},
|
||||||
function(err, result) {
|
function(err, result) {
|
||||||
var app = result;
|
var app = result;
|
||||||
|
@ -84,18 +84,18 @@ describe('Application', function() {
|
||||||
keyData: 'key',
|
keyData: 'key',
|
||||||
pushOptions: {
|
pushOptions: {
|
||||||
gateway: 'gateway.sandbox.push.apple.com',
|
gateway: 'gateway.sandbox.push.apple.com',
|
||||||
port: 2195
|
port: 2195,
|
||||||
},
|
},
|
||||||
feedbackOptions: {
|
feedbackOptions: {
|
||||||
gateway: 'feedback.sandbox.push.apple.com',
|
gateway: 'feedback.sandbox.push.apple.com',
|
||||||
port: 2196,
|
port: 2196,
|
||||||
interval: 300,
|
interval: 300,
|
||||||
batchFeedback: true
|
batchFeedback: true,
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
gcm: {
|
gcm: {
|
||||||
serverApiKey: 'serverKey'
|
serverApiKey: 'serverKey',
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
done(err, result);
|
done(err, result);
|
||||||
});
|
});
|
||||||
|
@ -103,7 +103,7 @@ describe('Application', function() {
|
||||||
|
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
Application.register('rfeng', 'MyApp2',
|
Application.register('rfeng', 'MyApp2',
|
||||||
{description: 'My second mobile application'}, function(err, result) {
|
{ description: 'My second mobile application' }, function(err, result) {
|
||||||
var app = result;
|
var app = result;
|
||||||
assert.equal(app.owner, 'rfeng');
|
assert.equal(app.owner, 'rfeng');
|
||||||
assert.equal(app.name, 'MyApp2');
|
assert.equal(app.name, 'MyApp2');
|
||||||
|
@ -211,10 +211,10 @@ describe('Application', function() {
|
||||||
function(done) {
|
function(done) {
|
||||||
Application.authenticate(registeredApp.id, registeredApp.clientKey)
|
Application.authenticate(registeredApp.id, registeredApp.clientKey)
|
||||||
.then(function(result) {
|
.then(function(result) {
|
||||||
assert.equal(result.application.id, registeredApp.id);
|
assert.equal(result.application.id, registeredApp.id);
|
||||||
assert.equal(result.keyType, 'clientKey');
|
assert.equal(result.keyType, 'clientKey');
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch(function(err) {
|
.catch(function(err) {
|
||||||
done(err);
|
done(err);
|
||||||
});
|
});
|
||||||
|
@ -280,10 +280,10 @@ describe('Application', function() {
|
||||||
describe('Application subclass', function() {
|
describe('Application subclass', function() {
|
||||||
it('should use subclass model name', function(done) {
|
it('should use subclass model name', function(done) {
|
||||||
var MyApp = Application.extend('MyApp');
|
var MyApp = Application.extend('MyApp');
|
||||||
var ds = loopback.createDataSource({connector: loopback.Memory});
|
var ds = loopback.createDataSource({ connector: loopback.Memory });
|
||||||
MyApp.attachTo(ds);
|
MyApp.attachTo(ds);
|
||||||
MyApp.register('rfeng', 'MyApp123',
|
MyApp.register('rfeng', 'MyApp123',
|
||||||
{description: 'My 123 mobile application'}, function(err, result) {
|
{ description: 'My 123 mobile application' }, function(err, result) {
|
||||||
var app = result;
|
var app = result;
|
||||||
assert.equal(app.owner, 'rfeng');
|
assert.equal(app.owner, 'rfeng');
|
||||||
assert.equal(app.name, 'MyApp123');
|
assert.equal(app.name, 'MyApp123');
|
||||||
|
|
|
@ -10,8 +10,8 @@ var describe = require('./util/describe');
|
||||||
describe('Model / PersistedModel', function() {
|
describe('Model / PersistedModel', function() {
|
||||||
defineModelTestsWithDataSource({
|
defineModelTestsWithDataSource({
|
||||||
dataSource: {
|
dataSource: {
|
||||||
connector: loopback.Memory
|
connector: loopback.Memory,
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Model.validatesUniquenessOf(property, options)', function() {
|
describe('Model.validatesUniquenessOf(property, options)', function() {
|
||||||
|
@ -23,19 +23,19 @@ describe('Model / PersistedModel', function() {
|
||||||
'password': String,
|
'password': String,
|
||||||
'gender': String,
|
'gender': String,
|
||||||
'domain': String,
|
'domain': String,
|
||||||
'email': String
|
'email': String,
|
||||||
});
|
});
|
||||||
|
|
||||||
var dataSource = loopback.createDataSource({
|
var dataSource = loopback.createDataSource({
|
||||||
connector: loopback.Memory
|
connector: loopback.Memory,
|
||||||
});
|
});
|
||||||
|
|
||||||
User.attachTo(dataSource);
|
User.attachTo(dataSource);
|
||||||
|
|
||||||
User.validatesUniquenessOf('email', {message: 'email is not unique'});
|
User.validatesUniquenessOf('email', { message: 'email is not unique' });
|
||||||
|
|
||||||
var joe = new User({email: 'joe@joe.com'});
|
var joe = new User({ email: 'joe@joe.com' });
|
||||||
var joe2 = new User({email: 'joe@joe.com'});
|
var joe2 = new User({ email: 'joe@joe.com' });
|
||||||
|
|
||||||
joe.save(function() {
|
joe.save(function() {
|
||||||
joe2.save(function(err) {
|
joe2.save(function(err) {
|
||||||
|
@ -50,25 +50,23 @@ describe('Model / PersistedModel', function() {
|
||||||
|
|
||||||
describe('Model.attachTo(dataSource)', function() {
|
describe('Model.attachTo(dataSource)', function() {
|
||||||
it('Attach a model to a [DataSource](#data-source)', function() {
|
it('Attach a model to a [DataSource](#data-source)', function() {
|
||||||
var MyModel = loopback.createModel('my-model', {name: String});
|
var MyModel = loopback.createModel('my-model', { name: String });
|
||||||
var dataSource = loopback.createDataSource({
|
var dataSource = loopback.createDataSource({
|
||||||
connector: loopback.Memory
|
connector: loopback.Memory,
|
||||||
});
|
});
|
||||||
|
|
||||||
MyModel.attachTo(dataSource);
|
MyModel.attachTo(dataSource);
|
||||||
|
|
||||||
MyModel.find(function(err, results) {
|
MyModel.find(function(err, results) {
|
||||||
assert(results.length === 0, 'should have data access methods after attaching to a data source');
|
assert(results.length === 0,
|
||||||
|
'should have data access methods after attaching to a data source');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe.onServer('Remote Methods', function() {
|
describe.onServer('Remote Methods', function() {
|
||||||
|
var User, Post, dataSource, app;
|
||||||
var User, Post;
|
|
||||||
var dataSource;
|
|
||||||
var app;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
User = PersistedModel.extend('user', {
|
User = PersistedModel.extend('user', {
|
||||||
|
@ -79,21 +77,21 @@ describe.onServer('Remote Methods', function() {
|
||||||
'password': String,
|
'password': String,
|
||||||
'gender': String,
|
'gender': String,
|
||||||
'domain': String,
|
'domain': String,
|
||||||
'email': String
|
'email': String,
|
||||||
}, {
|
}, {
|
||||||
trackChanges: true
|
trackChanges: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
Post = PersistedModel.extend('post', {
|
Post = PersistedModel.extend('post', {
|
||||||
id: { id: true, type: String, defaultFn: 'guid' },
|
id: { id: true, type: String, defaultFn: 'guid' },
|
||||||
title: String,
|
title: String,
|
||||||
content: String
|
content: String,
|
||||||
}, {
|
}, {
|
||||||
trackChanges: true
|
trackChanges: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
dataSource = loopback.createDataSource({
|
dataSource = loopback.createDataSource({
|
||||||
connector: loopback.Memory
|
connector: loopback.Memory,
|
||||||
});
|
});
|
||||||
|
|
||||||
User.attachTo(dataSource);
|
User.attachTo(dataSource);
|
||||||
|
@ -113,11 +111,11 @@ describe.onServer('Remote Methods', function() {
|
||||||
User.login,
|
User.login,
|
||||||
{
|
{
|
||||||
accepts: [
|
accepts: [
|
||||||
{arg: 'username', type: 'string', required: true},
|
{ arg: 'username', type: 'string', required: true },
|
||||||
{arg: 'password', type: 'string', required: true}
|
{ arg: 'password', type: 'string', required: true },
|
||||||
],
|
],
|
||||||
returns: {arg: 'sessionId', type: 'any', root: true},
|
returns: { arg: 'sessionId', type: 'any', root: true },
|
||||||
http: {path: '/sign-in', verb: 'get'}
|
http: { path: '/sign-in', verb: 'get' },
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -129,11 +127,11 @@ describe.onServer('Remote Methods', function() {
|
||||||
describe('Model.destroyAll(callback)', function() {
|
describe('Model.destroyAll(callback)', function() {
|
||||||
it('Delete all Model instances from data source', function(done) {
|
it('Delete all Model instances from data source', function(done) {
|
||||||
(new TaskEmitter())
|
(new TaskEmitter())
|
||||||
.task(User, 'create', {first: 'jill'})
|
.task(User, 'create', { first: 'jill' })
|
||||||
.task(User, 'create', {first: 'bob'})
|
.task(User, 'create', { first: 'bob' })
|
||||||
.task(User, 'create', {first: 'jan'})
|
.task(User, 'create', { first: 'jan' })
|
||||||
.task(User, 'create', {first: 'sam'})
|
.task(User, 'create', { first: 'sam' })
|
||||||
.task(User, 'create', {first: 'suzy'})
|
.task(User, 'create', { first: 'suzy' })
|
||||||
.on('done', function() {
|
.on('done', function() {
|
||||||
User.count(function(err, count) {
|
User.count(function(err, count) {
|
||||||
User.destroyAll(function() {
|
User.destroyAll(function() {
|
||||||
|
@ -178,7 +176,7 @@ describe.onServer('Remote Methods', function() {
|
||||||
it('Call the findById with filter.fields using HTTP / REST', function(done) {
|
it('Call the findById with filter.fields using HTTP / REST', function(done) {
|
||||||
request(app)
|
request(app)
|
||||||
.post('/users')
|
.post('/users')
|
||||||
.send({first: 'x', last: 'y'})
|
.send({ first: 'x', last: 'y' })
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
|
@ -201,7 +199,7 @@ describe.onServer('Remote Methods', function() {
|
||||||
it('Call the findById with filter.include using HTTP / REST', function(done) {
|
it('Call the findById with filter.include using HTTP / REST', function(done) {
|
||||||
request(app)
|
request(app)
|
||||||
.post('/users')
|
.post('/users')
|
||||||
.send({first: 'x', last: 'y'})
|
.send({ first: 'x', last: 'y' })
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
|
@ -210,7 +208,7 @@ describe.onServer('Remote Methods', function() {
|
||||||
assert(userId);
|
assert(userId);
|
||||||
request(app)
|
request(app)
|
||||||
.post('/users/' + userId + '/posts')
|
.post('/users/' + userId + '/posts')
|
||||||
.send({title: 'T1', content: 'C1'})
|
.send({ title: 'T1', content: 'C1' })
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
|
@ -230,7 +228,6 @@ describe.onServer('Remote Methods', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Model.beforeRemote(name, fn)', function() {
|
describe('Model.beforeRemote(name, fn)', function() {
|
||||||
|
@ -245,7 +242,7 @@ describe.onServer('Remote Methods', function() {
|
||||||
// invoke save
|
// invoke save
|
||||||
request(app)
|
request(app)
|
||||||
.post('/users')
|
.post('/users')
|
||||||
.send({data: {first: 'foo', last: 'bar'}})
|
.send({ data: { first: 'foo', last: 'bar' }})
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
|
@ -275,7 +272,7 @@ describe.onServer('Remote Methods', function() {
|
||||||
// invoke save
|
// invoke save
|
||||||
request(app)
|
request(app)
|
||||||
.post('/users')
|
.post('/users')
|
||||||
.send({data: {first: 'foo', last: 'bar'}})
|
.send({ data: { first: 'foo', last: 'bar' }})
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
|
@ -324,7 +321,7 @@ describe.onServer('Remote Methods', function() {
|
||||||
// invoke save
|
// invoke save
|
||||||
request(app)
|
request(app)
|
||||||
.post('/users')
|
.post('/users')
|
||||||
.send({data: {first: 'foo', last: 'bar'}})
|
.send({ data: { first: 'foo', last: 'bar' }})
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
|
@ -353,7 +350,7 @@ describe.onServer('Remote Methods', function() {
|
||||||
// invoke save
|
// invoke save
|
||||||
request(app)
|
request(app)
|
||||||
.post('/users')
|
.post('/users')
|
||||||
.send({data: {first: 'foo', last: 'bar'}})
|
.send({ data: { first: 'foo', last: 'bar' }})
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
|
@ -367,20 +364,20 @@ describe.onServer('Remote Methods', function() {
|
||||||
|
|
||||||
describe('Model.hasMany(Model)', function() {
|
describe('Model.hasMany(Model)', function() {
|
||||||
it('Define a one to many relationship', function(done) {
|
it('Define a one to many relationship', function(done) {
|
||||||
var Book = dataSource.createModel('book', {title: String, author: String});
|
var Book = dataSource.createModel('book', { title: String, author: String });
|
||||||
var Chapter = dataSource.createModel('chapter', {title: String});
|
var Chapter = dataSource.createModel('chapter', { title: String });
|
||||||
|
|
||||||
// by referencing model
|
// by referencing model
|
||||||
Book.hasMany(Chapter);
|
Book.hasMany(Chapter);
|
||||||
|
|
||||||
Book.create({title: 'Into the Wild', author: 'Jon Krakauer'}, function(err, book) {
|
Book.create({ title: 'Into the Wild', author: 'Jon Krakauer' }, function(err, book) {
|
||||||
// using 'chapters' scope for build:
|
// using 'chapters' scope for build:
|
||||||
var c = book.chapters.build({title: 'Chapter 1'});
|
var c = book.chapters.build({ title: 'Chapter 1' });
|
||||||
book.chapters.create({title: 'Chapter 2'}, function() {
|
book.chapters.create({ title: 'Chapter 2' }, function() {
|
||||||
c.save(function() {
|
c.save(function() {
|
||||||
Chapter.count({bookId: book.id}, function(err, count) {
|
Chapter.count({ bookId: book.id }, function(err, count) {
|
||||||
assert.equal(count, 2);
|
assert.equal(count, 2);
|
||||||
book.chapters({where: {title: 'Chapter 1'}}, function(err, chapters) {
|
book.chapters({ where: { title: 'Chapter 1' }}, function(err, chapters) {
|
||||||
assert.equal(chapters.length, 1);
|
assert.equal(chapters.length, 1);
|
||||||
assert.equal(chapters[0].title, 'Chapter 1');
|
assert.equal(chapters[0].title, 'Chapter 1');
|
||||||
done();
|
done();
|
||||||
|
@ -396,10 +393,10 @@ describe.onServer('Remote Methods', function() {
|
||||||
it('Normalized properties passed in originally by loopback.createModel()', function() {
|
it('Normalized properties passed in originally by loopback.createModel()', function() {
|
||||||
var props = {
|
var props = {
|
||||||
s: String,
|
s: String,
|
||||||
n: {type: 'Number'},
|
n: { type: 'Number' },
|
||||||
o: {type: 'String', min: 10, max: 100},
|
o: { type: 'String', min: 10, max: 100 },
|
||||||
d: Date,
|
d: Date,
|
||||||
g: loopback.GeoPoint
|
g: loopback.GeoPoint,
|
||||||
};
|
};
|
||||||
|
|
||||||
var MyModel = loopback.createModel('foo', props);
|
var MyModel = loopback.createModel('foo', props);
|
||||||
|
@ -426,7 +423,7 @@ describe.onServer('Remote Methods', function() {
|
||||||
describe('Model.extend()', function() {
|
describe('Model.extend()', function() {
|
||||||
it('Create a new model by extending an existing model', function() {
|
it('Create a new model by extending an existing model', function() {
|
||||||
var User = loopback.PersistedModel.extend('test-user', {
|
var User = loopback.PersistedModel.extend('test-user', {
|
||||||
email: String
|
email: String,
|
||||||
});
|
});
|
||||||
|
|
||||||
User.foo = function() {
|
User.foo = function() {
|
||||||
|
@ -439,7 +436,7 @@ describe.onServer('Remote Methods', function() {
|
||||||
|
|
||||||
var MyUser = User.extend('my-user', {
|
var MyUser = User.extend('my-user', {
|
||||||
a: String,
|
a: String,
|
||||||
b: String
|
b: String,
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(MyUser.prototype.bar, User.prototype.bar);
|
assert.equal(MyUser.prototype.bar, User.prototype.bar);
|
||||||
|
@ -448,7 +445,7 @@ describe.onServer('Remote Methods', function() {
|
||||||
var user = new MyUser({
|
var user = new MyUser({
|
||||||
email: 'foo@bar.com',
|
email: 'foo@bar.com',
|
||||||
a: 'foo',
|
a: 'foo',
|
||||||
b: 'bar'
|
b: 'bar',
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(user.email, 'foo@bar.com');
|
assert.equal(user.email, 'foo@bar.com');
|
||||||
|
@ -461,11 +458,11 @@ describe.onServer('Remote Methods', function() {
|
||||||
it('create isolated emitters for subclasses', function() {
|
it('create isolated emitters for subclasses', function() {
|
||||||
var User1 = loopback.createModel('User1', {
|
var User1 = loopback.createModel('User1', {
|
||||||
'first': String,
|
'first': String,
|
||||||
'last': String
|
'last': String,
|
||||||
});
|
});
|
||||||
|
|
||||||
var User2 = loopback.createModel('User2', {
|
var User2 = loopback.createModel('User2', {
|
||||||
'name': String
|
'name': String,
|
||||||
});
|
});
|
||||||
|
|
||||||
var user1Triggered = false;
|
var user1Triggered = false;
|
||||||
|
@ -486,7 +483,6 @@ describe.onServer('Remote Methods', function() {
|
||||||
assert(user1Triggered);
|
assert(user1Triggered);
|
||||||
assert(!user2Triggered);
|
assert(!user2Triggered);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Model.checkAccessTypeForMethod(remoteMethod)', function() {
|
describe('Model.checkAccessTypeForMethod(remoteMethod)', function() {
|
||||||
|
@ -506,7 +502,7 @@ describe.onServer('Remote Methods', function() {
|
||||||
function shouldReturn(methodName, expectedAccessType) {
|
function shouldReturn(methodName, expectedAccessType) {
|
||||||
describe(methodName, function() {
|
describe(methodName, function() {
|
||||||
it('should return ' + expectedAccessType, function() {
|
it('should return ' + expectedAccessType, function() {
|
||||||
var remoteMethod = {name: methodName};
|
var remoteMethod = { name: methodName };
|
||||||
assert.equal(
|
assert.equal(
|
||||||
User._getAccessTypeForMethod(remoteMethod),
|
User._getAccessTypeForMethod(remoteMethod),
|
||||||
expectedAccessType
|
expectedAccessType
|
||||||
|
@ -538,10 +534,9 @@ describe.onServer('Remote Methods', function() {
|
||||||
var Checkpoint = User.getChangeModel().getCheckpointModel();
|
var Checkpoint = User.getChangeModel().getCheckpointModel();
|
||||||
var tasks = [
|
var tasks = [
|
||||||
getCurrentCheckpoint,
|
getCurrentCheckpoint,
|
||||||
checkpoint
|
checkpoint,
|
||||||
];
|
];
|
||||||
var result;
|
var result, current;
|
||||||
var current;
|
|
||||||
|
|
||||||
async.series(tasks, function(err) {
|
async.series(tasks, function(err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
|
@ -615,7 +610,7 @@ describe.onServer('Remote Methods', function() {
|
||||||
'removeById',
|
'removeById',
|
||||||
'count',
|
'count',
|
||||||
'prototype.updateAttributes',
|
'prototype.updateAttributes',
|
||||||
'createChangeStream'
|
'createChangeStream',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,11 +5,11 @@ describe('Registry', function() {
|
||||||
var appBar = loopback();
|
var appBar = loopback();
|
||||||
var modelName = 'MyModel';
|
var modelName = 'MyModel';
|
||||||
var subModelName = 'Sub' + modelName;
|
var subModelName = 'Sub' + modelName;
|
||||||
var settings = {base: 'PersistedModel'};
|
var settings = { base: 'PersistedModel' };
|
||||||
appFoo.set('perAppRegistries', true);
|
appFoo.set('perAppRegistries', true);
|
||||||
appBar.set('perAppRegistries', true);
|
appBar.set('perAppRegistries', true);
|
||||||
var dsFoo = appFoo.dataSource('dsFoo', {connector: 'memory'});
|
var dsFoo = appFoo.dataSource('dsFoo', { connector: 'memory' });
|
||||||
var dsBar = appFoo.dataSource('dsBar', {connector: 'memory'});
|
var dsBar = appFoo.dataSource('dsBar', { connector: 'memory' });
|
||||||
|
|
||||||
var FooModel = appFoo.model(modelName, settings);
|
var FooModel = appFoo.model(modelName, settings);
|
||||||
var FooSubModel = appFoo.model(subModelName, settings);
|
var FooSubModel = appFoo.model(subModelName, settings);
|
||||||
|
@ -26,9 +26,9 @@ describe('Registry', function() {
|
||||||
|
|
||||||
expect(appFoo.models[modelName]).to.not.equal(appBar.models[modelName]);
|
expect(appFoo.models[modelName]).to.not.equal(appBar.models[modelName]);
|
||||||
|
|
||||||
BarModel.create({name: 'bar'}, function(err, bar) {
|
BarModel.create({ name: 'bar' }, function(err, bar) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
bar.subMyModels.create({parent: 'bar'}, function(err) {
|
bar.subMyModels.create({ parent: 'bar' }, function(err) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
FooSubModel.find(function(err, foos) {
|
FooSubModel.find(function(err, foos) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
/*jshint -W030 */
|
|
||||||
|
|
||||||
var loopback = require('../');
|
var loopback = require('../');
|
||||||
var lt = require('./helpers/loopback-testing-helper');
|
var lt = require('./helpers/loopback-testing-helper');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
@ -11,14 +9,13 @@ var debug = require('debug')('loopback:test:relations.integration');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
|
||||||
describe('relations - integration', function() {
|
describe('relations - integration', function() {
|
||||||
|
|
||||||
lt.beforeEach.withApp(app);
|
lt.beforeEach.withApp(app);
|
||||||
|
|
||||||
lt.beforeEach.givenModel('store');
|
lt.beforeEach.givenModel('store');
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
this.widgetName = 'foo';
|
this.widgetName = 'foo';
|
||||||
this.store.widgets.create({
|
this.store.widgets.create({
|
||||||
name: this.widgetName
|
name: this.widgetName,
|
||||||
}, function() {
|
}, function() {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -28,37 +25,36 @@ describe('relations - integration', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('polymorphicHasMany', function() {
|
describe('polymorphicHasMany', function() {
|
||||||
|
|
||||||
before(function defineProductAndCategoryModels() {
|
before(function defineProductAndCategoryModels() {
|
||||||
var Team = app.model(
|
var Team = app.model(
|
||||||
'Team',
|
'Team',
|
||||||
{ properties: { name: 'string' },
|
{ properties: { name: 'string' },
|
||||||
dataSource: 'db'
|
dataSource: 'db',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
var Reader = app.model(
|
var Reader = app.model(
|
||||||
'Reader',
|
'Reader',
|
||||||
{ properties: { name: 'string' },
|
{ properties: { name: 'string' },
|
||||||
dataSource: 'db'
|
dataSource: 'db',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
var Picture = app.model(
|
var Picture = app.model(
|
||||||
'Picture',
|
'Picture',
|
||||||
{ properties: { name: 'string', imageableId: 'number', imageableType: 'string'},
|
{ properties: { name: 'string', imageableId: 'number', imageableType: 'string' },
|
||||||
dataSource: 'db'
|
dataSource: 'db',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
Reader.hasMany(Picture, { polymorphic: { // alternative syntax
|
Reader.hasMany(Picture, { polymorphic: { // alternative syntax
|
||||||
as: 'imageable', // if not set, default to: reference
|
as: 'imageable', // if not set, default to: reference
|
||||||
foreignKey: 'imageableId', // defaults to 'as + Id'
|
foreignKey: 'imageableId', // defaults to 'as + Id'
|
||||||
discriminator: 'imageableType' // defaults to 'as + Type'
|
discriminator: 'imageableType', // defaults to 'as + Type'
|
||||||
} });
|
}});
|
||||||
|
|
||||||
Picture.belongsTo('imageable', { polymorphic: {
|
Picture.belongsTo('imageable', { polymorphic: {
|
||||||
foreignKey: 'imageableId',
|
foreignKey: 'imageableId',
|
||||||
discriminator: 'imageableType'
|
discriminator: 'imageableType',
|
||||||
} });
|
}});
|
||||||
|
|
||||||
Reader.belongsTo(Team);
|
Reader.belongsTo(Team);
|
||||||
});
|
});
|
||||||
|
@ -89,14 +85,14 @@ describe('relations - integration', function() {
|
||||||
it('includes the related child model', function(done) {
|
it('includes the related child model', function(done) {
|
||||||
var url = '/api/readers/' + this.reader.id;
|
var url = '/api/readers/' + this.reader.id;
|
||||||
this.get(url)
|
this.get(url)
|
||||||
.query({'filter': {'include' : 'pictures'}})
|
.query({ 'filter': { 'include': 'pictures' }})
|
||||||
.expect(200, function(err, res) {
|
.expect(200, function(err, res) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
// console.log(res.body);
|
// console.log(res.body);
|
||||||
expect(res.body.name).to.be.equal('Reader 1');
|
expect(res.body.name).to.be.equal('Reader 1');
|
||||||
expect(res.body.pictures).to.be.eql([
|
expect(res.body.pictures).to.be.eql([
|
||||||
{ name: 'Picture 1', id: 1, imageableId: 1, imageableType: 'Reader'},
|
{ name: 'Picture 1', id: 1, imageableId: 1, imageableType: 'Reader' },
|
||||||
{ name: 'Picture 2', id: 2, imageableId: 1, imageableType: 'Reader'},
|
{ name: 'Picture 2', id: 2, imageableId: 1, imageableType: 'Reader' },
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -105,13 +101,13 @@ describe('relations - integration', function() {
|
||||||
it('includes the related parent model', function(done) {
|
it('includes the related parent model', function(done) {
|
||||||
var url = '/api/pictures';
|
var url = '/api/pictures';
|
||||||
this.get(url)
|
this.get(url)
|
||||||
.query({'filter': {'include' : 'imageable'}})
|
.query({ 'filter': { 'include': 'imageable' }})
|
||||||
.expect(200, function(err, res) {
|
.expect(200, function(err, res) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
// console.log(res.body);
|
// console.log(res.body);
|
||||||
expect(res.body[0].name).to.be.equal('Picture 1');
|
expect(res.body[0].name).to.be.equal('Picture 1');
|
||||||
expect(res.body[1].name).to.be.equal('Picture 2');
|
expect(res.body[1].name).to.be.equal('Picture 2');
|
||||||
expect(res.body[0].imageable).to.be.eql({ name: 'Reader 1', id: 1, teamId: 1});
|
expect(res.body[0].imageable).to.be.eql({ name: 'Reader 1', id: 1, teamId: 1 });
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -119,17 +115,19 @@ describe('relations - integration', function() {
|
||||||
it('includes related models scoped to the related parent model', function(done) {
|
it('includes related models scoped to the related parent model', function(done) {
|
||||||
var url = '/api/pictures';
|
var url = '/api/pictures';
|
||||||
this.get(url)
|
this.get(url)
|
||||||
.query({'filter': {'include' : {'relation': 'imageable', 'scope': { 'include' : 'team'}}}})
|
.query({ 'filter': { 'include': {
|
||||||
|
'relation': 'imageable',
|
||||||
|
'scope': { 'include': 'team' },
|
||||||
|
}}})
|
||||||
.expect(200, function(err, res) {
|
.expect(200, function(err, res) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(res.body[0].name).to.be.equal('Picture 1');
|
expect(res.body[0].name).to.be.equal('Picture 1');
|
||||||
expect(res.body[1].name).to.be.equal('Picture 2');
|
expect(res.body[1].name).to.be.equal('Picture 2');
|
||||||
expect(res.body[0].imageable.name).to.be.eql('Reader 1');
|
expect(res.body[0].imageable.name).to.be.eql('Reader 1');
|
||||||
expect(res.body[0].imageable.team).to.be.eql({ name: 'Team 1', id: 1});
|
expect(res.body[0].imageable.team).to.be.eql({ name: 'Team 1', id: 1 });
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('/store/superStores', function() {
|
describe('/store/superStores', function() {
|
||||||
|
@ -148,7 +146,6 @@ describe('relations - integration', function() {
|
||||||
this.url = '/api/stores/' + this.store.id + '/widgets';
|
this.url = '/api/stores/' + this.store.id + '/widgets';
|
||||||
});
|
});
|
||||||
lt.describe.whenCalledRemotely('GET', '/api/stores/:id/widgets', function() {
|
lt.describe.whenCalledRemotely('GET', '/api/stores/:id/widgets', function() {
|
||||||
|
|
||||||
it('should succeed with statusCode 200', function() {
|
it('should succeed with statusCode 200', function() {
|
||||||
assert.equal(this.res.statusCode, 200);
|
assert.equal(this.res.statusCode, 200);
|
||||||
});
|
});
|
||||||
|
@ -180,7 +177,7 @@ describe('relations - integration', function() {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
this.newWidgetName = 'baz';
|
this.newWidgetName = 'baz';
|
||||||
this.newWidget = {
|
this.newWidget = {
|
||||||
name: this.newWidgetName
|
name: this.newWidgetName,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
|
@ -212,7 +209,7 @@ describe('relations - integration', function() {
|
||||||
});
|
});
|
||||||
it('should have a single widget with storeId', function(done) {
|
it('should have a single widget with storeId', function(done) {
|
||||||
this.app.models.widget.count({
|
this.app.models.widget.count({
|
||||||
storeId: this.store.id
|
storeId: this.store.id,
|
||||||
}, function(err, count) {
|
}, function(err, count) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
assert.equal(count, 2);
|
assert.equal(count, 2);
|
||||||
|
@ -226,7 +223,7 @@ describe('relations - integration', function() {
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.store.widgets.create({
|
this.store.widgets.create({
|
||||||
name: this.widgetName
|
name: this.widgetName,
|
||||||
}, function(err, widget) {
|
}, function(err, widget) {
|
||||||
self.widget = widget;
|
self.widget = widget;
|
||||||
self.url = '/api/stores/' + self.store.id + '/widgets/' + widget.id;
|
self.url = '/api/stores/' + self.store.id + '/widgets/' + widget.id;
|
||||||
|
@ -299,7 +296,7 @@ describe('relations - integration', function() {
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.store.widgets.create({
|
this.store.widgets.create({
|
||||||
name: this.widgetName
|
name: this.widgetName,
|
||||||
}, function(err, widget) {
|
}, function(err, widget) {
|
||||||
self.widget = widget;
|
self.widget = widget;
|
||||||
self.url = '/api/widgets/' + self.widget.id + '/store';
|
self.url = '/api/widgets/' + self.widget.id + '/store';
|
||||||
|
@ -315,7 +312,6 @@ describe('relations - integration', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('hasMany through', function() {
|
describe('hasMany through', function() {
|
||||||
|
|
||||||
function setup(connecting, cb) {
|
function setup(connecting, cb) {
|
||||||
var root = {};
|
var root = {};
|
||||||
|
|
||||||
|
@ -334,7 +330,7 @@ describe('relations - integration', function() {
|
||||||
// Create a physician
|
// Create a physician
|
||||||
function(done) {
|
function(done) {
|
||||||
app.models.physician.create({
|
app.models.physician.create({
|
||||||
name: 'ph1'
|
name: 'ph1',
|
||||||
}, function(err, physician) {
|
}, function(err, physician) {
|
||||||
root.physician = physician;
|
root.physician = physician;
|
||||||
done();
|
done();
|
||||||
|
@ -344,7 +340,7 @@ describe('relations - integration', function() {
|
||||||
// Create a patient
|
// Create a patient
|
||||||
connecting ? function(done) {
|
connecting ? function(done) {
|
||||||
root.physician.patients.create({
|
root.physician.patients.create({
|
||||||
name: 'pa1'
|
name: 'pa1',
|
||||||
}, function(err, patient) {
|
}, function(err, patient) {
|
||||||
root.patient = patient;
|
root.patient = patient;
|
||||||
root.relUrl = '/api/physicians/' + root.physician.id +
|
root.relUrl = '/api/physicians/' + root.physician.id +
|
||||||
|
@ -353,7 +349,7 @@ describe('relations - integration', function() {
|
||||||
});
|
});
|
||||||
} : function(done) {
|
} : function(done) {
|
||||||
app.models.patient.create({
|
app.models.patient.create({
|
||||||
name: 'pa1'
|
name: 'pa1',
|
||||||
}, function(err, patient) {
|
}, function(err, patient) {
|
||||||
root.patient = patient;
|
root.patient = patient;
|
||||||
root.relUrl = '/api/physicians/' + root.physician.id +
|
root.relUrl = '/api/physicians/' + root.physician.id +
|
||||||
|
@ -366,7 +362,6 @@ describe('relations - integration', function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('PUT /physicians/:id/patients/rel/:fk', function() {
|
describe('PUT /physicians/:id/patients/rel/:fk', function() {
|
||||||
|
|
||||||
before(function(done) {
|
before(function(done) {
|
||||||
var self = this;
|
var self = this;
|
||||||
setup(false, function(err, root) {
|
setup(false, function(err, root) {
|
||||||
|
@ -405,7 +400,6 @@ describe('relations - integration', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('PUT /physicians/:id/patients/rel/:fk with data', function() {
|
describe('PUT /physicians/:id/patients/rel/:fk with data', function() {
|
||||||
|
|
||||||
before(function(done) {
|
before(function(done) {
|
||||||
var self = this;
|
var self = this;
|
||||||
setup(false, function(err, root) {
|
setup(false, function(err, root) {
|
||||||
|
@ -450,7 +444,6 @@ describe('relations - integration', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('HEAD /physicians/:id/patients/rel/:fk', function() {
|
describe('HEAD /physicians/:id/patients/rel/:fk', function() {
|
||||||
|
|
||||||
before(function(done) {
|
before(function(done) {
|
||||||
var self = this;
|
var self = this;
|
||||||
setup(true, function(err, root) {
|
setup(true, function(err, root) {
|
||||||
|
@ -469,7 +462,6 @@ describe('relations - integration', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('HEAD /physicians/:id/patients/rel/:fk that does not exist', function() {
|
describe('HEAD /physicians/:id/patients/rel/:fk that does not exist', function() {
|
||||||
|
|
||||||
before(function(done) {
|
before(function(done) {
|
||||||
var self = this;
|
var self = this;
|
||||||
setup(true, function(err, root) {
|
setup(true, function(err, root) {
|
||||||
|
@ -489,7 +481,6 @@ describe('relations - integration', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('DELETE /physicians/:id/patients/rel/:fk', function() {
|
describe('DELETE /physicians/:id/patients/rel/:fk', function() {
|
||||||
|
|
||||||
before(function(done) {
|
before(function(done) {
|
||||||
var self = this;
|
var self = this;
|
||||||
setup(true, function(err, root) {
|
setup(true, function(err, root) {
|
||||||
|
@ -543,7 +534,6 @@ describe('relations - integration', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('GET /physicians/:id/patients/:fk', function() {
|
describe('GET /physicians/:id/patients/:fk', function() {
|
||||||
|
|
||||||
before(function(done) {
|
before(function(done) {
|
||||||
var self = this;
|
var self = this;
|
||||||
setup(true, function(err, root) {
|
setup(true, function(err, root) {
|
||||||
|
@ -564,7 +554,6 @@ describe('relations - integration', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('DELETE /physicians/:id/patients/:fk', function() {
|
describe('DELETE /physicians/:id/patients/:fk', function() {
|
||||||
|
|
||||||
before(function(done) {
|
before(function(done) {
|
||||||
var self = this;
|
var self = this;
|
||||||
setup(true, function(err, root) {
|
setup(true, function(err, root) {
|
||||||
|
@ -605,7 +594,6 @@ describe('relations - integration', function() {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -630,7 +618,7 @@ describe('relations - integration', function() {
|
||||||
beforeEach(function createProductsInCategory(done) {
|
beforeEach(function createProductsInCategory(done) {
|
||||||
var test = this;
|
var test = this;
|
||||||
this.category.products.create({
|
this.category.products.create({
|
||||||
name: 'a-product'
|
name: 'a-product',
|
||||||
}, function(err, product) {
|
}, function(err, product) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
test.product = product;
|
test.product = product;
|
||||||
|
@ -659,8 +647,8 @@ describe('relations - integration', function() {
|
||||||
expect(res.body).to.eql([
|
expect(res.body).to.eql([
|
||||||
{
|
{
|
||||||
id: expectedProduct.id,
|
id: expectedProduct.id,
|
||||||
name: expectedProduct.name
|
name: expectedProduct.name,
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -674,8 +662,8 @@ describe('relations - integration', function() {
|
||||||
expect(res.body).to.eql([
|
expect(res.body).to.eql([
|
||||||
{
|
{
|
||||||
id: expectedProduct.id,
|
id: expectedProduct.id,
|
||||||
name: expectedProduct.name
|
name: expectedProduct.name,
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -693,8 +681,8 @@ describe('relations - integration', function() {
|
||||||
expect(res.body.products).to.eql([
|
expect(res.body.products).to.eql([
|
||||||
{
|
{
|
||||||
id: expectedProduct.id,
|
id: expectedProduct.id,
|
||||||
name: expectedProduct.name
|
name: expectedProduct.name,
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -713,8 +701,8 @@ describe('relations - integration', function() {
|
||||||
expect(res.body.products).to.eql([
|
expect(res.body.products).to.eql([
|
||||||
{
|
{
|
||||||
id: expectedProduct.id,
|
id: expectedProduct.id,
|
||||||
name: expectedProduct.name
|
name: expectedProduct.name,
|
||||||
}
|
},
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -722,13 +710,12 @@ describe('relations - integration', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('embedsOne', function() {
|
describe('embedsOne', function() {
|
||||||
|
|
||||||
before(function defineGroupAndPosterModels() {
|
before(function defineGroupAndPosterModels() {
|
||||||
var group = app.model(
|
var group = app.model(
|
||||||
'group',
|
'group',
|
||||||
{ properties: { name: 'string' },
|
{ properties: { name: 'string' },
|
||||||
dataSource: 'db',
|
dataSource: 'db',
|
||||||
plural: 'groups'
|
plural: 'groups',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
var poster = app.model(
|
var poster = app.model(
|
||||||
|
@ -825,17 +812,15 @@ describe('relations - integration', function() {
|
||||||
var url = '/api/groups/' + this.group.id + '/cover';
|
var url = '/api/groups/' + this.group.id + '/cover';
|
||||||
this.get(url).expect(404, done);
|
this.get(url).expect(404, done);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('embedsMany', function() {
|
describe('embedsMany', function() {
|
||||||
|
|
||||||
before(function defineProductAndCategoryModels() {
|
before(function defineProductAndCategoryModels() {
|
||||||
var todoList = app.model(
|
var todoList = app.model(
|
||||||
'todoList',
|
'todoList',
|
||||||
{ properties: { name: 'string' },
|
{ properties: { name: 'string' },
|
||||||
dataSource: 'db',
|
dataSource: 'db',
|
||||||
plural: 'todo-lists'
|
plural: 'todo-lists',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
var todoItem = app.model(
|
var todoItem = app.model(
|
||||||
|
@ -870,7 +855,7 @@ describe('relations - integration', function() {
|
||||||
expect(res.body.name).to.be.equal('List A');
|
expect(res.body.name).to.be.equal('List A');
|
||||||
expect(res.body.todoItems).to.be.eql([
|
expect(res.body.todoItems).to.be.eql([
|
||||||
{ content: 'Todo 1', id: 1 },
|
{ content: 'Todo 1', id: 1 },
|
||||||
{ content: 'Todo 2', id: 2 }
|
{ content: 'Todo 2', id: 2 },
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -884,7 +869,7 @@ describe('relations - integration', function() {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(res.body).to.be.eql([
|
expect(res.body).to.be.eql([
|
||||||
{ content: 'Todo 1', id: 1 },
|
{ content: 'Todo 1', id: 1 },
|
||||||
{ content: 'Todo 2', id: 2 }
|
{ content: 'Todo 2', id: 2 },
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -898,7 +883,7 @@ describe('relations - integration', function() {
|
||||||
.expect(200, function(err, res) {
|
.expect(200, function(err, res) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(res.body).to.be.eql([
|
expect(res.body).to.be.eql([
|
||||||
{ content: 'Todo 2', id: 2 }
|
{ content: 'Todo 2', id: 2 },
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -926,7 +911,7 @@ describe('relations - integration', function() {
|
||||||
expect(res.body).to.be.eql([
|
expect(res.body).to.be.eql([
|
||||||
{ content: 'Todo 1', id: 1 },
|
{ content: 'Todo 1', id: 1 },
|
||||||
{ content: 'Todo 2', id: 2 },
|
{ content: 'Todo 2', id: 2 },
|
||||||
{ content: 'Todo 3', id: 3 }
|
{ content: 'Todo 3', id: 3 },
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -963,7 +948,7 @@ describe('relations - integration', function() {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(res.body).to.be.eql([
|
expect(res.body).to.be.eql([
|
||||||
{ content: 'Todo 1', id: 1 },
|
{ content: 'Todo 1', id: 1 },
|
||||||
{ content: 'Todo 3', id: 3 }
|
{ content: 'Todo 3', id: 3 },
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -997,11 +982,9 @@ describe('relations - integration', function() {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('referencesMany', function() {
|
describe('referencesMany', function() {
|
||||||
|
|
||||||
before(function defineProductAndCategoryModels() {
|
before(function defineProductAndCategoryModels() {
|
||||||
var recipe = app.model(
|
var recipe = app.model(
|
||||||
'recipe',
|
'recipe',
|
||||||
|
@ -1018,8 +1001,8 @@ describe('relations - integration', function() {
|
||||||
recipe.referencesMany(ingredient);
|
recipe.referencesMany(ingredient);
|
||||||
// contrived example for test:
|
// contrived example for test:
|
||||||
recipe.hasOne(photo, { as: 'picture', options: {
|
recipe.hasOne(photo, { as: 'picture', options: {
|
||||||
http: { path: 'image' }
|
http: { path: 'image' },
|
||||||
} });
|
}});
|
||||||
});
|
});
|
||||||
|
|
||||||
before(function createRecipe(done) {
|
before(function createRecipe(done) {
|
||||||
|
@ -1090,7 +1073,7 @@ describe('relations - integration', function() {
|
||||||
expect(res.body).to.be.eql([
|
expect(res.body).to.be.eql([
|
||||||
{ name: 'Chocolate', id: test.ingredient1 },
|
{ name: 'Chocolate', id: test.ingredient1 },
|
||||||
{ name: 'Sugar', id: test.ingredient2 },
|
{ name: 'Sugar', id: test.ingredient2 },
|
||||||
{ name: 'Butter', id: test.ingredient3 }
|
{ name: 'Butter', id: test.ingredient3 },
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -1105,7 +1088,7 @@ describe('relations - integration', function() {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(res.body).to.be.eql([
|
expect(res.body).to.be.eql([
|
||||||
{ name: 'Chocolate', id: test.ingredient1 },
|
{ name: 'Chocolate', id: test.ingredient1 },
|
||||||
{ name: 'Butter', id: test.ingredient3 }
|
{ name: 'Butter', id: test.ingredient3 },
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -1120,7 +1103,7 @@ describe('relations - integration', function() {
|
||||||
.expect(200, function(err, res) {
|
.expect(200, function(err, res) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(res.body).to.be.eql([
|
expect(res.body).to.be.eql([
|
||||||
{ name: 'Butter', id: test.ingredient3 }
|
{ name: 'Butter', id: test.ingredient3 },
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -1135,11 +1118,11 @@ describe('relations - integration', function() {
|
||||||
.expect(200, function(err, res) {
|
.expect(200, function(err, res) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(res.body.ingredientIds).to.eql([
|
expect(res.body.ingredientIds).to.eql([
|
||||||
test.ingredient1, test.ingredient3
|
test.ingredient1, test.ingredient3,
|
||||||
]);
|
]);
|
||||||
expect(res.body.ingredients).to.eql([
|
expect(res.body.ingredients).to.eql([
|
||||||
{ name: 'Chocolate', id: test.ingredient1 },
|
{ name: 'Chocolate', id: test.ingredient1 },
|
||||||
{ name: 'Butter', id: test.ingredient3 }
|
{ name: 'Butter', id: test.ingredient3 },
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -1195,7 +1178,7 @@ describe('relations - integration', function() {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(res.body).to.be.eql([
|
expect(res.body).to.be.eql([
|
||||||
{ name: 'Chocolate', id: test.ingredient1 },
|
{ name: 'Chocolate', id: test.ingredient1 },
|
||||||
{ name: 'Sugar', id: test.ingredient2 }
|
{ name: 'Sugar', id: test.ingredient2 },
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -1209,7 +1192,7 @@ describe('relations - integration', function() {
|
||||||
.expect(200, function(err, res) {
|
.expect(200, function(err, res) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(res.body).to.be.eql([
|
expect(res.body).to.be.eql([
|
||||||
{ name: 'Chocolate', id: test.ingredient1 }
|
{ name: 'Chocolate', id: test.ingredient1 },
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -1238,7 +1221,7 @@ describe('relations - integration', function() {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(res.body).to.be.eql([
|
expect(res.body).to.be.eql([
|
||||||
{ name: 'Chocolate', id: test.ingredient1 },
|
{ name: 'Chocolate', id: test.ingredient1 },
|
||||||
{ name: 'Sugar', id: test.ingredient2 }
|
{ name: 'Sugar', id: test.ingredient2 },
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -1263,7 +1246,7 @@ describe('relations - integration', function() {
|
||||||
.expect(200, function(err, res) {
|
.expect(200, function(err, res) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(res.body).to.be.eql([
|
expect(res.body).to.be.eql([
|
||||||
{ name: 'Sugar', id: test.ingredient2 }
|
{ name: 'Sugar', id: test.ingredient2 },
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -1278,7 +1261,7 @@ describe('relations - integration', function() {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(res.body).to.be.eql([
|
expect(res.body).to.be.eql([
|
||||||
{ name: 'Chocolate', id: test.ingredient1 },
|
{ name: 'Chocolate', id: test.ingredient1 },
|
||||||
{ name: 'Sugar', id: test.ingredient2 }
|
{ name: 'Sugar', id: test.ingredient2 },
|
||||||
]);
|
]);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -1315,11 +1298,9 @@ describe('relations - integration', function() {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('nested relations', function() {
|
describe('nested relations', function() {
|
||||||
|
|
||||||
before(function defineModels() {
|
before(function defineModels() {
|
||||||
var Book = app.model(
|
var Book = app.model(
|
||||||
'Book',
|
'Book',
|
||||||
|
@ -1357,7 +1338,7 @@ describe('relations - integration', function() {
|
||||||
throw new Error('This should not crash the app');
|
throw new Error('This should not crash the app');
|
||||||
};
|
};
|
||||||
|
|
||||||
Page.remoteMethod('__throw__errors', { isStatic: false, http: { path: '/throws', verb: 'get' } });
|
Page.remoteMethod('__throw__errors', { isStatic: false, http: { path: '/throws', verb: 'get' }});
|
||||||
|
|
||||||
Book.nestRemoting('pages');
|
Book.nestRemoting('pages');
|
||||||
Book.nestRemoting('chapters');
|
Book.nestRemoting('chapters');
|
||||||
|
@ -1375,7 +1356,6 @@ describe('relations - integration', function() {
|
||||||
ctx.res.set('x-after', 'after');
|
ctx.res.set('x-after', 'after');
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
before(function createBook(done) {
|
before(function createBook(done) {
|
||||||
|
@ -1573,7 +1553,7 @@ describe('relations - integration', function() {
|
||||||
var url = '/api/customers/' + cust.id + '/profile';
|
var url = '/api/customers/' + cust.id + '/profile';
|
||||||
|
|
||||||
this.post(url)
|
this.post(url)
|
||||||
.send({points: 10})
|
.send({ points: 10 })
|
||||||
.expect(200, function(err, res) {
|
.expect(200, function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
|
@ -1600,7 +1580,7 @@ describe('relations - integration', function() {
|
||||||
it('should not create the referenced model twice', function(done) {
|
it('should not create the referenced model twice', function(done) {
|
||||||
var url = '/api/customers/' + cust.id + '/profile';
|
var url = '/api/customers/' + cust.id + '/profile';
|
||||||
this.post(url)
|
this.post(url)
|
||||||
.send({points: 20})
|
.send({ points: 20 })
|
||||||
.expect(500, function(err, res) {
|
.expect(500, function(err, res) {
|
||||||
done(err);
|
done(err);
|
||||||
});
|
});
|
||||||
|
@ -1609,7 +1589,7 @@ describe('relations - integration', function() {
|
||||||
it('should update the referenced model', function(done) {
|
it('should update the referenced model', function(done) {
|
||||||
var url = '/api/customers/' + cust.id + '/profile';
|
var url = '/api/customers/' + cust.id + '/profile';
|
||||||
this.put(url)
|
this.put(url)
|
||||||
.send({points: 100})
|
.send({ points: 100 })
|
||||||
.expect(200, function(err, res) {
|
.expect(200, function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
|
@ -1636,5 +1616,4 @@ describe('relations - integration', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,8 +2,7 @@ var loopback = require('../');
|
||||||
var defineModelTestsWithDataSource = require('./util/model-tests');
|
var defineModelTestsWithDataSource = require('./util/model-tests');
|
||||||
|
|
||||||
describe('RemoteConnector', function() {
|
describe('RemoteConnector', function() {
|
||||||
var remoteApp;
|
var remoteApp, remote;
|
||||||
var remote;
|
|
||||||
|
|
||||||
defineModelTestsWithDataSource({
|
defineModelTestsWithDataSource({
|
||||||
beforeEach: function(done) {
|
beforeEach: function(done) {
|
||||||
|
@ -14,7 +13,7 @@ describe('RemoteConnector', function() {
|
||||||
test.dataSource = loopback.createDataSource({
|
test.dataSource = loopback.createDataSource({
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
port: remoteApp.get('port'),
|
port: remoteApp.get('port'),
|
||||||
connector: loopback.Remote
|
connector: loopback.Remote,
|
||||||
});
|
});
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -23,10 +22,10 @@ describe('RemoteConnector', function() {
|
||||||
var RemoteModel = Model.extend('Remote' + Model.modelName, {},
|
var RemoteModel = Model.extend('Remote' + Model.modelName, {},
|
||||||
{ plural: Model.pluralModelName });
|
{ plural: Model.pluralModelName });
|
||||||
RemoteModel.attachTo(loopback.createDataSource({
|
RemoteModel.attachTo(loopback.createDataSource({
|
||||||
connector: loopback.Memory
|
connector: loopback.Memory,
|
||||||
}));
|
}));
|
||||||
remoteApp.model(RemoteModel);
|
remoteApp.model(RemoteModel);
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
|
@ -41,7 +40,7 @@ describe('RemoteConnector', function() {
|
||||||
test.remote = loopback.createDataSource({
|
test.remote = loopback.createDataSource({
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
port: remoteApp.get('port'),
|
port: remoteApp.get('port'),
|
||||||
connector: loopback.Remote
|
connector: loopback.Remote,
|
||||||
});
|
});
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -62,7 +61,7 @@ describe('RemoteConnector', function() {
|
||||||
|
|
||||||
ServerModel.setupRemoting();
|
ServerModel.setupRemoting();
|
||||||
|
|
||||||
var m = new RemoteModel({foo: 'bar'});
|
var m = new RemoteModel({ foo: 'bar' });
|
||||||
m.save(function(err, inst) {
|
m.save(function(err, inst) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
assert(inst instanceof RemoteModel);
|
assert(inst instanceof RemoteModel);
|
||||||
|
|
|
@ -7,7 +7,7 @@ describe('remoting coercion', function() {
|
||||||
var app = loopback();
|
var app = loopback();
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
|
|
||||||
var TestModel = app.model('TestModel', {base: 'Model', dataSource: null, public: true});
|
var TestModel = app.model('TestModel', { base: 'Model', dataSource: null, public: true });
|
||||||
TestModel.test = function(inst, cb) {
|
TestModel.test = function(inst, cb) {
|
||||||
called = true;
|
called = true;
|
||||||
assert(inst instanceof TestModel);
|
assert(inst instanceof TestModel);
|
||||||
|
@ -15,15 +15,15 @@ describe('remoting coercion', function() {
|
||||||
cb();
|
cb();
|
||||||
};
|
};
|
||||||
TestModel.remoteMethod('test', {
|
TestModel.remoteMethod('test', {
|
||||||
accepts: {arg: 'inst', type: 'TestModel', http: {source: 'body'}},
|
accepts: { arg: 'inst', type: 'TestModel', http: { source: 'body' }},
|
||||||
http: {path: '/test', verb: 'post'}
|
http: { path: '/test', verb: 'post' },
|
||||||
});
|
});
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.post('/TestModels/test')
|
.post('/TestModels/test')
|
||||||
.set('Content-Type', 'application/json')
|
.set('Content-Type', 'application/json')
|
||||||
.send({
|
.send({
|
||||||
foo: 'bar'
|
foo: 'bar',
|
||||||
})
|
})
|
||||||
.end(function(err) {
|
.end(function(err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
|
|
|
@ -6,7 +6,6 @@ var app = require(path.join(SIMPLE_APP, 'server/server.js'));
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
|
|
||||||
describe('remoting - integration', function() {
|
describe('remoting - integration', function() {
|
||||||
|
|
||||||
lt.beforeEach.withApp(app);
|
lt.beforeEach.withApp(app);
|
||||||
lt.beforeEach.givenModel('store');
|
lt.beforeEach.givenModel('store');
|
||||||
|
|
||||||
|
@ -17,8 +16,8 @@ describe('remoting - integration', function() {
|
||||||
describe('app.remotes.options', function() {
|
describe('app.remotes.options', function() {
|
||||||
it('should load remoting options', function() {
|
it('should load remoting options', function() {
|
||||||
var remotes = app.remotes();
|
var remotes = app.remotes();
|
||||||
assert.deepEqual(remotes.options, {'json': {'limit': '1kb', 'strict': false},
|
assert.deepEqual(remotes.options, { 'json': { 'limit': '1kb', 'strict': false },
|
||||||
'urlencoded': {'limit': '8kb', 'extended': true}});
|
'urlencoded': { 'limit': '8kb', 'extended': true }});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rest handler', function() {
|
it('rest handler', function() {
|
||||||
|
@ -34,7 +33,7 @@ describe('remoting - integration', function() {
|
||||||
}
|
}
|
||||||
this.http = this.post('/api/stores');
|
this.http = this.post('/api/stores');
|
||||||
this.http.send({
|
this.http.send({
|
||||||
'name': name
|
'name': name,
|
||||||
});
|
});
|
||||||
this.http.end(function(err) {
|
this.http.end(function(err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
|
@ -53,7 +52,7 @@ describe('remoting - integration', function() {
|
||||||
}
|
}
|
||||||
this.http = this.post('/api/stores');
|
this.http = this.post('/api/stores');
|
||||||
this.http.send({
|
this.http.send({
|
||||||
'name': name
|
'name': name,
|
||||||
});
|
});
|
||||||
this.http.end(function(err) {
|
this.http.end(function(err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
|
@ -88,7 +87,7 @@ describe('remoting - integration', function() {
|
||||||
' ',
|
' ',
|
||||||
m.getHttpMethod(),
|
m.getHttpMethod(),
|
||||||
' ',
|
' ',
|
||||||
m.getFullPath()
|
m.getFullPath(),
|
||||||
].join('');
|
].join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +120,7 @@ describe('remoting - integration', function() {
|
||||||
'deleteById(id:any):object DELETE /stores/:id',
|
'deleteById(id:any):object DELETE /stores/:id',
|
||||||
'count(where:object):number GET /stores/count',
|
'count(where:object):number GET /stores/count',
|
||||||
'prototype.updateAttributes(data:object):store PUT /stores/:id',
|
'prototype.updateAttributes(data:object):store PUT /stores/:id',
|
||||||
'createChangeStream(options:object):ReadableStream POST /stores/change-stream'
|
'createChangeStream(options:object):ReadableStream POST /stores/change-stream',
|
||||||
];
|
];
|
||||||
|
|
||||||
// The list of methods is from docs:
|
// The list of methods is from docs:
|
||||||
|
@ -143,7 +142,7 @@ describe('remoting - integration', function() {
|
||||||
'__get__superStores(filter:object):store GET /stores/superStores',
|
'__get__superStores(filter:object):store GET /stores/superStores',
|
||||||
'__create__superStores(data:store):store POST /stores/superStores',
|
'__create__superStores(data:store):store POST /stores/superStores',
|
||||||
'__delete__superStores() DELETE /stores/superStores',
|
'__delete__superStores() DELETE /stores/superStores',
|
||||||
'__count__superStores(where:object):number GET /stores/superStores/count'
|
'__count__superStores(where:object):number GET /stores/superStores/count',
|
||||||
];
|
];
|
||||||
|
|
||||||
expect(methods).to.include.members(expectedMethods);
|
expect(methods).to.include.members(expectedMethods);
|
||||||
|
@ -151,7 +150,6 @@ describe('remoting - integration', function() {
|
||||||
|
|
||||||
it('should have correct signatures for belongsTo methods',
|
it('should have correct signatures for belongsTo methods',
|
||||||
function() {
|
function() {
|
||||||
|
|
||||||
var widgetClass = findClass('widget');
|
var widgetClass = findClass('widget');
|
||||||
var methods = widgetClass.methods
|
var methods = widgetClass.methods
|
||||||
.filter(function(m) {
|
.filter(function(m) {
|
||||||
|
@ -162,15 +160,14 @@ describe('remoting - integration', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
var expectedMethods = [
|
var expectedMethods = [
|
||||||
'prototype.__get__store(refresh:boolean):store ' +
|
'prototype.__get__store(refresh:boolean):store ' +
|
||||||
'GET /widgets/:id/store'
|
'GET /widgets/:id/store',
|
||||||
];
|
];
|
||||||
expect(methods).to.include.members(expectedMethods);
|
expect(methods).to.include.members(expectedMethods);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have correct signatures for hasMany methods',
|
it('should have correct signatures for hasMany methods',
|
||||||
function() {
|
function() {
|
||||||
|
|
||||||
var physicianClass = findClass('store');
|
var physicianClass = findClass('store');
|
||||||
var methods = physicianClass.methods
|
var methods = physicianClass.methods
|
||||||
.filter(function(m) {
|
.filter(function(m) {
|
||||||
|
@ -181,29 +178,28 @@ describe('remoting - integration', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
var expectedMethods = [
|
var expectedMethods = [
|
||||||
'prototype.__findById__widgets(fk:any):widget ' +
|
'prototype.__findById__widgets(fk:any):widget ' +
|
||||||
'GET /stores/:id/widgets/:fk',
|
'GET /stores/:id/widgets/:fk',
|
||||||
'prototype.__destroyById__widgets(fk:any) ' +
|
'prototype.__destroyById__widgets(fk:any) ' +
|
||||||
'DELETE /stores/:id/widgets/:fk',
|
'DELETE /stores/:id/widgets/:fk',
|
||||||
'prototype.__updateById__widgets(fk:any,data:widget):widget ' +
|
'prototype.__updateById__widgets(fk:any,data:widget):widget ' +
|
||||||
'PUT /stores/:id/widgets/:fk',
|
'PUT /stores/:id/widgets/:fk',
|
||||||
'prototype.__get__widgets(filter:object):widget ' +
|
'prototype.__get__widgets(filter:object):widget ' +
|
||||||
'GET /stores/:id/widgets',
|
'GET /stores/:id/widgets',
|
||||||
'prototype.__create__widgets(data:widget):widget ' +
|
'prototype.__create__widgets(data:widget):widget ' +
|
||||||
'POST /stores/:id/widgets',
|
'POST /stores/:id/widgets',
|
||||||
'prototype.__delete__widgets() ' +
|
'prototype.__delete__widgets() ' +
|
||||||
'DELETE /stores/:id/widgets',
|
'DELETE /stores/:id/widgets',
|
||||||
'prototype.__count__widgets(where:object):number ' +
|
'prototype.__count__widgets(where:object):number ' +
|
||||||
'GET /stores/:id/widgets/count'
|
'GET /stores/:id/widgets/count',
|
||||||
];
|
];
|
||||||
expect(methods).to.include.members(expectedMethods);
|
expect(methods).to.include.members(expectedMethods);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should have correct signatures for hasMany-through methods',
|
it('should have correct signatures for hasMany-through methods',
|
||||||
function() { // jscs:disable validateIndentation
|
function() { // jscs:disable validateIndentation
|
||||||
|
var physicianClass = findClass('physician');
|
||||||
var physicianClass = findClass('physician');
|
var methods = physicianClass.methods
|
||||||
var methods = physicianClass.methods
|
|
||||||
.filter(function(m) {
|
.filter(function(m) {
|
||||||
return m.name.indexOf('prototype.__') === 0;
|
return m.name.indexOf('prototype.__') === 0;
|
||||||
})
|
})
|
||||||
|
@ -211,7 +207,7 @@ describe('remoting - integration', function() {
|
||||||
return formatMethod(m);
|
return formatMethod(m);
|
||||||
});
|
});
|
||||||
|
|
||||||
var expectedMethods = [
|
var expectedMethods = [
|
||||||
'prototype.__findById__patients(fk:any):patient ' +
|
'prototype.__findById__patients(fk:any):patient ' +
|
||||||
'GET /physicians/:id/patients/:fk',
|
'GET /physicians/:id/patients/:fk',
|
||||||
'prototype.__destroyById__patients(fk:any) ' +
|
'prototype.__destroyById__patients(fk:any) ' +
|
||||||
|
@ -231,10 +227,9 @@ describe('remoting - integration', function() {
|
||||||
'prototype.__delete__patients() ' +
|
'prototype.__delete__patients() ' +
|
||||||
'DELETE /physicians/:id/patients',
|
'DELETE /physicians/:id/patients',
|
||||||
'prototype.__count__patients(where:object):number ' +
|
'prototype.__count__patients(where:object):number ' +
|
||||||
'GET /physicians/:id/patients/count'
|
'GET /physicians/:id/patients/count',
|
||||||
];
|
];
|
||||||
expect(methods).to.include.members(expectedMethods);
|
expect(methods).to.include.members(expectedMethods);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -10,10 +10,12 @@ describe('Replication over REST', function() {
|
||||||
var PETER = { id: 'p', username: 'peter', email: 'p@t.io', password: 'p' };
|
var PETER = { id: 'p', username: 'peter', email: 'p@t.io', password: 'p' };
|
||||||
var EMERY = { id: 'e', username: 'emery', email: 'e@t.io', password: 'p' };
|
var EMERY = { id: 'e', username: 'emery', email: 'e@t.io', password: 'p' };
|
||||||
|
|
||||||
|
/* eslint-disable one-var */
|
||||||
var serverApp, serverUrl, ServerUser, ServerCar, serverCars;
|
var serverApp, serverUrl, ServerUser, ServerCar, serverCars;
|
||||||
var aliceId, peterId, aliceToken, peterToken, emeryToken, request;
|
var aliceId, peterId, aliceToken, peterToken, emeryToken, request;
|
||||||
var clientApp, LocalUser, LocalCar, RemoteUser, RemoteCar, clientCars;
|
var clientApp, LocalUser, LocalCar, RemoteUser, RemoteCar, clientCars;
|
||||||
var conflictedCarId;
|
var conflictedCarId;
|
||||||
|
/* eslint-enable one-var */
|
||||||
|
|
||||||
before(setupServer);
|
before(setupServer);
|
||||||
before(setupClient);
|
before(setupClient);
|
||||||
|
@ -322,7 +324,7 @@ describe('Replication over REST', function() {
|
||||||
.to.have.property('fullname', 'Alice Smith');
|
.to.have.property('fullname', 'Alice Smith');
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -347,7 +349,7 @@ describe('Replication over REST', function() {
|
||||||
.to.not.have.property('fullname');
|
.to.not.have.property('fullname');
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -367,7 +369,7 @@ describe('Replication over REST', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
var USER_PROPS = {
|
var USER_PROPS = {
|
||||||
id: { type: 'string', id: true }
|
id: { type: 'string', id: true },
|
||||||
};
|
};
|
||||||
|
|
||||||
var USER_OPTS = {
|
var USER_OPTS = {
|
||||||
|
@ -375,13 +377,13 @@ describe('Replication over REST', function() {
|
||||||
plural: 'Users', // use the same REST path in all models
|
plural: 'Users', // use the same REST path in all models
|
||||||
trackChanges: true,
|
trackChanges: true,
|
||||||
strict: 'throw',
|
strict: 'throw',
|
||||||
persistUndefinedAsNull: true
|
persistUndefinedAsNull: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
var CAR_PROPS = {
|
var CAR_PROPS = {
|
||||||
id: { type: 'string', id: true, defaultFn: 'guid' },
|
id: { type: 'string', id: true, defaultFn: 'guid' },
|
||||||
model: { type: 'string', required: true },
|
model: { type: 'string', required: true },
|
||||||
maker: { type: 'string' }
|
maker: { type: 'string' },
|
||||||
};
|
};
|
||||||
|
|
||||||
var CAR_OPTS = {
|
var CAR_OPTS = {
|
||||||
|
@ -395,30 +397,30 @@ describe('Replication over REST', function() {
|
||||||
{
|
{
|
||||||
principalType: 'ROLE',
|
principalType: 'ROLE',
|
||||||
principalId: '$everyone',
|
principalId: '$everyone',
|
||||||
permission: 'DENY'
|
permission: 'DENY',
|
||||||
},
|
},
|
||||||
// allow all authenticated users to read data
|
// allow all authenticated users to read data
|
||||||
{
|
{
|
||||||
principalType: 'ROLE',
|
principalType: 'ROLE',
|
||||||
principalId: '$authenticated',
|
principalId: '$authenticated',
|
||||||
permission: 'ALLOW',
|
permission: 'ALLOW',
|
||||||
accessType: 'READ'
|
accessType: 'READ',
|
||||||
},
|
},
|
||||||
// allow Alice to pull changes
|
// allow Alice to pull changes
|
||||||
{
|
{
|
||||||
principalType: 'USER',
|
principalType: 'USER',
|
||||||
principalId: ALICE.id,
|
principalId: ALICE.id,
|
||||||
permission: 'ALLOW',
|
permission: 'ALLOW',
|
||||||
accessType: 'REPLICATE'
|
accessType: 'REPLICATE',
|
||||||
},
|
},
|
||||||
// allow Peter to write data
|
// allow Peter to write data
|
||||||
{
|
{
|
||||||
principalType: 'USER',
|
principalType: 'USER',
|
||||||
principalId: PETER.id,
|
principalId: PETER.id,
|
||||||
permission: 'ALLOW',
|
permission: 'ALLOW',
|
||||||
accessType: 'WRITE'
|
accessType: 'WRITE',
|
||||||
}
|
},
|
||||||
]
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
function setupServer(done) {
|
function setupServer(done) {
|
||||||
|
@ -435,9 +437,9 @@ describe('Replication over REST', function() {
|
||||||
user: {
|
user: {
|
||||||
type: 'belongsTo',
|
type: 'belongsTo',
|
||||||
model: 'ServerUser',
|
model: 'ServerUser',
|
||||||
foreignKey: 'userId'
|
foreignKey: 'userId',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
serverApp.model(ServerToken, { dataSource: 'db', public: false });
|
serverApp.model(ServerToken, { dataSource: 'db', public: false });
|
||||||
serverApp.model(loopback.ACL, { dataSource: 'db', public: false });
|
serverApp.model(loopback.ACL, { dataSource: 'db', public: false });
|
||||||
|
@ -448,7 +450,7 @@ describe('Replication over REST', function() {
|
||||||
serverApp.model(ServerUser, {
|
serverApp.model(ServerUser, {
|
||||||
dataSource: 'db',
|
dataSource: 'db',
|
||||||
public: true,
|
public: true,
|
||||||
relations: { accessTokens: { model: 'ServerToken' } }
|
relations: { accessTokens: { model: 'ServerToken' }},
|
||||||
});
|
});
|
||||||
|
|
||||||
ServerCar = loopback.createModel('ServerCar', CAR_PROPS, CAR_OPTS);
|
ServerCar = loopback.createModel('ServerCar', CAR_PROPS, CAR_OPTS);
|
||||||
|
@ -476,7 +478,7 @@ describe('Replication over REST', function() {
|
||||||
clientApp.dataSource('db', { connector: 'memory' });
|
clientApp.dataSource('db', { connector: 'memory' });
|
||||||
clientApp.dataSource('remote', {
|
clientApp.dataSource('remote', {
|
||||||
connector: 'remote',
|
connector: 'remote',
|
||||||
url: serverUrl
|
url: serverUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
// NOTE(bajtos) At the moment, all models share the same Checkpoint
|
// NOTE(bajtos) At the moment, all models share the same Checkpoint
|
||||||
|
@ -510,7 +512,7 @@ describe('Replication over REST', function() {
|
||||||
trackChanges: false,
|
trackChanges: false,
|
||||||
// Enable remote replication in order to get remoting API metadata
|
// Enable remote replication in order to get remoting API metadata
|
||||||
// used by the remoting connector
|
// used by the remoting connector
|
||||||
enableRemoteReplication: true
|
enableRemoteReplication: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -548,14 +550,14 @@ describe('Replication over REST', function() {
|
||||||
ServerCar.create(
|
ServerCar.create(
|
||||||
[
|
[
|
||||||
{ id: 'Ford-Mustang', maker: 'Ford', model: 'Mustang' },
|
{ id: 'Ford-Mustang', maker: 'Ford', model: 'Mustang' },
|
||||||
{ id: 'Audi-R8', maker: 'Audi', model: 'R8' }
|
{ id: 'Audi-R8', maker: 'Audi', model: 'R8' },
|
||||||
],
|
],
|
||||||
function(err, cars) {
|
function(err, cars) {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
serverCars = cars.map(carToString);
|
serverCars = cars.map(carToString);
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
], done);
|
], done);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -600,7 +602,7 @@ describe('Replication over REST', function() {
|
||||||
function setAccessToken(token) {
|
function setAccessToken(token) {
|
||||||
clientApp.dataSources.remote.connector.remotes.auth = {
|
clientApp.dataSources.remote.connector.remotes.auth = {
|
||||||
bearer: new Buffer(token).toString('base64'),
|
bearer: new Buffer(token).toString('base64'),
|
||||||
sendImmediately: true
|
sendImmediately: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,7 @@ var expect = require('chai').expect;
|
||||||
var debug = require('debug')('test');
|
var debug = require('debug')('test');
|
||||||
|
|
||||||
describe('Replication / Change APIs', function() {
|
describe('Replication / Change APIs', function() {
|
||||||
var dataSource, SourceModel, TargetModel;
|
var dataSource, SourceModel, TargetModel, useSinceFilter;
|
||||||
var useSinceFilter;
|
|
||||||
var tid = 0; // per-test unique id used e.g. to build unique model names
|
var tid = 0; // per-test unique id used e.g. to build unique model names
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
|
@ -17,18 +16,18 @@ describe('Replication / Change APIs', function() {
|
||||||
useSinceFilter = false;
|
useSinceFilter = false;
|
||||||
var test = this;
|
var test = this;
|
||||||
dataSource = this.dataSource = loopback.createDataSource({
|
dataSource = this.dataSource = loopback.createDataSource({
|
||||||
connector: loopback.Memory
|
connector: loopback.Memory,
|
||||||
});
|
});
|
||||||
SourceModel = this.SourceModel = PersistedModel.extend(
|
SourceModel = this.SourceModel = PersistedModel.extend(
|
||||||
'SourceModel-' + tid,
|
'SourceModel-' + tid,
|
||||||
{ id: { id: true, type: String, defaultFn: 'guid' } },
|
{ id: { id: true, type: String, defaultFn: 'guid' }},
|
||||||
{ trackChanges: true });
|
{ trackChanges: true });
|
||||||
|
|
||||||
SourceModel.attachTo(dataSource);
|
SourceModel.attachTo(dataSource);
|
||||||
|
|
||||||
TargetModel = this.TargetModel = PersistedModel.extend(
|
TargetModel = this.TargetModel = PersistedModel.extend(
|
||||||
'TargetModel-' + tid,
|
'TargetModel-' + tid,
|
||||||
{ id: { id: true, type: String, defaultFn: 'guid' } },
|
{ id: { id: true, type: String, defaultFn: 'guid' }},
|
||||||
{ trackChanges: true });
|
{ trackChanges: true });
|
||||||
|
|
||||||
// NOTE(bajtos) At the moment, all models share the same Checkpoint
|
// NOTE(bajtos) At the moment, all models share the same Checkpoint
|
||||||
|
@ -44,7 +43,7 @@ describe('Replication / Change APIs', function() {
|
||||||
test.startingCheckpoint = -1;
|
test.startingCheckpoint = -1;
|
||||||
|
|
||||||
this.createInitalData = function(cb) {
|
this.createInitalData = function(cb) {
|
||||||
SourceModel.create({name: 'foo'}, function(err, inst) {
|
SourceModel.create({ name: 'foo' }, function(err, inst) {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
test.model = inst;
|
test.model = inst;
|
||||||
SourceModel.replicate(TargetModel, cb);
|
SourceModel.replicate(TargetModel, cb);
|
||||||
|
@ -54,7 +53,7 @@ describe('Replication / Change APIs', function() {
|
||||||
|
|
||||||
describe('optimization check rectifyChange Vs rectifyAllChanges', function() {
|
describe('optimization check rectifyChange Vs rectifyAllChanges', function() {
|
||||||
beforeEach(function initialData(done) {
|
beforeEach(function initialData(done) {
|
||||||
var data = [{name: 'John', surname: 'Doe'}, {name: 'Jane', surname: 'Roe'}];
|
var data = [{ name: 'John', surname: 'Doe' }, { name: 'Jane', surname: 'Roe' }];
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function(callback) {
|
function(callback) {
|
||||||
SourceModel.create(data, callback);
|
SourceModel.create(data, callback);
|
||||||
|
@ -68,7 +67,7 @@ describe('Replication / Change APIs', function() {
|
||||||
|
|
||||||
it('should call rectifyAllChanges if no id is passed for rectifyOnDelete', function(done) {
|
it('should call rectifyAllChanges if no id is passed for rectifyOnDelete', function(done) {
|
||||||
var calls = mockSourceModelRectify();
|
var calls = mockSourceModelRectify();
|
||||||
SourceModel.destroyAll({name: 'John'}, function(err, data) {
|
SourceModel.destroyAll({ name: 'John' }, function(err, data) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(calls).to.eql(['rectifyAllChanges']);
|
expect(calls).to.eql(['rectifyAllChanges']);
|
||||||
done();
|
done();
|
||||||
|
@ -77,24 +76,25 @@ describe('Replication / Change APIs', function() {
|
||||||
|
|
||||||
it('should call rectifyAllChanges if no id is passed for rectifyOnSave', function(done) {
|
it('should call rectifyAllChanges if no id is passed for rectifyOnSave', function(done) {
|
||||||
var calls = mockSourceModelRectify();
|
var calls = mockSourceModelRectify();
|
||||||
var newData = {'name': 'Janie'};
|
var newData = { 'name': 'Janie' };
|
||||||
SourceModel.update({name: 'Jane'}, newData, function(err, data) {
|
SourceModel.update({ name: 'Jane' }, newData, function(err, data) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(calls).to.eql(['rectifyAllChanges']);
|
expect(calls).to.eql(['rectifyAllChanges']);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rectifyOnDelete for Delete should call rectifyChange instead of rectifyAllChanges', function(done) {
|
it('rectifyOnDelete for Delete should call rectifyChange instead of rectifyAllChanges',
|
||||||
|
function(done) {
|
||||||
var calls = mockTargetModelRectify();
|
var calls = mockTargetModelRectify();
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function(callback) {
|
function(callback) {
|
||||||
SourceModel.destroyAll({name: 'John'}, callback);
|
SourceModel.destroyAll({ name: 'John' }, callback);
|
||||||
},
|
},
|
||||||
function(data, callback) {
|
function(data, callback) {
|
||||||
SourceModel.replicate(TargetModel, callback);
|
SourceModel.replicate(TargetModel, callback);
|
||||||
// replicate should call `rectifyOnSave` and then `rectifyChange` not `rectifyAllChanges` through `after save` operation
|
// replicate should call `rectifyOnSave` and then `rectifyChange` not `rectifyAllChanges` through `after save` operation
|
||||||
}
|
},
|
||||||
], function(err, results) {
|
], function(err, results) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(calls).to.eql(['rectifyChange']);
|
expect(calls).to.eql(['rectifyChange']);
|
||||||
|
@ -102,17 +102,18 @@ describe('Replication / Change APIs', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rectifyOnSave for Update should call rectifyChange instead of rectifyAllChanges', function(done) {
|
it('rectifyOnSave for Update should call rectifyChange instead of rectifyAllChanges',
|
||||||
|
function(done) {
|
||||||
var calls = mockTargetModelRectify();
|
var calls = mockTargetModelRectify();
|
||||||
var newData = {'name': 'Janie'};
|
var newData = { 'name': 'Janie' };
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function(callback) {
|
function(callback) {
|
||||||
SourceModel.update({name: 'Jane'}, newData, callback);
|
SourceModel.update({ name: 'Jane' }, newData, callback);
|
||||||
},
|
},
|
||||||
function(data, callback) {
|
function(data, callback) {
|
||||||
SourceModel.replicate(TargetModel, callback);
|
SourceModel.replicate(TargetModel, callback);
|
||||||
// replicate should call `rectifyOnSave` and then `rectifyChange` not `rectifyAllChanges` through `after save` operation
|
// replicate should call `rectifyOnSave` and then `rectifyChange` not `rectifyAllChanges` through `after save` operation
|
||||||
}
|
},
|
||||||
], function(err, result) {
|
], function(err, result) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(calls).to.eql(['rectifyChange']);
|
expect(calls).to.eql(['rectifyChange']);
|
||||||
|
@ -120,9 +121,10 @@ describe('Replication / Change APIs', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('rectifyOnSave for Create should call rectifyChange instead of rectifyAllChanges', function(done) {
|
it('rectifyOnSave for Create should call rectifyChange instead of rectifyAllChanges',
|
||||||
|
function(done) {
|
||||||
var calls = mockTargetModelRectify();
|
var calls = mockTargetModelRectify();
|
||||||
var newData = [{name: 'Janie', surname: 'Doe'}];
|
var newData = [{ name: 'Janie', surname: 'Doe' }];
|
||||||
async.waterfall([
|
async.waterfall([
|
||||||
function(callback) {
|
function(callback) {
|
||||||
SourceModel.create(newData, callback);
|
SourceModel.create(newData, callback);
|
||||||
|
@ -130,7 +132,7 @@ describe('Replication / Change APIs', function() {
|
||||||
function(data, callback) {
|
function(data, callback) {
|
||||||
SourceModel.replicate(TargetModel, callback);
|
SourceModel.replicate(TargetModel, callback);
|
||||||
// replicate should call `rectifyOnSave` and then `rectifyChange` not `rectifyAllChanges` through `after save` operation
|
// replicate should call `rectifyOnSave` and then `rectifyChange` not `rectifyAllChanges` through `after save` operation
|
||||||
}
|
},
|
||||||
], function(err, result) {
|
], function(err, result) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(calls).to.eql(['rectifyChange']);
|
expect(calls).to.eql(['rectifyChange']);
|
||||||
|
@ -174,7 +176,7 @@ describe('Replication / Change APIs', function() {
|
||||||
describe('Model.changes(since, filter, callback)', function() {
|
describe('Model.changes(since, filter, callback)', function() {
|
||||||
it('Get changes since the given checkpoint', function(done) {
|
it('Get changes since the given checkpoint', function(done) {
|
||||||
var test = this;
|
var test = this;
|
||||||
this.SourceModel.create({name: 'foo'}, function(err) {
|
this.SourceModel.create({ name: 'foo' }, function(err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
test.SourceModel.changes(test.startingCheckpoint, {}, function(err, changes) {
|
test.SourceModel.changes(test.startingCheckpoint, {}, function(err, changes) {
|
||||||
|
@ -192,7 +194,6 @@ describe('Replication / Change APIs', function() {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
SourceModel.changes(FUTURE_CHECKPOINT, {}, function(err, changes) {
|
SourceModel.changes(FUTURE_CHECKPOINT, {}, function(err, changes) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
/*jshint -W030 */
|
|
||||||
expect(changes).to.be.empty;
|
expect(changes).to.be.empty;
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -201,11 +202,9 @@ describe('Replication / Change APIs', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Model.replicate(since, targetModel, options, callback)', function() {
|
describe('Model.replicate(since, targetModel, options, callback)', function() {
|
||||||
|
|
||||||
function assertTargetModelEqualsSourceModel(conflicts, sourceModel,
|
function assertTargetModelEqualsSourceModel(conflicts, sourceModel,
|
||||||
targetModel, done) {
|
targetModel, done) {
|
||||||
var sourceData;
|
var sourceData, targetData;
|
||||||
var targetData;
|
|
||||||
|
|
||||||
assert(conflicts.length === 0);
|
assert(conflicts.length === 0);
|
||||||
async.parallel([
|
async.parallel([
|
||||||
|
@ -222,7 +221,7 @@ describe('Replication / Change APIs', function() {
|
||||||
targetData = result;
|
targetData = result;
|
||||||
cb();
|
cb();
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
], function(err) {
|
], function(err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
|
|
||||||
|
@ -235,7 +234,7 @@ describe('Replication / Change APIs', function() {
|
||||||
var test = this;
|
var test = this;
|
||||||
var options = {};
|
var options = {};
|
||||||
|
|
||||||
this.SourceModel.create({name: 'foo'}, function(err) {
|
this.SourceModel.create({ name: 'foo' }, function(err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
test.SourceModel.replicate(test.startingCheckpoint, test.TargetModel,
|
test.SourceModel.replicate(test.startingCheckpoint, test.TargetModel,
|
||||||
options, function(err, conflicts) {
|
options, function(err, conflicts) {
|
||||||
|
@ -251,7 +250,7 @@ describe('Replication / Change APIs', function() {
|
||||||
var test = this;
|
var test = this;
|
||||||
var options = {};
|
var options = {};
|
||||||
|
|
||||||
this.SourceModel.create({name: 'foo'}, function(err) {
|
this.SourceModel.create({ name: 'foo' }, function(err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
test.SourceModel.replicate(test.startingCheckpoint, test.TargetModel,
|
test.SourceModel.replicate(test.startingCheckpoint, test.TargetModel,
|
||||||
options)
|
options)
|
||||||
|
@ -289,7 +288,7 @@ describe('Replication / Change APIs', function() {
|
||||||
expect(getIds(list)).to.eql(['2']);
|
expect(getIds(list)).to.eql(['2']);
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -321,7 +320,7 @@ describe('Replication / Change APIs', function() {
|
||||||
expect(getIds(list)).to.eql(['2']);
|
expect(getIds(list)).to.eql(['2']);
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -433,7 +432,7 @@ describe('Replication / Change APIs', function() {
|
||||||
expect(getIds(list), 'target ids').to.eql(['init', 'racer']);
|
expect(getIds(list), 'target ids').to.eql(['init', 'racer']);
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -453,11 +452,11 @@ describe('Replication / Change APIs', function() {
|
||||||
expect(conflicts, 'conflicts').to.eql([]);
|
expect(conflicts, 'conflicts').to.eql([]);
|
||||||
expect(newCheckpoints, 'currentCheckpoints').to.eql({
|
expect(newCheckpoints, 'currentCheckpoints').to.eql({
|
||||||
source: sourceCp + 1,
|
source: sourceCp + 1,
|
||||||
target: targetCp + 1
|
target: targetCp + 1,
|
||||||
});
|
});
|
||||||
cb();
|
cb();
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
], done);
|
], done);
|
||||||
|
|
||||||
function bumpSourceCheckpoint(cb) {
|
function bumpSourceCheckpoint(cb) {
|
||||||
|
@ -487,14 +486,14 @@ describe('Replication / Change APIs', function() {
|
||||||
TargetModel.currentCheckpoint(function(err, cp) {
|
TargetModel.currentCheckpoint(function(err, cp) {
|
||||||
if (err) return next(err);
|
if (err) return next(err);
|
||||||
TargetModel.getChangeModel().find(
|
TargetModel.getChangeModel().find(
|
||||||
{ where: { checkpoint: { gte: cp } } },
|
{ where: { checkpoint: { gte: cp }}},
|
||||||
function(err, changes) {
|
function(err, changes) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(changes).to.have.length(0);
|
expect(changes).to.have.length(0);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -513,14 +512,14 @@ describe('Replication / Change APIs', function() {
|
||||||
connector.updateAttributes(
|
connector.updateAttributes(
|
||||||
TargetModel.modelName,
|
TargetModel.modelName,
|
||||||
'1',
|
'1',
|
||||||
{name: '3rd-party'},
|
{ name: '3rd-party' },
|
||||||
cb);
|
cb);
|
||||||
} else {
|
} else {
|
||||||
// 2.x connectors require `options`
|
// 2.x connectors require `options`
|
||||||
connector.updateAttributes(
|
connector.updateAttributes(
|
||||||
TargetModel.modelName,
|
TargetModel.modelName,
|
||||||
'1',
|
'1',
|
||||||
{name: '3rd-party'},
|
{ name: '3rd-party' },
|
||||||
{}, // options
|
{}, // options
|
||||||
cb);
|
cb);
|
||||||
}
|
}
|
||||||
|
@ -540,7 +539,7 @@ describe('Replication / Change APIs', function() {
|
||||||
},
|
},
|
||||||
|
|
||||||
replicateExpectingSuccess(),
|
replicateExpectingSuccess(),
|
||||||
verifyInstanceWasReplicated(SourceModel, TargetModel, '1')
|
verifyInstanceWasReplicated(SourceModel, TargetModel, '1'),
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -555,13 +554,13 @@ describe('Replication / Change APIs', function() {
|
||||||
if (connector.create.length <= 3) {
|
if (connector.create.length <= 3) {
|
||||||
connector.create(
|
connector.create(
|
||||||
TargetModel.modelName,
|
TargetModel.modelName,
|
||||||
{id: '1', name: '3rd-party'},
|
{ id: '1', name: '3rd-party' },
|
||||||
cb);
|
cb);
|
||||||
} else {
|
} else {
|
||||||
// 2.x connectors require `options`
|
// 2.x connectors require `options`
|
||||||
connector.create(
|
connector.create(
|
||||||
TargetModel.modelName,
|
TargetModel.modelName,
|
||||||
{id: '1', name: '3rd-party'},
|
{ id: '1', name: '3rd-party' },
|
||||||
{}, // options
|
{}, // options
|
||||||
cb);
|
cb);
|
||||||
}
|
}
|
||||||
|
@ -581,7 +580,7 @@ describe('Replication / Change APIs', function() {
|
||||||
},
|
},
|
||||||
|
|
||||||
replicateExpectingSuccess(),
|
replicateExpectingSuccess(),
|
||||||
verifyInstanceWasReplicated(SourceModel, TargetModel, '1')
|
verifyInstanceWasReplicated(SourceModel, TargetModel, '1'),
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -599,14 +598,14 @@ describe('Replication / Change APIs', function() {
|
||||||
connector.updateAttributes(
|
connector.updateAttributes(
|
||||||
TargetModel.modelName,
|
TargetModel.modelName,
|
||||||
'1',
|
'1',
|
||||||
{name: '3rd-party'},
|
{ name: '3rd-party' },
|
||||||
cb);
|
cb);
|
||||||
} else {
|
} else {
|
||||||
// 2.x connectors require `options`
|
// 2.x connectors require `options`
|
||||||
connector.updateAttributes(
|
connector.updateAttributes(
|
||||||
TargetModel.modelName,
|
TargetModel.modelName,
|
||||||
'1',
|
'1',
|
||||||
{name: '3rd-party'},
|
{ name: '3rd-party' },
|
||||||
{}, // options
|
{}, // options
|
||||||
cb);
|
cb);
|
||||||
}
|
}
|
||||||
|
@ -626,7 +625,7 @@ describe('Replication / Change APIs', function() {
|
||||||
},
|
},
|
||||||
|
|
||||||
replicateExpectingSuccess(),
|
replicateExpectingSuccess(),
|
||||||
verifyInstanceWasReplicated(SourceModel, TargetModel, '1')
|
verifyInstanceWasReplicated(SourceModel, TargetModel, '1'),
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -657,7 +656,7 @@ describe('Replication / Change APIs', function() {
|
||||||
next();
|
next();
|
||||||
},
|
},
|
||||||
replicateExpectingSuccess(),
|
replicateExpectingSuccess(),
|
||||||
verifyInstanceWasReplicated(SourceModel, TargetModel, '1')
|
verifyInstanceWasReplicated(SourceModel, TargetModel, '1'),
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -686,7 +685,7 @@ describe('Replication / Change APIs', function() {
|
||||||
inst.name = 'target update';
|
inst.name = 'target update';
|
||||||
inst.save(cb);
|
inst.save(cb);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
], function(err) {
|
], function(err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
SourceModel.replicate(TargetModel, function(err, conflicts) {
|
SourceModel.replicate(TargetModel, function(err, conflicts) {
|
||||||
|
@ -724,11 +723,11 @@ describe('Replication / Change APIs', function() {
|
||||||
this.conflict.models(function(err, source, target) {
|
this.conflict.models(function(err, source, target) {
|
||||||
assert.deepEqual(source.toJSON(), {
|
assert.deepEqual(source.toJSON(), {
|
||||||
id: test.model.id,
|
id: test.model.id,
|
||||||
name: 'source update'
|
name: 'source update',
|
||||||
});
|
});
|
||||||
assert.deepEqual(target.toJSON(), {
|
assert.deepEqual(target.toJSON(), {
|
||||||
id: test.model.id,
|
id: test.model.id,
|
||||||
name: 'target update'
|
name: 'target update',
|
||||||
});
|
});
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -758,7 +757,7 @@ describe('Replication / Change APIs', function() {
|
||||||
inst.name = 'target update';
|
inst.name = 'target update';
|
||||||
inst.save(cb);
|
inst.save(cb);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
], function(err) {
|
], function(err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
SourceModel.replicate(TargetModel, function(err, conflicts) {
|
SourceModel.replicate(TargetModel, function(err, conflicts) {
|
||||||
|
@ -797,7 +796,7 @@ describe('Replication / Change APIs', function() {
|
||||||
assert.equal(source, null);
|
assert.equal(source, null);
|
||||||
assert.deepEqual(target.toJSON(), {
|
assert.deepEqual(target.toJSON(), {
|
||||||
id: test.model.id,
|
id: test.model.id,
|
||||||
name: 'target update'
|
name: 'target update',
|
||||||
});
|
});
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -827,7 +826,7 @@ describe('Replication / Change APIs', function() {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
inst.remove(cb);
|
inst.remove(cb);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
], function(err) {
|
], function(err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
SourceModel.replicate(TargetModel, function(err, conflicts) {
|
SourceModel.replicate(TargetModel, function(err, conflicts) {
|
||||||
|
@ -866,7 +865,7 @@ describe('Replication / Change APIs', function() {
|
||||||
assert.equal(target, null);
|
assert.equal(target, null);
|
||||||
assert.deepEqual(source.toJSON(), {
|
assert.deepEqual(source.toJSON(), {
|
||||||
id: test.model.id,
|
id: test.model.id,
|
||||||
name: 'source update'
|
name: 'source update',
|
||||||
});
|
});
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -895,7 +894,7 @@ describe('Replication / Change APIs', function() {
|
||||||
if (err) return cb(err);
|
if (err) return cb(err);
|
||||||
inst.remove(cb);
|
inst.remove(cb);
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
], function(err) {
|
], function(err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
SourceModel.replicate(TargetModel, function(err, conflicts) {
|
SourceModel.replicate(TargetModel, function(err, conflicts) {
|
||||||
|
@ -958,7 +957,7 @@ describe('Replication / Change APIs', function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
SourceModel.findOrCreate(
|
SourceModel.findOrCreate(
|
||||||
{ where: { name: 'does-not-exist' } },
|
{ where: { name: 'does-not-exist' }},
|
||||||
{ name: 'created' },
|
{ name: 'created' },
|
||||||
function(err, inst) {
|
function(err, inst) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
|
@ -1070,14 +1069,14 @@ describe('Replication / Change APIs', function() {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
replicateExpectingSuccess(),
|
replicateExpectingSuccess(),
|
||||||
verifySourceWasReplicated()
|
verifySourceWasReplicated(),
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(function setupThirdModel() {
|
beforeEach(function setupThirdModel() {
|
||||||
AnotherModel = this.AnotherModel = PersistedModel.extend(
|
AnotherModel = this.AnotherModel = PersistedModel.extend(
|
||||||
'AnotherModel-' + tid,
|
'AnotherModel-' + tid,
|
||||||
{ id: { id: true, type: String, defaultFn: 'guid' } },
|
{ id: { id: true, type: String, defaultFn: 'guid' }},
|
||||||
{ trackChanges: true });
|
{ trackChanges: true });
|
||||||
|
|
||||||
// NOTE(bajtos) At the moment, all models share the same Checkpoint
|
// NOTE(bajtos) At the moment, all models share the same Checkpoint
|
||||||
|
@ -1107,7 +1106,7 @@ describe('Replication / Change APIs', function() {
|
||||||
expect(getIds(list)).to.not.contain(sourceInstance.id);
|
expect(getIds(list)).to.not.contain(sourceInstance.id);
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1119,7 +1118,7 @@ describe('Replication / Change APIs', function() {
|
||||||
updateSourceInstanceNameTo('updated'),
|
updateSourceInstanceNameTo('updated'),
|
||||||
updateSourceInstanceNameTo('again'),
|
updateSourceInstanceNameTo('again'),
|
||||||
replicateExpectingSuccess(),
|
replicateExpectingSuccess(),
|
||||||
verifySourceWasReplicated()
|
verifySourceWasReplicated(),
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1141,7 +1140,7 @@ describe('Replication / Change APIs', function() {
|
||||||
async.series([
|
async.series([
|
||||||
// Note that ClientA->Server was already replicated during setup
|
// Note that ClientA->Server was already replicated during setup
|
||||||
replicateExpectingSuccess(Server, ClientB),
|
replicateExpectingSuccess(Server, ClientB),
|
||||||
verifySourceWasReplicated(ClientB)
|
verifySourceWasReplicated(ClientB),
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1158,7 +1157,7 @@ describe('Replication / Change APIs', function() {
|
||||||
replicateExpectingSuccess(ClientA, Server),
|
replicateExpectingSuccess(ClientA, Server),
|
||||||
|
|
||||||
replicateExpectingSuccess(Server, ClientB),
|
replicateExpectingSuccess(Server, ClientB),
|
||||||
verifySourceWasReplicated(ClientB)
|
verifySourceWasReplicated(ClientB),
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1167,7 +1166,7 @@ describe('Replication / Change APIs', function() {
|
||||||
deleteSourceInstance(),
|
deleteSourceInstance(),
|
||||||
replicateExpectingSuccess(ClientA, Server),
|
replicateExpectingSuccess(ClientA, Server),
|
||||||
replicateExpectingSuccess(Server, ClientB),
|
replicateExpectingSuccess(Server, ClientB),
|
||||||
verifySourceWasReplicated(ClientB)
|
verifySourceWasReplicated(ClientB),
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1183,7 +1182,7 @@ describe('Replication / Change APIs', function() {
|
||||||
it('propagates CREATE', function(done) {
|
it('propagates CREATE', function(done) {
|
||||||
async.series([
|
async.series([
|
||||||
sync(ClientA, Server),
|
sync(ClientA, Server),
|
||||||
sync(ClientB, Server)
|
sync(ClientB, Server),
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1207,7 +1206,6 @@ describe('Replication / Change APIs', function() {
|
||||||
// ClientB fetches the created & updated instance from the server
|
// ClientB fetches the created & updated instance from the server
|
||||||
sync(ClientB, Server),
|
sync(ClientB, Server),
|
||||||
], done);
|
], done);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not report false conflicts', function(done) {
|
it('does not report false conflicts', function(done) {
|
||||||
|
@ -1227,7 +1225,7 @@ describe('Replication / Change APIs', function() {
|
||||||
sync(ClientB, Server),
|
sync(ClientB, Server),
|
||||||
|
|
||||||
// client A fetches the changes
|
// client A fetches the changes
|
||||||
sync(ClientA, Server)
|
sync(ClientA, Server),
|
||||||
], done);
|
], done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1325,7 +1323,7 @@ describe('Replication / Change APIs', function() {
|
||||||
// and sync back to ClientA too
|
// and sync back to ClientA too
|
||||||
sync(ClientA, Server),
|
sync(ClientA, Server),
|
||||||
|
|
||||||
verifyInstanceWasReplicated(ClientB, ClientA, sourceInstanceId)
|
verifyInstanceWasReplicated(ClientB, ClientA, sourceInstanceId),
|
||||||
], cb);
|
], cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1363,7 +1361,7 @@ describe('Replication / Change APIs', function() {
|
||||||
// and sync back to ClientA too
|
// and sync back to ClientA too
|
||||||
sync(ClientA, Server),
|
sync(ClientA, Server),
|
||||||
|
|
||||||
verifyInstanceWasReplicated(ClientB, ClientA, sourceInstanceId)
|
verifyInstanceWasReplicated(ClientB, ClientA, sourceInstanceId),
|
||||||
], cb);
|
], cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1383,11 +1381,10 @@ describe('Replication / Change APIs', function() {
|
||||||
// NOTE(bajtos) It's important to replicate from the client to the
|
// NOTE(bajtos) It's important to replicate from the client to the
|
||||||
// server first, so that we can resolve any conflicts at the client
|
// server first, so that we can resolve any conflicts at the client
|
||||||
replicateExpectingSuccess(client, server),
|
replicateExpectingSuccess(client, server),
|
||||||
replicateExpectingSuccess(server, client)
|
replicateExpectingSuccess(server, client),
|
||||||
], next);
|
], next);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function updateSourceInstanceNameTo(value) {
|
function updateSourceInstanceNameTo(value) {
|
||||||
|
|
|
@ -4,7 +4,7 @@ describe('loopback.rest', function() {
|
||||||
var MyModel;
|
var MyModel;
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
var ds = app.dataSource('db', { connector: loopback.Memory });
|
var ds = app.dataSource('db', { connector: loopback.Memory });
|
||||||
MyModel = ds.createModel('MyModel', {name: String});
|
MyModel = ds.createModel('MyModel', { name: String });
|
||||||
loopback.autoAttach();
|
loopback.autoAttach();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ describe('loopback.rest', function() {
|
||||||
app.set('legacyExplorer', false);
|
app.set('legacyExplorer', false);
|
||||||
app.model(MyModel);
|
app.model(MyModel);
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
MyModel.create({name: 'm1'}, function(err, inst) {
|
MyModel.create({ name: 'm1' }, function(err, inst) {
|
||||||
request(app)
|
request(app)
|
||||||
.del('/mymodels/' + inst.id)
|
.del('/mymodels/' + inst.id)
|
||||||
.expect(200, function(err, res) {
|
.expect(200, function(err, res) {
|
||||||
|
@ -61,7 +61,7 @@ describe('loopback.rest', function() {
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(res.body).to.eql({exists: false});
|
expect(res.body).to.eql({ exists: false });
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -69,7 +69,7 @@ describe('loopback.rest', function() {
|
||||||
it('should report 200 for GET /:id found', function(done) {
|
it('should report 200 for GET /:id found', function(done) {
|
||||||
app.model(MyModel);
|
app.model(MyModel);
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
MyModel.create({name: 'm1'}, function(err, inst) {
|
MyModel.create({ name: 'm1' }, function(err, inst) {
|
||||||
request(app).get('/mymodels/' + inst.id)
|
request(app).get('/mymodels/' + inst.id)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(done);
|
.end(done);
|
||||||
|
@ -79,7 +79,7 @@ describe('loopback.rest', function() {
|
||||||
it('should report 200 for HEAD /:id found', function(done) {
|
it('should report 200 for HEAD /:id found', function(done) {
|
||||||
app.model(MyModel);
|
app.model(MyModel);
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
MyModel.create({name: 'm2'}, function(err, inst) {
|
MyModel.create({ name: 'm2' }, function(err, inst) {
|
||||||
request(app).head('/mymodels/' + inst.id)
|
request(app).head('/mymodels/' + inst.id)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(done);
|
.end(done);
|
||||||
|
@ -89,12 +89,12 @@ describe('loopback.rest', function() {
|
||||||
it('should report 200 for GET /:id/exists found', function(done) {
|
it('should report 200 for GET /:id/exists found', function(done) {
|
||||||
app.model(MyModel);
|
app.model(MyModel);
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
MyModel.create({name: 'm2'}, function(err, inst) {
|
MyModel.create({ name: 'm2' }, function(err, inst) {
|
||||||
request(app).get('/mymodels/' + inst.id + '/exists')
|
request(app).get('/mymodels/' + inst.id + '/exists')
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(res.body).to.eql({exists: true});
|
expect(res.body).to.eql({ exists: true });
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -105,7 +105,7 @@ describe('loopback.rest', function() {
|
||||||
|
|
||||||
// NOTE it is crucial to set `remoting` before creating any models
|
// NOTE it is crucial to set `remoting` before creating any models
|
||||||
var supportedTypes = ['json', 'application/javascript', 'text/javascript'];
|
var supportedTypes = ['json', 'application/javascript', 'text/javascript'];
|
||||||
app.set('remoting', { rest: { supportedTypes: supportedTypes } });
|
app.set('remoting', { rest: { supportedTypes: supportedTypes }});
|
||||||
|
|
||||||
app.model(MyModel);
|
app.model(MyModel);
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
|
@ -120,7 +120,7 @@ describe('loopback.rest', function() {
|
||||||
var ds = app.dataSource('db', { connector: loopback.Memory });
|
var ds = app.dataSource('db', { connector: loopback.Memory });
|
||||||
var CustomModel = ds.createModel('CustomModel',
|
var CustomModel = ds.createModel('CustomModel',
|
||||||
{ name: String },
|
{ name: String },
|
||||||
{ http: { 'path': 'domain1/CustomModelPath' }
|
{ http: { 'path': 'domain1/CustomModelPath' },
|
||||||
});
|
});
|
||||||
|
|
||||||
app.model(CustomModel);
|
app.model(CustomModel);
|
||||||
|
@ -133,7 +133,7 @@ describe('loopback.rest', function() {
|
||||||
var ds = app.dataSource('db', { connector: loopback.Memory });
|
var ds = app.dataSource('db', { connector: loopback.Memory });
|
||||||
var CustomModel = ds.createModel('CustomModel',
|
var CustomModel = ds.createModel('CustomModel',
|
||||||
{ name: String },
|
{ name: String },
|
||||||
{ http: { path: 'domain%20one/CustomModelPath' }
|
{ http: { path: 'domain%20one/CustomModelPath' },
|
||||||
});
|
});
|
||||||
|
|
||||||
app.model(CustomModel);
|
app.model(CustomModel);
|
||||||
|
@ -163,8 +163,8 @@ describe('loopback.rest', function() {
|
||||||
cb(null, req.accessToken ? req.accessToken.id : null);
|
cb(null, req.accessToken ? req.accessToken.id : null);
|
||||||
};
|
};
|
||||||
loopback.remoteMethod(User.getToken, {
|
loopback.remoteMethod(User.getToken, {
|
||||||
accepts: [{ type: 'object', http: { source: 'req' } }],
|
accepts: [{ type: 'object', http: { source: 'req' }}],
|
||||||
returns: [{ type: 'object', name: 'id' }]
|
returns: [{ type: 'object', name: 'id' }],
|
||||||
});
|
});
|
||||||
|
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
|
@ -240,15 +240,15 @@ describe('loopback.rest', function() {
|
||||||
cb(null, req && req.accessToken ? req.accessToken.id : null);
|
cb(null, req && req.accessToken ? req.accessToken.id : null);
|
||||||
};
|
};
|
||||||
// Set up the ACL
|
// Set up the ACL
|
||||||
User.settings.acls.push({principalType: 'ROLE',
|
User.settings.acls.push({ principalType: 'ROLE',
|
||||||
principalId: '$authenticated', permission: 'ALLOW',
|
principalId: '$authenticated', permission: 'ALLOW',
|
||||||
property: 'getToken'});
|
property: 'getToken' });
|
||||||
|
|
||||||
loopback.remoteMethod(User.getToken, {
|
loopback.remoteMethod(User.getToken, {
|
||||||
accepts: [],
|
accepts: [],
|
||||||
returns: [
|
returns: [
|
||||||
{ type: 'object', name: 'id' }
|
{ type: 'object', name: 'id' },
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -276,7 +276,7 @@ describe('loopback.rest', function() {
|
||||||
|
|
||||||
it('should enable context with loopback.rest', function(done) {
|
it('should enable context with loopback.rest', function(done) {
|
||||||
app.enableAuth();
|
app.enableAuth();
|
||||||
app.set('remoting', { context: { enableHttpContext: true } });
|
app.set('remoting', { context: { enableHttpContext: true }});
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
|
|
||||||
invokeGetToken(done);
|
invokeGetToken(done);
|
||||||
|
@ -312,8 +312,8 @@ describe('loopback.rest', function() {
|
||||||
loopback.remoteMethod(User.getToken, {
|
loopback.remoteMethod(User.getToken, {
|
||||||
accepts: [],
|
accepts: [],
|
||||||
returns: [
|
returns: [
|
||||||
{ type: 'object', name: 'id' }
|
{ type: 'object', name: 'id' },
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
invokeGetToken(done);
|
invokeGetToken(done);
|
||||||
|
@ -326,9 +326,9 @@ describe('loopback.rest', function() {
|
||||||
// the global model registry
|
// the global model registry
|
||||||
app.model('accessToken', {
|
app.model('accessToken', {
|
||||||
options: {
|
options: {
|
||||||
base: 'AccessToken'
|
base: 'AccessToken',
|
||||||
},
|
},
|
||||||
dataSource: 'db'
|
dataSource: 'db',
|
||||||
});
|
});
|
||||||
return app.model('user', {
|
return app.model('user', {
|
||||||
options: {
|
options: {
|
||||||
|
@ -337,11 +337,11 @@ describe('loopback.rest', function() {
|
||||||
accessTokens: {
|
accessTokens: {
|
||||||
model: 'accessToken',
|
model: 'accessToken',
|
||||||
type: 'hasMany',
|
type: 'hasMany',
|
||||||
foreignKey: 'userId'
|
foreignKey: 'userId',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
dataSource: 'db'
|
dataSource: 'db',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function givenLoggedInUser(cb, done) {
|
function givenLoggedInUser(cb, done) {
|
||||||
|
@ -363,11 +363,11 @@ describe('loopback.rest', function() {
|
||||||
describe('with specific definitions in model-config.json', function() {
|
describe('with specific definitions in model-config.json', function() {
|
||||||
it('should not be exposed when the definition value is false',
|
it('should not be exposed when the definition value is false',
|
||||||
function(done) {
|
function(done) {
|
||||||
var app = require(getFixturePath('model-config-defined-false'));
|
var app = require(getFixturePath('model-config-defined-false'));
|
||||||
request(app)
|
request(app)
|
||||||
.get('/todos')
|
.get('/todos')
|
||||||
.expect(404, done);
|
.expect(404, done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be exposed when the definition value is true', function(done) {
|
it('should be exposed when the definition value is true', function(done) {
|
||||||
var app = require(getFixturePath('model-config-defined-true'));
|
var app = require(getFixturePath('model-config-defined-true'));
|
||||||
|
@ -380,18 +380,18 @@ describe('loopback.rest', function() {
|
||||||
describe('with default definitions in model-config.json', function() {
|
describe('with default definitions in model-config.json', function() {
|
||||||
it('should not be exposed when the definition value is false',
|
it('should not be exposed when the definition value is false',
|
||||||
function(done) {
|
function(done) {
|
||||||
var app = require(getFixturePath('model-config-default-false'));
|
var app = require(getFixturePath('model-config-default-false'));
|
||||||
request(app)
|
request(app)
|
||||||
.get('/todos')
|
.get('/todos')
|
||||||
.expect(404, done);
|
.expect(404, done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be exposed when the definition value is true', function(done) {
|
it('should be exposed when the definition value is true', function(done) {
|
||||||
var app = require(getFixturePath('model-config-default-true'));
|
var app = require(getFixturePath('model-config-default-true'));
|
||||||
app.models.Todo.create([
|
app.models.Todo.create([
|
||||||
{content: 'a'},
|
{ content: 'a' },
|
||||||
{content: 'b'},
|
{ content: 'b' },
|
||||||
{content: 'c'}
|
{ content: 'c' },
|
||||||
], function() {
|
], function() {
|
||||||
request(app)
|
request(app)
|
||||||
.del('/todos')
|
.del('/todos')
|
||||||
|
@ -408,36 +408,36 @@ describe('loopback.rest', function() {
|
||||||
describe('with specific definitions in config.json', function() {
|
describe('with specific definitions in config.json', function() {
|
||||||
it('should not be exposed when the definition value is false',
|
it('should not be exposed when the definition value is false',
|
||||||
function(done) {
|
function(done) {
|
||||||
var app = require(getFixturePath('config-defined-false'));
|
var app = require(getFixturePath('config-defined-false'));
|
||||||
request(app)
|
request(app)
|
||||||
.get('/todos')
|
.get('/todos')
|
||||||
.expect(404, done);
|
.expect(404, done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be exposed when the definition value is true',
|
it('should be exposed when the definition value is true',
|
||||||
function(done) {
|
function(done) {
|
||||||
var app = require(getFixturePath('config-defined-true'));
|
var app = require(getFixturePath('config-defined-true'));
|
||||||
request(app)
|
request(app)
|
||||||
.get('/todos')
|
.get('/todos')
|
||||||
.expect(200, done);
|
.expect(200, done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('with default definitions in config.json', function() {
|
describe('with default definitions in config.json', function() {
|
||||||
it('should not be exposed when the definition value is false',
|
it('should not be exposed when the definition value is false',
|
||||||
function(done) {
|
function(done) {
|
||||||
var app = require(getFixturePath('config-default-false'));
|
var app = require(getFixturePath('config-default-false'));
|
||||||
request(app)
|
request(app)
|
||||||
.get('/todos')
|
.get('/todos')
|
||||||
.expect(404, done);
|
.expect(404, done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be exposed when the definition value is true', function(done) {
|
it('should be exposed when the definition value is true', function(done) {
|
||||||
var app = require(getFixturePath('config-default-true'));
|
var app = require(getFixturePath('config-default-true'));
|
||||||
app.models.Todo.create([
|
app.models.Todo.create([
|
||||||
{content: 'a'},
|
{ content: 'a' },
|
||||||
{content: 'b'},
|
{ content: 'b' },
|
||||||
{content: 'c'}
|
{ content: 'c' },
|
||||||
], function() {
|
], function() {
|
||||||
request(app)
|
request(app)
|
||||||
.del('/todos')
|
.del('/todos')
|
||||||
|
@ -457,20 +457,20 @@ describe('loopback.rest', function() {
|
||||||
// consequence, this causes the tests in user.integration to fail.
|
// consequence, this causes the tests in user.integration to fail.
|
||||||
describe.skip('with definitions in both config.json and model-config.json',
|
describe.skip('with definitions in both config.json and model-config.json',
|
||||||
function() {
|
function() {
|
||||||
it('should prioritize the settings in model-config.json', function(done) {
|
it('should prioritize the settings in model-config.json', function(done) {
|
||||||
var app = require(getFixturePath('both-configs-set'));
|
var app = require(getFixturePath('both-configs-set'));
|
||||||
request(app)
|
request(app)
|
||||||
.del('/todos')
|
.del('/todos')
|
||||||
.expect(404, done);
|
.expect(404, done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fall back to config.json settings if setting is not found in' +
|
it('should fall back to config.json settings if setting is not found in' +
|
||||||
'model-config.json', function(done) {
|
'model-config.json', function(done) {
|
||||||
var app = require(getFixturePath('both-configs-set'));
|
var app = require(getFixturePath('both-configs-set'));
|
||||||
request(app)
|
request(app)
|
||||||
.get('/todos')
|
.get('/todos')
|
||||||
.expect(404, done);
|
.expect(404, done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -18,7 +18,7 @@ describe('role model', function() {
|
||||||
var ds;
|
var ds;
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
ds = loopback.createDataSource({connector: 'memory'});
|
ds = loopback.createDataSource({ connector: 'memory' });
|
||||||
// Re-attach the models so that they can have isolated store to avoid
|
// Re-attach the models so that they can have isolated store to avoid
|
||||||
// pollutions from other tests
|
// pollutions from other tests
|
||||||
ACL.attachTo(ds);
|
ACL.attachTo(ds);
|
||||||
|
@ -36,9 +36,10 @@ describe('role model', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should define role/role relations', function() {
|
it('should define role/role relations', function() {
|
||||||
Role.create({name: 'user'}, function(err, userRole) {
|
Role.create({ name: 'user' }, function(err, userRole) {
|
||||||
Role.create({name: 'admin'}, function(err, adminRole) {
|
Role.create({ name: 'admin' }, function(err, adminRole) {
|
||||||
userRole.principals.create({principalType: RoleMapping.ROLE, principalId: adminRole.id}, function(err, mapping) {
|
userRole.principals.create({ principalType: RoleMapping.ROLE, principalId: adminRole.id },
|
||||||
|
function(err, mapping) {
|
||||||
Role.find(function(err, roles) {
|
Role.find(function(err, roles) {
|
||||||
assert.equal(roles.length, 2);
|
assert.equal(roles.length, 2);
|
||||||
});
|
});
|
||||||
|
@ -56,15 +57,14 @@ describe('role model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should define role/user relations', function() {
|
it('should define role/user relations', function() {
|
||||||
|
User.create({ name: 'Raymond', email: 'x@y.com', password: 'foobar' }, function(err, user) {
|
||||||
User.create({name: 'Raymond', email: 'x@y.com', password: 'foobar'}, function(err, user) {
|
|
||||||
// console.log('User: ', user.id);
|
// console.log('User: ', user.id);
|
||||||
Role.create({name: 'userRole'}, function(err, role) {
|
Role.create({ name: 'userRole' }, function(err, role) {
|
||||||
role.principals.create({principalType: RoleMapping.USER, principalId: user.id}, function(err, p) {
|
role.principals.create({ principalType: RoleMapping.USER, principalId: user.id },
|
||||||
|
function(err, p) {
|
||||||
Role.find(function(err, roles) {
|
Role.find(function(err, roles) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
assert.equal(roles.length, 1);
|
assert.equal(roles.length, 1);
|
||||||
|
@ -85,16 +85,15 @@ describe('role model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should automatically generate role id', function() {
|
it('should automatically generate role id', function() {
|
||||||
|
User.create({ name: 'Raymond', email: 'x@y.com', password: 'foobar' }, function(err, user) {
|
||||||
User.create({name: 'Raymond', email: 'x@y.com', password: 'foobar'}, function(err, user) {
|
|
||||||
// console.log('User: ', user.id);
|
// console.log('User: ', user.id);
|
||||||
Role.create({name: 'userRole'}, function(err, role) {
|
Role.create({ name: 'userRole' }, function(err, role) {
|
||||||
assert(role.id);
|
assert(role.id);
|
||||||
role.principals.create({principalType: RoleMapping.USER, principalId: user.id}, function(err, p) {
|
role.principals.create({ principalType: RoleMapping.USER, principalId: user.id },
|
||||||
|
function(err, p) {
|
||||||
assert(p.id);
|
assert(p.id);
|
||||||
assert.equal(p.roleId, role.id);
|
assert.equal(p.roleId, role.id);
|
||||||
Role.find(function(err, roles) {
|
Role.find(function(err, roles) {
|
||||||
|
@ -117,45 +116,52 @@ describe('role model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support getRoles() and isInRole()', function() {
|
it('should support getRoles() and isInRole()', function() {
|
||||||
User.create({name: 'Raymond', email: 'x@y.com', password: 'foobar'}, function(err, user) {
|
User.create({ name: 'Raymond', email: 'x@y.com', password: 'foobar' }, function(err, user) {
|
||||||
// console.log('User: ', user.id);
|
// console.log('User: ', user.id);
|
||||||
Role.create({name: 'userRole'}, function(err, role) {
|
Role.create({ name: 'userRole' }, function(err, role) {
|
||||||
role.principals.create({principalType: RoleMapping.USER, principalId: user.id}, function(err, p) {
|
role.principals.create({ principalType: RoleMapping.USER, principalId: user.id },
|
||||||
|
function(err, p) {
|
||||||
// Role.find(console.log);
|
// Role.find(console.log);
|
||||||
// role.principals(console.log);
|
// role.principals(console.log);
|
||||||
Role.isInRole('userRole', {principalType: RoleMapping.USER, principalId: user.id}, function(err, exists) {
|
Role.isInRole('userRole', { principalType: RoleMapping.USER, principalId: user.id },
|
||||||
|
function(err, exists) {
|
||||||
assert(!err && exists === true);
|
assert(!err && exists === true);
|
||||||
});
|
});
|
||||||
|
|
||||||
Role.isInRole('userRole', {principalType: RoleMapping.APP, principalId: user.id}, function(err, exists) {
|
Role.isInRole('userRole', { principalType: RoleMapping.APP, principalId: user.id },
|
||||||
|
function(err, exists) {
|
||||||
assert(!err && exists === false);
|
assert(!err && exists === false);
|
||||||
});
|
});
|
||||||
|
|
||||||
Role.isInRole('userRole', {principalType: RoleMapping.USER, principalId: 100}, function(err, exists) {
|
Role.isInRole('userRole', { principalType: RoleMapping.USER, principalId: 100 },
|
||||||
|
function(err, exists) {
|
||||||
assert(!err && exists === false);
|
assert(!err && exists === false);
|
||||||
});
|
});
|
||||||
|
|
||||||
Role.getRoles({principalType: RoleMapping.USER, principalId: user.id}, function(err, roles) {
|
Role.getRoles({ principalType: RoleMapping.USER, principalId: user.id },
|
||||||
|
function(err, roles) {
|
||||||
assert.equal(roles.length, 3); // everyone, authenticated, userRole
|
assert.equal(roles.length, 3); // everyone, authenticated, userRole
|
||||||
assert(roles.indexOf(role.id) >= 0);
|
assert(roles.indexOf(role.id) >= 0);
|
||||||
assert(roles.indexOf(Role.EVERYONE) >= 0);
|
assert(roles.indexOf(Role.EVERYONE) >= 0);
|
||||||
assert(roles.indexOf(Role.AUTHENTICATED) >= 0);
|
assert(roles.indexOf(Role.AUTHENTICATED) >= 0);
|
||||||
});
|
});
|
||||||
Role.getRoles({principalType: RoleMapping.APP, principalId: user.id}, function(err, roles) {
|
Role.getRoles({ principalType: RoleMapping.APP, principalId: user.id },
|
||||||
|
function(err, roles) {
|
||||||
assert.equal(roles.length, 2);
|
assert.equal(roles.length, 2);
|
||||||
assert(roles.indexOf(Role.EVERYONE) >= 0);
|
assert(roles.indexOf(Role.EVERYONE) >= 0);
|
||||||
assert(roles.indexOf(Role.AUTHENTICATED) >= 0);
|
assert(roles.indexOf(Role.AUTHENTICATED) >= 0);
|
||||||
});
|
});
|
||||||
Role.getRoles({principalType: RoleMapping.USER, principalId: 100}, function(err, roles) {
|
Role.getRoles({ principalType: RoleMapping.USER, principalId: 100 },
|
||||||
|
function(err, roles) {
|
||||||
assert.equal(roles.length, 2);
|
assert.equal(roles.length, 2);
|
||||||
assert(roles.indexOf(Role.EVERYONE) >= 0);
|
assert(roles.indexOf(Role.EVERYONE) >= 0);
|
||||||
assert(roles.indexOf(Role.AUTHENTICATED) >= 0);
|
assert(roles.indexOf(Role.AUTHENTICATED) >= 0);
|
||||||
});
|
});
|
||||||
Role.getRoles({principalType: RoleMapping.USER, principalId: null}, function(err, roles) {
|
Role.getRoles({ principalType: RoleMapping.USER, principalId: null },
|
||||||
|
function(err, roles) {
|
||||||
assert.equal(roles.length, 2);
|
assert.equal(roles.length, 2);
|
||||||
assert(roles.indexOf(Role.EVERYONE) >= 0);
|
assert(roles.indexOf(Role.EVERYONE) >= 0);
|
||||||
assert(roles.indexOf(Role.UNAUTHENTICATED) >= 0);
|
assert(roles.indexOf(Role.UNAUTHENTICATED) >= 0);
|
||||||
|
@ -163,54 +169,60 @@ describe('role model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support owner role resolver', function() {
|
it('should support owner role resolver', function() {
|
||||||
|
|
||||||
var Album = ds.createModel('Album', {
|
var Album = ds.createModel('Album', {
|
||||||
name: String,
|
name: String,
|
||||||
userId: Number
|
userId: Number,
|
||||||
}, {
|
}, {
|
||||||
relations: {
|
relations: {
|
||||||
user: {
|
user: {
|
||||||
type: 'belongsTo',
|
type: 'belongsTo',
|
||||||
model: 'User',
|
model: 'User',
|
||||||
foreignKey: 'userId'
|
foreignKey: 'userId',
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
User.create({name: 'Raymond', email: 'x@y.com', password: 'foobar'}, function(err, user) {
|
User.create({ name: 'Raymond', email: 'x@y.com', password: 'foobar' }, function(err, user) {
|
||||||
Role.isInRole(Role.AUTHENTICATED, {principalType: ACL.USER, principalId: user.id}, function(err, yes) {
|
Role.isInRole(Role.AUTHENTICATED, { principalType: ACL.USER, principalId: user.id },
|
||||||
|
function(err, yes) {
|
||||||
assert(!err && yes);
|
assert(!err && yes);
|
||||||
});
|
});
|
||||||
Role.isInRole(Role.AUTHENTICATED, {principalType: ACL.USER, principalId: null}, function(err, yes) {
|
Role.isInRole(Role.AUTHENTICATED, { principalType: ACL.USER, principalId: null },
|
||||||
|
function(err, yes) {
|
||||||
assert(!err && !yes);
|
assert(!err && !yes);
|
||||||
});
|
});
|
||||||
|
|
||||||
Role.isInRole(Role.UNAUTHENTICATED, {principalType: ACL.USER, principalId: user.id}, function(err, yes) {
|
Role.isInRole(Role.UNAUTHENTICATED, { principalType: ACL.USER, principalId: user.id },
|
||||||
|
function(err, yes) {
|
||||||
assert(!err && !yes);
|
assert(!err && !yes);
|
||||||
});
|
});
|
||||||
Role.isInRole(Role.UNAUTHENTICATED, {principalType: ACL.USER, principalId: null}, function(err, yes) {
|
Role.isInRole(Role.UNAUTHENTICATED, { principalType: ACL.USER, principalId: null },
|
||||||
|
function(err, yes) {
|
||||||
assert(!err && yes);
|
assert(!err && yes);
|
||||||
});
|
});
|
||||||
|
|
||||||
Role.isInRole(Role.EVERYONE, {principalType: ACL.USER, principalId: user.id}, function(err, yes) {
|
Role.isInRole(Role.EVERYONE, { principalType: ACL.USER, principalId: user.id },
|
||||||
|
function(err, yes) {
|
||||||
assert(!err && yes);
|
assert(!err && yes);
|
||||||
});
|
});
|
||||||
|
|
||||||
Role.isInRole(Role.EVERYONE, {principalType: ACL.USER, principalId: null}, function(err, yes) {
|
Role.isInRole(Role.EVERYONE, { principalType: ACL.USER, principalId: null },
|
||||||
|
function(err, yes) {
|
||||||
assert(!err && yes);
|
assert(!err && yes);
|
||||||
});
|
});
|
||||||
|
|
||||||
// console.log('User: ', user.id);
|
// console.log('User: ', user.id);
|
||||||
Album.create({name: 'Album 1', userId: user.id}, function(err, album1) {
|
Album.create({ name: 'Album 1', userId: user.id }, function(err, album1) {
|
||||||
Role.isInRole(Role.OWNER, {principalType: ACL.USER, principalId: user.id, model: Album, id: album1.id}, function(err, yes) {
|
var role = { principalType: ACL.USER, principalId: user.id, model: Album, id: album1.id };
|
||||||
|
Role.isInRole(Role.OWNER, role, function(err, yes) {
|
||||||
assert(!err && yes);
|
assert(!err && yes);
|
||||||
});
|
});
|
||||||
Album.create({name: 'Album 2'}, function(err, album2) {
|
Album.create({ name: 'Album 2' }, function(err, album2) {
|
||||||
Role.isInRole(Role.OWNER, {principalType: ACL.USER, principalId: user.id, model: Album, id: album2.id}, function(err, yes) {
|
role = { principalType: ACL.USER, principalId: user.id, model: Album, id: album2.id };
|
||||||
|
Role.isInRole(Role.OWNER, role, function(err, yes) {
|
||||||
assert(!err && !yes);
|
assert(!err && !yes);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -225,35 +237,35 @@ describe('role model', function() {
|
||||||
User.create({
|
User.create({
|
||||||
username: 'john',
|
username: 'john',
|
||||||
email: 'john@gmail.com',
|
email: 'john@gmail.com',
|
||||||
password: 'jpass'
|
password: 'jpass',
|
||||||
}, function(err, u) {
|
}, function(err, u) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
user = u;
|
user = u;
|
||||||
User.create({
|
User.create({
|
||||||
username: 'mary',
|
username: 'mary',
|
||||||
email: 'mary@gmail.com',
|
email: 'mary@gmail.com',
|
||||||
password: 'mpass'
|
password: 'mpass',
|
||||||
}, function(err, u) {
|
}, function(err, u) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
Application.create({
|
Application.create({
|
||||||
name: 'demo'
|
name: 'demo',
|
||||||
}, function(err, a) {
|
}, function(err, a) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
app = a;
|
app = a;
|
||||||
Role.create({
|
Role.create({
|
||||||
name: 'admin'
|
name: 'admin',
|
||||||
}, function(err, r) {
|
}, function(err, r) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
role = r;
|
role = r;
|
||||||
var principals = [
|
var principals = [
|
||||||
{
|
{
|
||||||
principalType: ACL.USER,
|
principalType: ACL.USER,
|
||||||
principalId: user.id
|
principalId: user.id,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
principalType: ACL.APP,
|
principalType: ACL.APP,
|
||||||
principalId: app.id
|
principalId: app.id,
|
||||||
}
|
},
|
||||||
];
|
];
|
||||||
async.each(principals, function(p, done) {
|
async.each(principals, function(p, done) {
|
||||||
role.principals.create(p, done);
|
role.principals.create(p, done);
|
||||||
|
@ -344,7 +356,6 @@ describe('role model', function() {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('listByPrincipalType', function() {
|
describe('listByPrincipalType', function() {
|
||||||
|
@ -371,9 +382,10 @@ describe('role model', function() {
|
||||||
|
|
||||||
mappings.forEach(function(principalType) {
|
mappings.forEach(function(principalType) {
|
||||||
var Model = principalTypesToModels[principalType];
|
var Model = principalTypesToModels[principalType];
|
||||||
Model.create({name:'test', email:'x@y.com', password: 'foobar'}, function(err, model) {
|
Model.create({ name: 'test', email: 'x@y.com', password: 'foobar' }, function(err, model) {
|
||||||
Role.create({name:'testRole'}, function(err, role) {
|
Role.create({ name: 'testRole' }, function(err, role) {
|
||||||
role.principals.create({principalType: principalType, principalId: model.id}, function(err, p) {
|
role.principals.create({ principalType: principalType, principalId: model.id },
|
||||||
|
function(err, p) {
|
||||||
var pluralName = Model.pluralModelName.toLowerCase();
|
var pluralName = Model.pluralModelName.toLowerCase();
|
||||||
role[pluralName](function(err, models) {
|
role[pluralName](function(err, models) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
|
@ -389,10 +401,11 @@ describe('role model', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should apply query', function(done) {
|
it('should apply query', function(done) {
|
||||||
User.create({name: 'Raymond', email: 'x@y.com', password: 'foobar'}, function(err, user) {
|
User.create({ name: 'Raymond', email: 'x@y.com', password: 'foobar' }, function(err, user) {
|
||||||
Role.create({name: 'userRole'}, function(err, role) {
|
Role.create({ name: 'userRole' }, function(err, role) {
|
||||||
role.principals.create({principalType: RoleMapping.USER, principalId: user.id}, function(err, p) {
|
role.principals.create({ principalType: RoleMapping.USER, principalId: user.id },
|
||||||
var query = {fields:['id', 'name']};
|
function(err, p) {
|
||||||
|
var query = { fields: ['id', 'name'] };
|
||||||
sandbox.spy(User, 'find');
|
sandbox.spy(User, 'find');
|
||||||
role.users(query, function(err, users) {
|
role.users(query, function(err, users) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
|
@ -406,5 +419,4 @@ describe('role model', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -21,14 +21,14 @@ beforeEach(function() {
|
||||||
|
|
||||||
// setup default data sources
|
// setup default data sources
|
||||||
loopback.setDefaultDataSourceForType('db', {
|
loopback.setDefaultDataSourceForType('db', {
|
||||||
connector: loopback.Memory
|
connector: loopback.Memory,
|
||||||
});
|
});
|
||||||
|
|
||||||
loopback.setDefaultDataSourceForType('mail', {
|
loopback.setDefaultDataSourceForType('mail', {
|
||||||
connector: loopback.Mail,
|
connector: loopback.Mail,
|
||||||
transports: [
|
transports: [
|
||||||
{type: 'STUB'}
|
{ type: 'STUB' },
|
||||||
]
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
/*jshint -W030 */
|
|
||||||
var loopback = require('../');
|
var loopback = require('../');
|
||||||
var lt = require('./helpers/loopback-testing-helper');
|
var lt = require('./helpers/loopback-testing-helper');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
@ -7,7 +6,6 @@ var app = require(path.join(SIMPLE_APP, 'server/server.js'));
|
||||||
var expect = require('chai').expect;
|
var expect = require('chai').expect;
|
||||||
|
|
||||||
describe('users - integration', function() {
|
describe('users - integration', function() {
|
||||||
|
|
||||||
lt.beforeEach.withApp(app);
|
lt.beforeEach.withApp(app);
|
||||||
|
|
||||||
before(function(done) {
|
before(function(done) {
|
||||||
|
@ -15,7 +13,7 @@ describe('users - integration', function() {
|
||||||
// other tests
|
// other tests
|
||||||
app.models.User.hasMany(app.models.post);
|
app.models.User.hasMany(app.models.post);
|
||||||
app.models.User.hasMany(app.models.AccessToken,
|
app.models.User.hasMany(app.models.AccessToken,
|
||||||
{options: {disableInclude: true}});
|
{ options: { disableInclude: true }});
|
||||||
app.models.AccessToken.belongsTo(app.models.User);
|
app.models.AccessToken.belongsTo(app.models.User);
|
||||||
app.models.User.destroyAll(function(err) {
|
app.models.User.destroyAll(function(err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
|
@ -30,12 +28,11 @@ describe('users - integration', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('base-user', function() {
|
describe('base-user', function() {
|
||||||
var userId;
|
var userId, accessToken;
|
||||||
var accessToken;
|
|
||||||
|
|
||||||
it('should create a new user', function(done) {
|
it('should create a new user', function(done) {
|
||||||
this.post('/api/users')
|
this.post('/api/users')
|
||||||
.send({username: 'x', email: 'x@y.com', password: 'x'})
|
.send({ username: 'x', email: 'x@y.com', password: 'x' })
|
||||||
.expect(200, function(err, res) {
|
.expect(200, function(err, res) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
expect(res.body.id).to.exist;
|
expect(res.body.id).to.exist;
|
||||||
|
@ -48,7 +45,7 @@ describe('users - integration', function() {
|
||||||
var url = '/api/users/login';
|
var url = '/api/users/login';
|
||||||
|
|
||||||
this.post(url)
|
this.post(url)
|
||||||
.send({username: 'x', email: 'x@y.com', password: 'x'})
|
.send({ username: 'x', email: 'x@y.com', password: 'x' })
|
||||||
.expect(200, function(err, res) {
|
.expect(200, function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
|
@ -62,7 +59,7 @@ describe('users - integration', function() {
|
||||||
it('should create post for a given user', function(done) {
|
it('should create post for a given user', function(done) {
|
||||||
var url = '/api/users/' + userId + '/posts?access_token=' + accessToken;
|
var url = '/api/users/' + userId + '/posts?access_token=' + accessToken;
|
||||||
this.post(url)
|
this.post(url)
|
||||||
.send({title: 'T1', content: 'C1'})
|
.send({ title: 'T1', content: 'C1' })
|
||||||
.expect(200, function(err, res) {
|
.expect(200, function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
|
@ -93,14 +90,13 @@ describe('users - integration', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('sub-user', function() {
|
describe('sub-user', function() {
|
||||||
var userId;
|
var userId, accessToken;
|
||||||
var accessToken;
|
|
||||||
|
|
||||||
it('should create a new user', function(done) {
|
it('should create a new user', function(done) {
|
||||||
var url = '/api/myUsers';
|
var url = '/api/myUsers';
|
||||||
|
|
||||||
this.post(url)
|
this.post(url)
|
||||||
.send({username: 'x', email: 'x@y.com', password: 'x'})
|
.send({ username: 'x', email: 'x@y.com', password: 'x' })
|
||||||
.expect(200, function(err, res) {
|
.expect(200, function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
|
@ -115,7 +111,7 @@ describe('users - integration', function() {
|
||||||
var url = '/api/myUsers/login';
|
var url = '/api/myUsers/login';
|
||||||
|
|
||||||
this.post(url)
|
this.post(url)
|
||||||
.send({username: 'x', email: 'x@y.com', password: 'x'})
|
.send({ username: 'x', email: 'x@y.com', password: 'x' })
|
||||||
.expect(200, function(err, res) {
|
.expect(200, function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
|
@ -129,7 +125,7 @@ describe('users - integration', function() {
|
||||||
it('should create blog for a given user', function(done) {
|
it('should create blog for a given user', function(done) {
|
||||||
var url = '/api/myUsers/' + userId + '/blogs?access_token=' + accessToken;
|
var url = '/api/myUsers/' + userId + '/blogs?access_token=' + accessToken;
|
||||||
this.post(url)
|
this.post(url)
|
||||||
.send({title: 'T1', content: 'C1'})
|
.send({ title: 'T1', content: 'C1' })
|
||||||
.expect(200, function(err, res) {
|
.expect(200, function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
@ -157,6 +153,4 @@ describe('users - integration', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,25 @@
|
||||||
require('./support');
|
require('./support');
|
||||||
var loopback = require('../');
|
var loopback = require('../');
|
||||||
var User;
|
var User, AccessToken;
|
||||||
var AccessToken;
|
|
||||||
var MailConnector = require('../lib/connectors/mail');
|
var MailConnector = require('../lib/connectors/mail');
|
||||||
|
|
||||||
var userMemory = loopback.createDataSource({
|
var userMemory = loopback.createDataSource({
|
||||||
connector: 'memory'
|
connector: 'memory',
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('User', function() {
|
describe('User', function() {
|
||||||
var validCredentialsEmail = 'foo@bar.com';
|
var validCredentialsEmail = 'foo@bar.com';
|
||||||
var validCredentials = {email: validCredentialsEmail, password: 'bar'};
|
var validCredentials = { email: validCredentialsEmail, password: 'bar' };
|
||||||
var validCredentialsEmailVerified = {email: 'foo1@bar.com', password: 'bar1', emailVerified: true};
|
var validCredentialsEmailVerified = {
|
||||||
var validCredentialsEmailVerifiedOverREST = {email: 'foo2@bar.com', password: 'bar2', emailVerified: true};
|
email: 'foo1@bar.com', password: 'bar1', emailVerified: true };
|
||||||
var validCredentialsWithTTL = {email: 'foo@bar.com', password: 'bar', ttl: 3600};
|
var validCredentialsEmailVerifiedOverREST = {
|
||||||
var validCredentialsWithTTLAndScope = {email: 'foo@bar.com', password: 'bar', ttl: 3600, scope: 'all'};
|
email: 'foo2@bar.com', password: 'bar2', emailVerified: true };
|
||||||
var validMixedCaseEmailCredentials = {email: 'Foo@bar.com', password: 'bar'};
|
var validCredentialsWithTTL = { email: 'foo@bar.com', password: 'bar', ttl: 3600 };
|
||||||
var invalidCredentials = {email: 'foo1@bar.com', password: 'invalid'};
|
var validCredentialsWithTTLAndScope = {
|
||||||
var incompleteCredentials = {password: 'bar1'};
|
email: 'foo@bar.com', password: 'bar', ttl: 3600, scope: 'all' };
|
||||||
|
var validMixedCaseEmailCredentials = { email: 'Foo@bar.com', password: 'bar' };
|
||||||
|
var invalidCredentials = { email: 'foo1@bar.com', password: 'invalid' };
|
||||||
|
var incompleteCredentials = { password: 'bar1' };
|
||||||
|
|
||||||
var defaultApp;
|
var defaultApp;
|
||||||
|
|
||||||
|
@ -26,23 +28,22 @@ describe('User', function() {
|
||||||
// to the wrong app instance
|
// to the wrong app instance
|
||||||
defaultApp = loopback.User.app;
|
defaultApp = loopback.User.app;
|
||||||
loopback.User.app = null;
|
loopback.User.app = null;
|
||||||
User = loopback.User.extend('TestUser', {}, {http: {path: 'test-users'}});
|
User = loopback.User.extend('TestUser', {}, { http: { path: 'test-users' }});
|
||||||
AccessToken = loopback.AccessToken.extend('TestAccessToken');
|
AccessToken = loopback.AccessToken.extend('TestAccessToken');
|
||||||
User.email = loopback.Email.extend('email');
|
User.email = loopback.Email.extend('email');
|
||||||
loopback.autoAttach();
|
loopback.autoAttach();
|
||||||
|
|
||||||
// Update the AccessToken relation to use the subclass of User
|
// Update the AccessToken relation to use the subclass of User
|
||||||
AccessToken.belongsTo(User, {as: 'user', foreignKey: 'userId'});
|
AccessToken.belongsTo(User, { as: 'user', foreignKey: 'userId' });
|
||||||
User.hasMany(AccessToken, {as: 'accessTokens', foreignKey: 'userId'});
|
User.hasMany(AccessToken, { as: 'accessTokens', foreignKey: 'userId' });
|
||||||
|
|
||||||
// allow many User.afterRemote's to be called
|
// allow many User.afterRemote's to be called
|
||||||
User.setMaxListeners(0);
|
User.setMaxListeners(0);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
app.enableAuth();
|
app.enableAuth();
|
||||||
app.use(loopback.token({model: AccessToken}));
|
app.use(loopback.token({ model: AccessToken }));
|
||||||
app.use(loopback.rest());
|
app.use(loopback.rest());
|
||||||
app.model(User);
|
app.model(User);
|
||||||
|
|
||||||
|
@ -60,7 +61,7 @@ describe('User', function() {
|
||||||
|
|
||||||
describe('User.create', function() {
|
describe('User.create', function() {
|
||||||
it('Create a new user', function(done) {
|
it('Create a new user', function(done) {
|
||||||
User.create({email: 'f@b.com', password: 'bar'}, function(err, user) {
|
User.create({ email: 'f@b.com', password: 'bar' }, function(err, user) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
assert(user.id);
|
assert(user.id);
|
||||||
assert(user.email);
|
assert(user.email);
|
||||||
|
@ -70,7 +71,7 @@ describe('User', function() {
|
||||||
|
|
||||||
it('Create a new user (email case-sensitivity off)', function(done) {
|
it('Create a new user (email case-sensitivity off)', function(done) {
|
||||||
User.settings.caseSensitiveEmail = false;
|
User.settings.caseSensitiveEmail = false;
|
||||||
User.create({email: 'F@b.com', password: 'bar'}, function(err, user) {
|
User.create({ email: 'F@b.com', password: 'bar' }, function(err, user) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
assert(user.id);
|
assert(user.id);
|
||||||
assert.equal(user.email, user.email.toLowerCase());
|
assert.equal(user.email, user.email.toLowerCase());
|
||||||
|
@ -79,7 +80,7 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Create a new user (email case-sensitive)', function(done) {
|
it('Create a new user (email case-sensitive)', function(done) {
|
||||||
User.create({email: 'F@b.com', password: 'bar'}, function(err, user) {
|
User.create({ email: 'F@b.com', password: 'bar' }, function(err, user) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
assert(user.id);
|
assert(user.id);
|
||||||
assert(user.email);
|
assert(user.email);
|
||||||
|
@ -89,30 +90,30 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('credentials/challenges are object types', function(done) {
|
it('credentials/challenges are object types', function(done) {
|
||||||
User.create({email: 'f1@b.com', password: 'bar1',
|
User.create({ email: 'f1@b.com', password: 'bar1',
|
||||||
credentials: {cert: 'xxxxx', key: '111'},
|
credentials: { cert: 'xxxxx', key: '111' },
|
||||||
challenges: {x: 'X', a: 1}
|
challenges: { x: 'X', a: 1 },
|
||||||
}, function(err, user) {
|
}, function(err, user) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
User.findById(user.id, function(err, user) {
|
User.findById(user.id, function(err, user) {
|
||||||
assert(user.id);
|
assert(user.id);
|
||||||
assert(user.email);
|
assert(user.email);
|
||||||
assert.deepEqual(user.credentials, {cert: 'xxxxx', key: '111'});
|
assert.deepEqual(user.credentials, { cert: 'xxxxx', key: '111' });
|
||||||
assert.deepEqual(user.challenges, {x: 'X', a: 1});
|
assert.deepEqual(user.challenges, { x: 'X', a: 1 });
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Email is required', function(done) {
|
it('Email is required', function(done) {
|
||||||
User.create({password: '123'}, function(err) {
|
User.create({ password: '123' }, function(err) {
|
||||||
assert(err);
|
assert(err);
|
||||||
assert.equal(err.name, 'ValidationError');
|
assert.equal(err.name, 'ValidationError');
|
||||||
assert.equal(err.statusCode, 422);
|
assert.equal(err.statusCode, 422);
|
||||||
assert.equal(err.details.context, User.modelName);
|
assert.equal(err.details.context, User.modelName);
|
||||||
assert.deepEqual(err.details.codes.email, [
|
assert.deepEqual(err.details.codes.email, [
|
||||||
'presence',
|
'presence',
|
||||||
'format.null'
|
'format.null',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
|
@ -121,24 +122,24 @@ describe('User', function() {
|
||||||
|
|
||||||
// will change in future versions where password will be optional by default
|
// will change in future versions where password will be optional by default
|
||||||
it('Password is required', function(done) {
|
it('Password is required', function(done) {
|
||||||
var u = new User({email: '123@456.com'});
|
var u = new User({ email: '123@456.com' });
|
||||||
|
|
||||||
User.create({email: 'c@d.com'}, function(err) {
|
User.create({ email: 'c@d.com' }, function(err) {
|
||||||
assert(err);
|
assert(err);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Requires a valid email', function(done) {
|
it('Requires a valid email', function(done) {
|
||||||
User.create({email: 'foo@', password: '123'}, function(err) {
|
User.create({ email: 'foo@', password: '123' }, function(err) {
|
||||||
assert(err);
|
assert(err);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Requires a unique email', function(done) {
|
it('Requires a unique email', function(done) {
|
||||||
User.create({email: 'a@b.com', password: 'foobar'}, function() {
|
User.create({ email: 'a@b.com', password: 'foobar' }, function() {
|
||||||
User.create({email: 'a@b.com', password: 'batbaz'}, function(err) {
|
User.create({ email: 'a@b.com', password: 'batbaz' }, function(err) {
|
||||||
assert(err, 'should error because the email is not unique!');
|
assert(err, 'should error because the email is not unique!');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -147,9 +148,9 @@ describe('User', function() {
|
||||||
|
|
||||||
it('Requires a unique email (email case-sensitivity off)', function(done) {
|
it('Requires a unique email (email case-sensitivity off)', function(done) {
|
||||||
User.settings.caseSensitiveEmail = false;
|
User.settings.caseSensitiveEmail = false;
|
||||||
User.create({email: 'A@b.com', password: 'foobar'}, function(err) {
|
User.create({ email: 'A@b.com', password: 'foobar' }, function(err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
User.create({email: 'a@b.com', password: 'batbaz'}, function(err) {
|
User.create({ email: 'a@b.com', password: 'batbaz' }, function(err) {
|
||||||
assert(err, 'should error because the email is not unique!');
|
assert(err, 'should error because the email is not unique!');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -157,8 +158,8 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Requires a unique email (email case-sensitive)', function(done) {
|
it('Requires a unique email (email case-sensitive)', function(done) {
|
||||||
User.create({email: 'A@b.com', password: 'foobar'}, function(err, user1) {
|
User.create({ email: 'A@b.com', password: 'foobar' }, function(err, user1) {
|
||||||
User.create({email: 'a@b.com', password: 'batbaz'}, function(err, user2) {
|
User.create({ email: 'a@b.com', password: 'batbaz' }, function(err, user2) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
assert.notEqual(user1.email, user2.email);
|
assert.notEqual(user1.email, user2.email);
|
||||||
done();
|
done();
|
||||||
|
@ -167,8 +168,8 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Requires a unique username', function(done) {
|
it('Requires a unique username', function(done) {
|
||||||
User.create({email: 'a@b.com', username: 'abc', password: 'foobar'}, function() {
|
User.create({ email: 'a@b.com', username: 'abc', password: 'foobar' }, function() {
|
||||||
User.create({email: 'b@b.com', username: 'abc', password: 'batbaz'}, function(err) {
|
User.create({ email: 'b@b.com', username: 'abc', password: 'batbaz' }, function(err) {
|
||||||
assert(err, 'should error because the username is not unique!');
|
assert(err, 'should error because the username is not unique!');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -176,8 +177,8 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Requires a password to login with basic auth', function(done) {
|
it('Requires a password to login with basic auth', function(done) {
|
||||||
User.create({email: 'b@c.com'}, function(err) {
|
User.create({ email: 'b@c.com' }, function(err) {
|
||||||
User.login({email: 'b@c.com'}, function(err, accessToken) {
|
User.login({ email: 'b@c.com' }, function(err, accessToken) {
|
||||||
assert(!accessToken, 'should not create a accessToken without a valid password');
|
assert(!accessToken, 'should not create a accessToken without a valid password');
|
||||||
assert(err, 'should not login without a password');
|
assert(err, 'should not login without a password');
|
||||||
assert.equal(err.code, 'LOGIN_FAILED');
|
assert.equal(err.code, 'LOGIN_FAILED');
|
||||||
|
@ -187,20 +188,19 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Hashes the given password', function() {
|
it('Hashes the given password', function() {
|
||||||
var u = new User({username: 'foo', password: 'bar'});
|
var u = new User({ username: 'foo', password: 'bar' });
|
||||||
assert(u.password !== 'bar');
|
assert(u.password !== 'bar');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not hash the password if it\'s already hashed', function() {
|
it('does not hash the password if it\'s already hashed', function() {
|
||||||
var u1 = new User({username: 'foo', password: 'bar'});
|
var u1 = new User({ username: 'foo', password: 'bar' });
|
||||||
assert(u1.password !== 'bar');
|
assert(u1.password !== 'bar');
|
||||||
var u2 = new User({username: 'foo', password: u1.password});
|
var u2 = new User({ username: 'foo', password: u1.password });
|
||||||
assert(u2.password === u1.password);
|
assert(u2.password === u1.password);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('custom password hash', function() {
|
describe('custom password hash', function() {
|
||||||
var defaultHashPassword;
|
var defaultHashPassword, defaultValidatePassword;
|
||||||
var defaultValidatePassword;
|
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
defaultHashPassword = User.hashPassword;
|
defaultHashPassword = User.hashPassword;
|
||||||
|
@ -225,7 +225,7 @@ describe('User', function() {
|
||||||
|
|
||||||
it('Reports invalid password', function() {
|
it('Reports invalid password', function() {
|
||||||
try {
|
try {
|
||||||
var u = new User({username: 'foo', password: 'aa'});
|
var u = new User({ username: 'foo', password: 'aa' });
|
||||||
assert(false, 'Error should have been thrown');
|
assert(false, 'Error should have been thrown');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Ignore
|
// Ignore
|
||||||
|
@ -233,7 +233,7 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Hashes the given password', function() {
|
it('Hashes the given password', function() {
|
||||||
var u = new User({username: 'foo', password: 'bar'});
|
var u = new User({ username: 'foo', password: 'bar' });
|
||||||
assert(u.password === 'BAR');
|
assert(u.password === 'BAR');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -264,7 +264,7 @@ describe('User', function() {
|
||||||
|
|
||||||
it('Should be able to find lowercase email with mixed-case email query', function(done) {
|
it('Should be able to find lowercase email with mixed-case email query', function(done) {
|
||||||
User.settings.caseSensitiveEmail = false;
|
User.settings.caseSensitiveEmail = false;
|
||||||
User.find({where:{email: validMixedCaseEmailCredentials.email}}, function(err, result) {
|
User.find({ where: { email: validMixedCaseEmailCredentials.email }}, function(err, result) {
|
||||||
if (err) done(err);
|
if (err) done(err);
|
||||||
assert(result[0], 'The query did not find the user');
|
assert(result[0], 'The query did not find the user');
|
||||||
assert.equal(result[0].email, validCredentialsEmail);
|
assert.equal(result[0].email, validCredentialsEmail);
|
||||||
|
@ -355,7 +355,7 @@ describe('User', function() {
|
||||||
// Override createAccessToken
|
// Override createAccessToken
|
||||||
User.prototype.createAccessToken = function(ttl, cb) {
|
User.prototype.createAccessToken = function(ttl, cb) {
|
||||||
// Reduce the ttl by half for testing purpose
|
// Reduce the ttl by half for testing purpose
|
||||||
this.accessTokens.create({ttl: ttl / 2 }, cb);
|
this.accessTokens.create({ ttl: ttl / 2 }, cb);
|
||||||
};
|
};
|
||||||
User.login(validCredentialsWithTTL, function(err, accessToken) {
|
User.login(validCredentialsWithTTL, function(err, accessToken) {
|
||||||
assert(accessToken.userId);
|
assert(accessToken.userId);
|
||||||
|
@ -383,7 +383,7 @@ describe('User', function() {
|
||||||
// Override createAccessToken
|
// Override createAccessToken
|
||||||
User.prototype.createAccessToken = function(ttl, options, cb) {
|
User.prototype.createAccessToken = function(ttl, options, cb) {
|
||||||
// Reduce the ttl by half for testing purpose
|
// Reduce the ttl by half for testing purpose
|
||||||
this.accessTokens.create({ttl: ttl / 2, scopes: options.scope}, cb);
|
this.accessTokens.create({ ttl: ttl / 2, scopes: options.scope }, cb);
|
||||||
};
|
};
|
||||||
User.login(validCredentialsWithTTLAndScope, function(err, accessToken) {
|
User.login(validCredentialsWithTTLAndScope, function(err, accessToken) {
|
||||||
assert(accessToken.userId);
|
assert(accessToken.userId);
|
||||||
|
@ -393,7 +393,7 @@ describe('User', function() {
|
||||||
assert.equal(accessToken.scopes, 'all');
|
assert.equal(accessToken.scopes, 'all');
|
||||||
|
|
||||||
User.findById(accessToken.userId, function(err, user) {
|
User.findById(accessToken.userId, function(err, user) {
|
||||||
user.createAccessToken(120, {scope: 'default'}, function(err, accessToken) {
|
user.createAccessToken(120, { scope: 'default' }, function(err, accessToken) {
|
||||||
assert(accessToken.userId);
|
assert(accessToken.userId);
|
||||||
assert(accessToken.id);
|
assert(accessToken.id);
|
||||||
assert.equal(accessToken.ttl, 60);
|
assert.equal(accessToken.ttl, 60);
|
||||||
|
@ -575,22 +575,26 @@ describe('User', function() {
|
||||||
it('Require valid and complete credentials for email verification error', function(done) {
|
it('Require valid and complete credentials for email verification error', function(done) {
|
||||||
User.login({ email: validCredentialsEmail }, function(err, accessToken) {
|
User.login({ email: validCredentialsEmail }, function(err, accessToken) {
|
||||||
// strongloop/loopback#931
|
// strongloop/loopback#931
|
||||||
// error message should be "login failed" and not "login failed as the email has not been verified"
|
// error message should be "login failed"
|
||||||
assert(err && !/verified/.test(err.message), ('expecting "login failed" error message, received: "' + err.message + '"'));
|
// and not "login failed as the email has not been verified"
|
||||||
|
assert(err && !/verified/.test(err.message),
|
||||||
|
'expecting "login failed" error message, received: "' + err.message + '"');
|
||||||
assert.equal(err.code, 'LOGIN_FAILED');
|
assert.equal(err.code, 'LOGIN_FAILED');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Require valid and complete credentials for email verification error - promise variant', function(done) {
|
it('Require valid and complete credentials for email verification error - promise variant',
|
||||||
|
function(done) {
|
||||||
User.login({ email: validCredentialsEmail })
|
User.login({ email: validCredentialsEmail })
|
||||||
.then(function(accessToken) {
|
.then(function(accessToken) {
|
||||||
done();
|
done();
|
||||||
})
|
})
|
||||||
.catch(function(err) {
|
.catch(function(err) {
|
||||||
// strongloop/loopback#931
|
// strongloop/loopback#931
|
||||||
// error message should be "login failed" and not "login failed as the email has not been verified"
|
// error message should be "login failed" and not "login failed as the email has not been verified"
|
||||||
assert(err && !/verified/.test(err.message), ('expecting "login failed" error message, received: "' + err.message + '"'));
|
assert(err && !/verified/.test(err.message),
|
||||||
|
'expecting "login failed" error message, received: "' + err.message + '"');
|
||||||
assert.equal(err.code, 'LOGIN_FAILED');
|
assert.equal(err.code, 'LOGIN_FAILED');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -653,7 +657,9 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Login a user over REST require complete and valid credentials for email verification error message', function(done) {
|
it('Login user over REST require complete and valid credentials ' +
|
||||||
|
'for email verification error message',
|
||||||
|
function(done) {
|
||||||
request(app)
|
request(app)
|
||||||
.post('/test-users/login')
|
.post('/test-users/login')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
|
@ -664,9 +670,11 @@ describe('User', function() {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
// strongloop/loopback#931
|
// strongloop/loopback#931
|
||||||
// error message should be "login failed" and not "login failed as the email has not been verified"
|
// error message should be "login failed"
|
||||||
|
// and not "login failed as the email has not been verified"
|
||||||
var errorResponse = res.body.error;
|
var errorResponse = res.body.error;
|
||||||
assert(errorResponse && !/verified/.test(errorResponse.message), ('expecting "login failed" error message, received: "' + errorResponse.message + '"'));
|
assert(errorResponse && !/verified/.test(errorResponse.message),
|
||||||
|
'expecting "login failed" error message, received: "' + errorResponse.message + '"');
|
||||||
assert.equal(errorResponse.code, 'LOGIN_FAILED');
|
assert.equal(errorResponse.code, 'LOGIN_FAILED');
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -687,23 +695,21 @@ describe('User', function() {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('User.login requiring realm', function() {
|
describe('User.login requiring realm', function() {
|
||||||
var User;
|
var User, AccessToken;
|
||||||
var AccessToken;
|
|
||||||
|
|
||||||
before(function() {
|
before(function() {
|
||||||
User = loopback.User.extend('RealmUser', {},
|
User = loopback.User.extend('RealmUser', {},
|
||||||
{realmRequired: true, realmDelimiter: ':'});
|
{ realmRequired: true, realmDelimiter: ':' });
|
||||||
AccessToken = loopback.AccessToken.extend('RealmAccessToken');
|
AccessToken = loopback.AccessToken.extend('RealmAccessToken');
|
||||||
|
|
||||||
loopback.autoAttach();
|
loopback.autoAttach();
|
||||||
|
|
||||||
// Update the AccessToken relation to use the subclass of User
|
// Update the AccessToken relation to use the subclass of User
|
||||||
AccessToken.belongsTo(User, {as: 'user', foreignKey: 'userId'});
|
AccessToken.belongsTo(User, { as: 'user', foreignKey: 'userId' });
|
||||||
User.hasMany(AccessToken, {as: 'accessTokens', foreignKey: 'userId'});
|
User.hasMany(AccessToken, { as: 'accessTokens', foreignKey: 'userId' });
|
||||||
|
|
||||||
// allow many User.afterRemote's to be called
|
// allow many User.afterRemote's to be called
|
||||||
User.setMaxListeners(0);
|
User.setMaxListeners(0);
|
||||||
|
@ -713,53 +719,53 @@ describe('User', function() {
|
||||||
realm: 'realm1',
|
realm: 'realm1',
|
||||||
username: 'foo100',
|
username: 'foo100',
|
||||||
email: 'foo100@bar.com',
|
email: 'foo100@bar.com',
|
||||||
password: 'pass100'
|
password: 'pass100',
|
||||||
};
|
};
|
||||||
|
|
||||||
var realm2User = {
|
var realm2User = {
|
||||||
realm: 'realm2',
|
realm: 'realm2',
|
||||||
username: 'foo100',
|
username: 'foo100',
|
||||||
email: 'foo100@bar.com',
|
email: 'foo100@bar.com',
|
||||||
password: 'pass200'
|
password: 'pass200',
|
||||||
};
|
};
|
||||||
|
|
||||||
var credentialWithoutRealm = {
|
var credentialWithoutRealm = {
|
||||||
username: 'foo100',
|
username: 'foo100',
|
||||||
email: 'foo100@bar.com',
|
email: 'foo100@bar.com',
|
||||||
password: 'pass100'
|
password: 'pass100',
|
||||||
};
|
};
|
||||||
|
|
||||||
var credentialWithBadPass = {
|
var credentialWithBadPass = {
|
||||||
realm: 'realm1',
|
realm: 'realm1',
|
||||||
username: 'foo100',
|
username: 'foo100',
|
||||||
email: 'foo100@bar.com',
|
email: 'foo100@bar.com',
|
||||||
password: 'pass001'
|
password: 'pass001',
|
||||||
};
|
};
|
||||||
|
|
||||||
var credentialWithBadRealm = {
|
var credentialWithBadRealm = {
|
||||||
realm: 'realm3',
|
realm: 'realm3',
|
||||||
username: 'foo100',
|
username: 'foo100',
|
||||||
email: 'foo100@bar.com',
|
email: 'foo100@bar.com',
|
||||||
password: 'pass100'
|
password: 'pass100',
|
||||||
};
|
};
|
||||||
|
|
||||||
var credentialWithRealm = {
|
var credentialWithRealm = {
|
||||||
realm: 'realm1',
|
realm: 'realm1',
|
||||||
username: 'foo100',
|
username: 'foo100',
|
||||||
password: 'pass100'
|
password: 'pass100',
|
||||||
};
|
};
|
||||||
|
|
||||||
var credentialRealmInUsername = {
|
var credentialRealmInUsername = {
|
||||||
username: 'realm1:foo100',
|
username: 'realm1:foo100',
|
||||||
password: 'pass100'
|
password: 'pass100',
|
||||||
};
|
};
|
||||||
|
|
||||||
var credentialRealmInEmail = {
|
var credentialRealmInEmail = {
|
||||||
email: 'realm1:foo100@bar.com',
|
email: 'realm1:foo100@bar.com',
|
||||||
password: 'pass100'
|
password: 'pass100',
|
||||||
};
|
};
|
||||||
|
|
||||||
var user1;
|
var user1 = null;
|
||||||
beforeEach(function(done) {
|
beforeEach(function(done) {
|
||||||
User.create(realm1User, function(err, u) {
|
User.create(realm1User, function(err, u) {
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -771,11 +777,11 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(function(done) {
|
afterEach(function(done) {
|
||||||
User.deleteAll({realm: 'realm1'}, function(err) {
|
User.deleteAll({ realm: 'realm1' }, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
}
|
}
|
||||||
User.deleteAll({realm: 'realm2'}, done);
|
User.deleteAll({ realm: 'realm2' }, done);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -860,7 +866,7 @@ describe('User', function() {
|
||||||
login(logout);
|
login(logout);
|
||||||
|
|
||||||
function login(fn) {
|
function login(fn) {
|
||||||
User.login({email: 'foo@bar.com', password: 'bar'}, fn);
|
User.login({ email: 'foo@bar.com', password: 'bar' }, fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
function logout(err, accessToken) {
|
function logout(err, accessToken) {
|
||||||
|
@ -868,11 +874,12 @@ describe('User', function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Logout a user by providing the current accessToken id (using node) - promise variant', function(done) {
|
it('Logout a user by providing the current accessToken id (using node) - promise variant',
|
||||||
|
function(done) {
|
||||||
login(logout);
|
login(logout);
|
||||||
|
|
||||||
function login(fn) {
|
function login(fn) {
|
||||||
User.login({email: 'foo@bar.com', password: 'bar'}, fn);
|
User.login({ email: 'foo@bar.com', password: 'bar' }, fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
function logout(err, accessToken) {
|
function logout(err, accessToken) {
|
||||||
|
@ -891,7 +898,7 @@ describe('User', function() {
|
||||||
.post('/test-users/login')
|
.post('/test-users/login')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.send({email: 'foo@bar.com', password: 'bar'})
|
.send({ email: 'foo@bar.com', password: 'bar' })
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
|
@ -932,7 +939,7 @@ describe('User', function() {
|
||||||
|
|
||||||
describe('user.hasPassword(plain, fn)', function() {
|
describe('user.hasPassword(plain, fn)', function() {
|
||||||
it('Determine if the password matches the stored password', function(done) {
|
it('Determine if the password matches the stored password', function(done) {
|
||||||
var u = new User({username: 'foo', password: 'bar'});
|
var u = new User({ username: 'foo', password: 'bar' });
|
||||||
u.hasPassword('bar', function(err, isMatch) {
|
u.hasPassword('bar', function(err, isMatch) {
|
||||||
assert(isMatch, 'password doesnt match');
|
assert(isMatch, 'password doesnt match');
|
||||||
done();
|
done();
|
||||||
|
@ -940,7 +947,7 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Determine if the password matches the stored password - promise variant', function(done) {
|
it('Determine if the password matches the stored password - promise variant', function(done) {
|
||||||
var u = new User({username: 'foo', password: 'bar'});
|
var u = new User({ username: 'foo', password: 'bar' });
|
||||||
u.hasPassword('bar')
|
u.hasPassword('bar')
|
||||||
.then(function(isMatch) {
|
.then(function(isMatch) {
|
||||||
assert(isMatch, 'password doesnt match');
|
assert(isMatch, 'password doesnt match');
|
||||||
|
@ -952,7 +959,7 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should match a password when saved', function(done) {
|
it('should match a password when saved', function(done) {
|
||||||
var u = new User({username: 'a', password: 'b', email: 'z@z.net'});
|
var u = new User({ username: 'a', password: 'b', email: 'z@z.net' });
|
||||||
|
|
||||||
u.save(function(err, user) {
|
u.save(function(err, user) {
|
||||||
User.findById(user.id, function(err, uu) {
|
User.findById(user.id, function(err, uu) {
|
||||||
|
@ -965,7 +972,7 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should match a password after it is changed', function(done) {
|
it('should match a password after it is changed', function(done) {
|
||||||
User.create({email: 'foo@baz.net', username: 'bat', password: 'baz'}, function(err, user) {
|
User.create({ email: 'foo@baz.net', username: 'bat', password: 'baz' }, function(err, user) {
|
||||||
User.findById(user.id, function(err, foundUser) {
|
User.findById(user.id, function(err, foundUser) {
|
||||||
assert(foundUser);
|
assert(foundUser);
|
||||||
foundUser.hasPassword('baz', function(err, isMatch) {
|
foundUser.hasPassword('baz', function(err, isMatch) {
|
||||||
|
@ -989,7 +996,6 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Verification', function() {
|
describe('Verification', function() {
|
||||||
|
|
||||||
describe('user.verify(options, fn)', function() {
|
describe('user.verify(options, fn)', function() {
|
||||||
it('Verify a user\'s email address', function(done) {
|
it('Verify a user\'s email address', function(done) {
|
||||||
User.afterRemote('create', function(ctx, user, next) {
|
User.afterRemote('create', function(ctx, user, next) {
|
||||||
|
@ -1001,7 +1007,7 @@ describe('User', function() {
|
||||||
from: 'noreply@myapp.org',
|
from: 'noreply@myapp.org',
|
||||||
redirect: '/',
|
redirect: '/',
|
||||||
protocol: ctx.req.protocol,
|
protocol: ctx.req.protocol,
|
||||||
host: ctx.req.get('host')
|
host: ctx.req.get('host'),
|
||||||
};
|
};
|
||||||
|
|
||||||
user.verify(options, function(err, result) {
|
user.verify(options, function(err, result) {
|
||||||
|
@ -1019,7 +1025,7 @@ describe('User', function() {
|
||||||
.post('/test-users')
|
.post('/test-users')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.send({email: 'bar@bat.com', password: 'bar'})
|
.send({ email: 'bar@bat.com', password: 'bar' })
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
|
@ -1037,7 +1043,7 @@ describe('User', function() {
|
||||||
from: 'noreply@myapp.org',
|
from: 'noreply@myapp.org',
|
||||||
redirect: '/',
|
redirect: '/',
|
||||||
protocol: ctx.req.protocol,
|
protocol: ctx.req.protocol,
|
||||||
host: ctx.req.get('host')
|
host: ctx.req.get('host'),
|
||||||
};
|
};
|
||||||
|
|
||||||
user.verify(options)
|
user.verify(options)
|
||||||
|
@ -1057,7 +1063,7 @@ describe('User', function() {
|
||||||
|
|
||||||
request(app)
|
request(app)
|
||||||
.post('/test-users')
|
.post('/test-users')
|
||||||
.send({email: 'bar@bat.com', password: 'bar'})
|
.send({ email: 'bar@bat.com', password: 'bar' })
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
|
@ -1078,7 +1084,7 @@ describe('User', function() {
|
||||||
redirect: '/',
|
redirect: '/',
|
||||||
protocol: ctx.req.protocol,
|
protocol: ctx.req.protocol,
|
||||||
host: ctx.req.get('host'),
|
host: ctx.req.get('host'),
|
||||||
headers: {'message-id':'custom-header-value'}
|
headers: { 'message-id': 'custom-header-value' },
|
||||||
};
|
};
|
||||||
|
|
||||||
user.verify(options, function(err, result) {
|
user.verify(options, function(err, result) {
|
||||||
|
@ -1092,7 +1098,7 @@ describe('User', function() {
|
||||||
.post('/test-users')
|
.post('/test-users')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.send({email: 'bar@bat.com', password: 'bar'})
|
.send({ email: 'bar@bat.com', password: 'bar' })
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
|
@ -1120,7 +1126,7 @@ describe('User', function() {
|
||||||
process.nextTick(function() {
|
process.nextTick(function() {
|
||||||
cb(null, 'token-123456');
|
cb(null, 'token-123456');
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
user.verify(options, function(err, result) {
|
user.verify(options, function(err, result) {
|
||||||
|
@ -1138,7 +1144,7 @@ describe('User', function() {
|
||||||
.post('/test-users')
|
.post('/test-users')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.send({email: 'bar@bat.com', password: 'bar'})
|
.send({ email: 'bar@bat.com', password: 'bar' })
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
|
@ -1162,7 +1168,7 @@ describe('User', function() {
|
||||||
process.nextTick(function() {
|
process.nextTick(function() {
|
||||||
cb(new Error('Fake error'));
|
cb(new Error('Fake error'));
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
user.verify(options, function(err, result) {
|
user.verify(options, function(err, result) {
|
||||||
|
@ -1177,7 +1183,7 @@ describe('User', function() {
|
||||||
.post('/test-users')
|
.post('/test-users')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.send({email: 'bar@bat.com', password: 'bar'})
|
.send({ email: 'bar@bat.com', password: 'bar' })
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
|
@ -1197,7 +1203,7 @@ describe('User', function() {
|
||||||
redirect: '/',
|
redirect: '/',
|
||||||
protocol: 'http',
|
protocol: 'http',
|
||||||
host: 'myapp.org',
|
host: 'myapp.org',
|
||||||
port: 3000
|
port: 3000,
|
||||||
};
|
};
|
||||||
|
|
||||||
user.verify(options, function(err, result) {
|
user.verify(options, function(err, result) {
|
||||||
|
@ -1211,7 +1217,7 @@ describe('User', function() {
|
||||||
.post('/test-users')
|
.post('/test-users')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.send({email: 'bar@bat.com', password: 'bar'})
|
.send({ email: 'bar@bat.com', password: 'bar' })
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
|
@ -1230,7 +1236,7 @@ describe('User', function() {
|
||||||
redirect: '/',
|
redirect: '/',
|
||||||
protocol: 'http',
|
protocol: 'http',
|
||||||
host: 'myapp.org',
|
host: 'myapp.org',
|
||||||
port: 80
|
port: 80,
|
||||||
};
|
};
|
||||||
|
|
||||||
user.verify(options, function(err, result) {
|
user.verify(options, function(err, result) {
|
||||||
|
@ -1244,7 +1250,7 @@ describe('User', function() {
|
||||||
.post('/test-users')
|
.post('/test-users')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.send({email: 'bar@bat.com', password: 'bar'})
|
.send({ email: 'bar@bat.com', password: 'bar' })
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
|
@ -1263,7 +1269,7 @@ describe('User', function() {
|
||||||
redirect: '/',
|
redirect: '/',
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
host: 'myapp.org',
|
host: 'myapp.org',
|
||||||
port: 3000
|
port: 3000,
|
||||||
};
|
};
|
||||||
|
|
||||||
user.verify(options, function(err, result) {
|
user.verify(options, function(err, result) {
|
||||||
|
@ -1277,7 +1283,7 @@ describe('User', function() {
|
||||||
.post('/test-users')
|
.post('/test-users')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.send({email: 'bar@bat.com', password: 'bar'})
|
.send({ email: 'bar@bat.com', password: 'bar' })
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
|
@ -1296,7 +1302,7 @@ describe('User', function() {
|
||||||
redirect: '/',
|
redirect: '/',
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
host: 'myapp.org',
|
host: 'myapp.org',
|
||||||
port: 443
|
port: 443,
|
||||||
};
|
};
|
||||||
|
|
||||||
user.verify(options, function(err, result) {
|
user.verify(options, function(err, result) {
|
||||||
|
@ -1310,7 +1316,7 @@ describe('User', function() {
|
||||||
.post('/test-users')
|
.post('/test-users')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.send({email: 'bar@bat.com', password: 'bar'})
|
.send({ email: 'bar@bat.com', password: 'bar' })
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
|
@ -1320,7 +1326,11 @@ describe('User', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hide verification tokens from user JSON', function(done) {
|
it('should hide verification tokens from user JSON', function(done) {
|
||||||
var user = new User({email: 'bar@bat.com', password: 'bar', verificationToken: 'a-token' });
|
var user = new User({
|
||||||
|
email: 'bar@bat.com',
|
||||||
|
password: 'bar',
|
||||||
|
verificationToken: 'a-token',
|
||||||
|
});
|
||||||
var data = user.toJSON();
|
var data = user.toJSON();
|
||||||
assert(!('verificationToken' in data));
|
assert(!('verificationToken' in data));
|
||||||
done();
|
done();
|
||||||
|
@ -1340,7 +1350,7 @@ describe('User', function() {
|
||||||
from: 'noreply@myapp.org',
|
from: 'noreply@myapp.org',
|
||||||
redirect: 'http://foo.com/bar',
|
redirect: 'http://foo.com/bar',
|
||||||
protocol: ctx.req.protocol,
|
protocol: ctx.req.protocol,
|
||||||
host: ctx.req.get('host')
|
host: ctx.req.get('host'),
|
||||||
};
|
};
|
||||||
|
|
||||||
user.verify(options, function(err, result) {
|
user.verify(options, function(err, result) {
|
||||||
|
@ -1355,7 +1365,7 @@ describe('User', function() {
|
||||||
.post('/test-users')
|
.post('/test-users')
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(302)
|
.expect(302)
|
||||||
.send({email: 'bar@bat.com', password: 'bar'})
|
.send({ email: 'bar@bat.com', password: 'bar' })
|
||||||
.end(function(err, res) {
|
.end(function(err, res) {
|
||||||
if (err) {
|
if (err) {
|
||||||
return done(err);
|
return done(err);
|
||||||
|
@ -1478,7 +1488,7 @@ describe('User', function() {
|
||||||
var calledBack = false;
|
var calledBack = false;
|
||||||
|
|
||||||
User.resetPassword({
|
User.resetPassword({
|
||||||
email: email
|
email: email,
|
||||||
}, function() {
|
}, function() {
|
||||||
calledBack = true;
|
calledBack = true;
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,11 +7,8 @@ var PersistedModel = loopback.PersistedModel;
|
||||||
var RemoteObjects = require('strong-remoting');
|
var RemoteObjects = require('strong-remoting');
|
||||||
|
|
||||||
module.exports = function defineModelTestsWithDataSource(options) {
|
module.exports = function defineModelTestsWithDataSource(options) {
|
||||||
|
|
||||||
describe('Model Tests', function() {
|
describe('Model Tests', function() {
|
||||||
|
var User, dataSource;
|
||||||
var User;
|
|
||||||
var dataSource;
|
|
||||||
|
|
||||||
if (options.beforeEach) {
|
if (options.beforeEach) {
|
||||||
beforeEach(options.beforeEach);
|
beforeEach(options.beforeEach);
|
||||||
|
@ -44,9 +41,9 @@ module.exports = function defineModelTestsWithDataSource(options) {
|
||||||
'password': String,
|
'password': String,
|
||||||
'gender': String,
|
'gender': String,
|
||||||
'domain': String,
|
'domain': String,
|
||||||
'email': String
|
'email': String,
|
||||||
}, {
|
}, {
|
||||||
trackChanges: true
|
trackChanges: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
User.attachTo(dataSource);
|
User.attachTo(dataSource);
|
||||||
|
@ -59,7 +56,7 @@ module.exports = function defineModelTestsWithDataSource(options) {
|
||||||
describe('Model.validatesPresenceOf(properties...)', function() {
|
describe('Model.validatesPresenceOf(properties...)', function() {
|
||||||
it('Require a model to include a property to be considered valid', function() {
|
it('Require a model to include a property to be considered valid', function() {
|
||||||
User.validatesPresenceOf('first', 'last', 'age');
|
User.validatesPresenceOf('first', 'last', 'age');
|
||||||
var joe = new User({first: 'joe'});
|
var joe = new User({ first: 'joe' });
|
||||||
assert(joe.isValid() === false, 'model should not validate');
|
assert(joe.isValid() === false, 'model should not validate');
|
||||||
assert(joe.errors.last, 'should have a missing last error');
|
assert(joe.errors.last, 'should have a missing last error');
|
||||||
assert(joe.errors.age, 'should have a missing age error');
|
assert(joe.errors.age, 'should have a missing age error');
|
||||||
|
@ -68,8 +65,8 @@ module.exports = function defineModelTestsWithDataSource(options) {
|
||||||
|
|
||||||
describe('Model.validatesLengthOf(property, options)', function() {
|
describe('Model.validatesLengthOf(property, options)', function() {
|
||||||
it('Require a property length to be within a specified range', function() {
|
it('Require a property length to be within a specified range', function() {
|
||||||
User.validatesLengthOf('password', {min: 5, message: {min: 'Password is too short'}});
|
User.validatesLengthOf('password', { min: 5, message: { min: 'Password is too short' }});
|
||||||
var joe = new User({password: '1234'});
|
var joe = new User({ password: '1234' });
|
||||||
assert(joe.isValid() === false, 'model should not be valid');
|
assert(joe.isValid() === false, 'model should not be valid');
|
||||||
assert(joe.errors.password, 'should have password error');
|
assert(joe.errors.password, 'should have password error');
|
||||||
});
|
});
|
||||||
|
@ -77,8 +74,8 @@ module.exports = function defineModelTestsWithDataSource(options) {
|
||||||
|
|
||||||
describe('Model.validatesInclusionOf(property, options)', function() {
|
describe('Model.validatesInclusionOf(property, options)', function() {
|
||||||
it('Require a value for `property` to be in the specified array', function() {
|
it('Require a value for `property` to be in the specified array', function() {
|
||||||
User.validatesInclusionOf('gender', {in: ['male', 'female']});
|
User.validatesInclusionOf('gender', { in: ['male', 'female'] });
|
||||||
var foo = new User({gender: 'bar'});
|
var foo = new User({ gender: 'bar' });
|
||||||
assert(foo.isValid() === false, 'model should not be valid');
|
assert(foo.isValid() === false, 'model should not be valid');
|
||||||
assert(foo.errors.gender, 'should have gender error');
|
assert(foo.errors.gender, 'should have gender error');
|
||||||
});
|
});
|
||||||
|
@ -86,10 +83,10 @@ module.exports = function defineModelTestsWithDataSource(options) {
|
||||||
|
|
||||||
describe('Model.validatesExclusionOf(property, options)', function() {
|
describe('Model.validatesExclusionOf(property, options)', function() {
|
||||||
it('Require a value for `property` to not exist in the specified array', function() {
|
it('Require a value for `property` to not exist in the specified array', function() {
|
||||||
User.validatesExclusionOf('domain', {in: ['www', 'billing', 'admin']});
|
User.validatesExclusionOf('domain', { in: ['www', 'billing', 'admin'] });
|
||||||
var foo = new User({domain: 'www'});
|
var foo = new User({ domain: 'www' });
|
||||||
var bar = new User({domain: 'billing'});
|
var bar = new User({ domain: 'billing' });
|
||||||
var bat = new User({domain: 'admin'});
|
var bat = new User({ domain: 'admin' });
|
||||||
assert(foo.isValid() === false);
|
assert(foo.isValid() === false);
|
||||||
assert(bar.isValid() === false);
|
assert(bar.isValid() === false);
|
||||||
assert(bat.isValid() === false);
|
assert(bat.isValid() === false);
|
||||||
|
@ -101,10 +98,10 @@ module.exports = function defineModelTestsWithDataSource(options) {
|
||||||
|
|
||||||
describe('Model.validatesNumericalityOf(property, options)', function() {
|
describe('Model.validatesNumericalityOf(property, options)', function() {
|
||||||
it('Require a value for `property` to be a specific type of `Number`', function() {
|
it('Require a value for `property` to be a specific type of `Number`', function() {
|
||||||
User.validatesNumericalityOf('age', {int: true});
|
User.validatesNumericalityOf('age', { int: true });
|
||||||
var joe = new User({age: 10.2});
|
var joe = new User({ age: 10.2 });
|
||||||
assert(joe.isValid() === false);
|
assert(joe.isValid() === false);
|
||||||
var bob = new User({age: 0});
|
var bob = new User({ age: 0 });
|
||||||
assert(bob.isValid() === true);
|
assert(bob.isValid() === true);
|
||||||
assert(joe.errors.age, 'model should have an age error');
|
assert(joe.errors.age, 'model should have an age error');
|
||||||
});
|
});
|
||||||
|
@ -112,16 +109,16 @@ module.exports = function defineModelTestsWithDataSource(options) {
|
||||||
|
|
||||||
describe('myModel.isValid()', function() {
|
describe('myModel.isValid()', function() {
|
||||||
it('Validate the model instance', function() {
|
it('Validate the model instance', function() {
|
||||||
User.validatesNumericalityOf('age', {int: true});
|
User.validatesNumericalityOf('age', { int: true });
|
||||||
var user = new User({first: 'joe', age: 'flarg'});
|
var user = new User({ first: 'joe', age: 'flarg' });
|
||||||
var valid = user.isValid();
|
var valid = user.isValid();
|
||||||
assert(valid === false);
|
assert(valid === false);
|
||||||
assert(user.errors.age, 'model should have age error');
|
assert(user.errors.age, 'model should have age error');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Asynchronously validate the model', function(done) {
|
it('Asynchronously validate the model', function(done) {
|
||||||
User.validatesNumericalityOf('age', {int: true});
|
User.validatesNumericalityOf('age', { int: true });
|
||||||
var user = new User({first: 'joe', age: 'flarg'});
|
var user = new User({ first: 'joe', age: 'flarg' });
|
||||||
user.isValid(function(valid) {
|
user.isValid(function(valid) {
|
||||||
assert(valid === false);
|
assert(valid === false);
|
||||||
assert(user.errors.age, 'model should have age error');
|
assert(user.errors.age, 'model should have age error');
|
||||||
|
@ -131,8 +128,9 @@ module.exports = function defineModelTestsWithDataSource(options) {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Model.create([data], [callback])', function() {
|
describe('Model.create([data], [callback])', function() {
|
||||||
it('Create an instance of Model with given data and save to the attached data source', function(done) {
|
it('Create an instance of Model with given data and save to the attached data source',
|
||||||
User.create({first: 'Joe', last: 'Bob'}, function(err, user) {
|
function(done) {
|
||||||
|
User.create({ first: 'Joe', last: 'Bob' }, function(err, user) {
|
||||||
assert(user instanceof User);
|
assert(user instanceof User);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
@ -141,7 +139,7 @@ module.exports = function defineModelTestsWithDataSource(options) {
|
||||||
|
|
||||||
describe('model.save([options], [callback])', function() {
|
describe('model.save([options], [callback])', function() {
|
||||||
it('Save an instance of a Model to the attached data source', function(done) {
|
it('Save an instance of a Model to the attached data source', function(done) {
|
||||||
var joe = new User({first: 'Joe', last: 'Bob'});
|
var joe = new User({ first: 'Joe', last: 'Bob' });
|
||||||
joe.save(function(err, user) {
|
joe.save(function(err, user) {
|
||||||
assert(user.id);
|
assert(user.id);
|
||||||
assert(!err);
|
assert(!err);
|
||||||
|
@ -153,13 +151,13 @@ module.exports = function defineModelTestsWithDataSource(options) {
|
||||||
|
|
||||||
describe('model.updateAttributes(data, [callback])', function() {
|
describe('model.updateAttributes(data, [callback])', function() {
|
||||||
it('Save specified attributes to the attached data source', function(done) {
|
it('Save specified attributes to the attached data source', function(done) {
|
||||||
User.create({first: 'joe', age: 100}, function(err, user) {
|
User.create({ first: 'joe', age: 100 }, function(err, user) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
assert.equal(user.first, 'joe');
|
assert.equal(user.first, 'joe');
|
||||||
|
|
||||||
user.updateAttributes({
|
user.updateAttributes({
|
||||||
first: 'updatedFirst',
|
first: 'updatedFirst',
|
||||||
last: 'updatedLast'
|
last: 'updatedLast',
|
||||||
}, function(err, updatedUser) {
|
}, function(err, updatedUser) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
assert.equal(updatedUser.first, 'updatedFirst');
|
assert.equal(updatedUser.first, 'updatedFirst');
|
||||||
|
@ -173,11 +171,11 @@ module.exports = function defineModelTestsWithDataSource(options) {
|
||||||
|
|
||||||
describe('Model.upsert(data, callback)', function() {
|
describe('Model.upsert(data, callback)', function() {
|
||||||
it('Update when record with id=data.id found, insert otherwise', function(done) {
|
it('Update when record with id=data.id found, insert otherwise', function(done) {
|
||||||
User.upsert({first: 'joe', id: 7}, function(err, user) {
|
User.upsert({ first: 'joe', id: 7 }, function(err, user) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
assert.equal(user.first, 'joe');
|
assert.equal(user.first, 'joe');
|
||||||
|
|
||||||
User.upsert({first: 'bob', id: 7}, function(err, updatedUser) {
|
User.upsert({ first: 'bob', id: 7 }, function(err, updatedUser) {
|
||||||
assert(!err);
|
assert(!err);
|
||||||
assert.equal(updatedUser.first, 'bob');
|
assert.equal(updatedUser.first, 'bob');
|
||||||
done();
|
done();
|
||||||
|
@ -188,13 +186,13 @@ module.exports = function defineModelTestsWithDataSource(options) {
|
||||||
|
|
||||||
describe('model.destroy([callback])', function() {
|
describe('model.destroy([callback])', function() {
|
||||||
it('Remove a model from the attached data source', function(done) {
|
it('Remove a model from the attached data source', function(done) {
|
||||||
User.create({first: 'joe', last: 'bob'}, function(err, user) {
|
User.create({ first: 'joe', last: 'bob' }, function(err, user) {
|
||||||
User.findById(user.id, function(err, foundUser) {
|
User.findById(user.id, function(err, foundUser) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
assert.equal(user.id, foundUser.id);
|
assert.equal(user.id, foundUser.id);
|
||||||
User.deleteById(foundUser.id, function(err) {
|
User.deleteById(foundUser.id, function(err) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
User.find({ where: { id: user.id } }, function(err, found) {
|
User.find({ where: { id: user.id }}, function(err, found) {
|
||||||
if (err) return done(err);
|
if (err) return done(err);
|
||||||
assert.equal(found.length, 0);
|
assert.equal(found.length, 0);
|
||||||
done();
|
done();
|
||||||
|
@ -207,7 +205,7 @@ module.exports = function defineModelTestsWithDataSource(options) {
|
||||||
|
|
||||||
describe('Model.deleteById(id, [callback])', function() {
|
describe('Model.deleteById(id, [callback])', function() {
|
||||||
it('Delete a model instance from the attached data source', function(done) {
|
it('Delete a model instance from the attached data source', function(done) {
|
||||||
User.create({first: 'joe', last: 'bob'}, function(err, user) {
|
User.create({ first: 'joe', last: 'bob' }, function(err, user) {
|
||||||
User.deleteById(user.id, function(err) {
|
User.deleteById(user.id, function(err) {
|
||||||
User.findById(user.id, function(err, notFound) {
|
User.findById(user.id, function(err, notFound) {
|
||||||
assert.equal(notFound, null);
|
assert.equal(notFound, null);
|
||||||
|
@ -220,7 +218,7 @@ module.exports = function defineModelTestsWithDataSource(options) {
|
||||||
|
|
||||||
describe('Model.findById(id, callback)', function() {
|
describe('Model.findById(id, callback)', function() {
|
||||||
it('Find an instance by id', function(done) {
|
it('Find an instance by id', function(done) {
|
||||||
User.create({first: 'michael', last: 'jordan', id: 23}, function() {
|
User.create({ first: 'michael', last: 'jordan', id: 23 }, function() {
|
||||||
User.findById(23, function(err, user) {
|
User.findById(23, function(err, user) {
|
||||||
assert.equal(user.id, 23);
|
assert.equal(user.id, 23);
|
||||||
assert.equal(user.first, 'michael');
|
assert.equal(user.first, 'michael');
|
||||||
|
@ -234,20 +232,18 @@ module.exports = function defineModelTestsWithDataSource(options) {
|
||||||
describe('Model.count([query], callback)', function() {
|
describe('Model.count([query], callback)', function() {
|
||||||
it('Query count of Model instances in data source', function(done) {
|
it('Query count of Model instances in data source', function(done) {
|
||||||
(new TaskEmitter())
|
(new TaskEmitter())
|
||||||
.task(User, 'create', {first: 'jill', age: 100})
|
.task(User, 'create', { first: 'jill', age: 100 })
|
||||||
.task(User, 'create', {first: 'bob', age: 200})
|
.task(User, 'create', { first: 'bob', age: 200 })
|
||||||
.task(User, 'create', {first: 'jan'})
|
.task(User, 'create', { first: 'jan' })
|
||||||
.task(User, 'create', {first: 'sam'})
|
.task(User, 'create', { first: 'sam' })
|
||||||
.task(User, 'create', {first: 'suzy'})
|
.task(User, 'create', { first: 'suzy' })
|
||||||
.on('done', function() {
|
.on('done', function() {
|
||||||
User.count({age: {gt: 99}}, function(err, count) {
|
User.count({ age: { gt: 99 }}, function(err, count) {
|
||||||
assert.equal(count, 2);
|
assert.equal(count, 2);
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue