Use eslint with loopback config
Drop jshint and jscs in favour of eslint. Fix style violations. While we are at this, reduce the max line length from 150 to 100.
This commit is contained in:
parent
2a86e9535b
commit
f9702b0ace
|
@ -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