2013-12-20 01:49:47 +00:00
/ * !
2013-07-15 18:32:00 +00:00
* Module Dependencies .
* /
2013-11-19 00:13:40 +00:00
var loopback = require ( '../loopback' ) ;
2014-01-22 10:22:23 +00:00
var compat = require ( '../compat' ) ;
2014-04-30 00:17:49 +00:00
var juggler = require ( 'loopback-datasource-juggler' ) ;
var ModelBuilder = juggler . ModelBuilder ;
var DataSource = juggler . DataSource ;
2013-07-15 18:32:00 +00:00
var modeler = new ModelBuilder ( ) ;
2014-01-28 20:54:41 +00:00
var async = require ( 'async' ) ;
2013-12-10 23:57:55 +00:00
var assert = require ( 'assert' ) ;
2014-04-30 00:17:49 +00:00
var _ = require ( 'underscore' ) ;
2013-07-15 18:32:00 +00:00
/ * *
2014-01-28 20:54:41 +00:00
* The base class for * * all models * * .
2013-12-20 01:49:47 +00:00
*
2014-01-28 20:54:41 +00:00
* * * Inheriting from ` Model ` * *
*
* ` ` ` js
* var properties = { ... } ;
* var options = { ... } ;
* var MyModel = loopback . Model . extend ( 'MyModel' , properties , options ) ;
* ` ` `
*
* * * Options * *
*
* - ` trackChanges ` - If true , changes to the model will be tracked . * * Required
* for replication . * *
*
* * * Events * *
*
* # # # # Event : ` changed `
*
* Emitted after a model has been successfully created , saved , or updated .
*
* ` ` ` js
* MyModel . on ( 'changed' , function ( inst ) {
* console . log ( 'model with id %s has been changed' , inst . id ) ;
* // => model with id 1 has been changed
* } ) ;
* ` ` `
*
* # # # # Event : ` deleted `
*
* Emitted after an individual model has been deleted .
*
* ` ` ` js
* MyModel . on ( 'deleted' , function ( inst ) {
* console . log ( 'model with id %s has been deleted' , inst . id ) ;
* // => model with id 1 has been deleted
* } ) ;
* ` ` `
*
* # # # # Event : ` deletedAll `
*
* Emitted after an individual model has been deleted .
*
* ` ` ` js
* MyModel . on ( 'deletedAll' , function ( where ) {
* if ( where ) {
* console . log ( 'all models where' , where , 'have been deleted' ) ;
* // => all models where
* // => {price: {gt: 100}}
* // => have been deleted
* }
* } ) ;
* ` ` `
*
* # # # # Event : ` attached `
*
* Emitted after a ` Model ` has been attached to an ` app ` .
*
* # # # # Event : ` dataSourceAttached `
*
* Emitted after a ` Model ` has been attached to a ` DataSource ` .
*
2013-12-20 01:49:47 +00:00
* @ class
* @ param { Object } data
2014-01-28 20:54:41 +00:00
* @ property { String } modelName The name of the model
* @ property { DataSource } dataSource
2013-07-15 18:32:00 +00:00
* /
2013-11-11 21:35:54 +00:00
var Model = module . exports = modeler . define ( 'Model' ) ;
2013-07-15 18:32:00 +00:00
/ * !
2013-07-15 21:07:17 +00:00
* Called when a model is extended .
2013-07-15 18:32:00 +00:00
* /
Model . setup = function ( ) {
var ModelCtor = this ;
2014-01-28 20:54:41 +00:00
var options = this . settings ;
2013-07-15 18:32:00 +00:00
2014-05-16 19:32:54 +00:00
ModelCtor . sharedClass = new SharedClass ( remoteNamespace , ModelCtor ) ;
2013-07-16 03:14:04 +00:00
ModelCtor . sharedCtor = function ( data , id , fn ) {
2013-07-16 02:45:27 +00:00
if ( typeof data === 'function' ) {
fn = data ;
data = null ;
id = null ;
} else if ( typeof id === 'function' ) {
fn = id ;
if ( typeof data !== 'object' ) {
id = data ;
data = null ;
} else {
id = null ;
}
}
if ( id && data ) {
2013-07-16 19:51:59 +00:00
var model = new ModelCtor ( data ) ;
2013-07-16 02:45:27 +00:00
model . id = id ;
fn ( null , model ) ;
} else if ( data ) {
2013-07-16 19:51:59 +00:00
fn ( null , new ModelCtor ( data ) ) ;
2013-07-16 02:45:27 +00:00
} else if ( id ) {
2013-08-17 05:11:58 +00:00
ModelCtor . findById ( id , function ( err , model ) {
2013-07-16 02:45:27 +00:00
if ( err ) {
fn ( err ) ;
} else if ( model ) {
fn ( null , model ) ;
} else {
err = new Error ( 'could not find a model with id ' + id ) ;
err . statusCode = 404 ;
fn ( err ) ;
}
} ) ;
} else {
fn ( new Error ( 'must specify an id or data' ) ) ;
}
}
// before remote hook
ModelCtor . beforeRemote = function ( name , fn ) {
var self = this ;
if ( this . app ) {
var remotes = this . app . remotes ( ) ;
2014-01-22 10:22:23 +00:00
var className = compat . getClassNameForRemoting ( self ) ;
remotes . before ( className + '.' + name , function ( ctx , next ) {
2013-07-16 02:45:27 +00:00
fn ( ctx , ctx . result , next ) ;
} ) ;
} else {
var args = arguments ;
this . once ( 'attached' , function ( ) {
self . beforeRemote . apply ( self , args ) ;
} ) ;
}
2013-11-20 21:31:30 +00:00
} ;
2013-07-16 02:45:27 +00:00
2013-07-16 17:25:02 +00:00
// after remote hook
ModelCtor . afterRemote = function ( name , fn ) {
var self = this ;
if ( this . app ) {
var remotes = this . app . remotes ( ) ;
2014-01-22 10:22:23 +00:00
var className = compat . getClassNameForRemoting ( self ) ;
remotes . after ( className + '.' + name , function ( ctx , next ) {
2013-07-16 17:25:02 +00:00
fn ( ctx , ctx . result , next ) ;
} ) ;
} else {
var args = arguments ;
this . once ( 'attached' , function ( ) {
self . afterRemote . apply ( self , args ) ;
} ) ;
}
2013-11-20 21:31:30 +00:00
} ;
2013-08-16 23:39:13 +00:00
// Map the prototype method to /:id with data in the body
2014-04-03 08:39:42 +00:00
var idDesc = ModelCtor . modelName + ' id' ;
2013-07-15 18:32:00 +00:00
ModelCtor . sharedCtor . accepts = [
2014-04-03 08:39:42 +00:00
{ arg : 'id' , type : 'any' , http : { source : 'path' } , description : idDesc }
2013-08-16 23:39:13 +00:00
// {arg: 'instance', type: 'object', http: {source: 'body'}}
2013-07-15 18:32:00 +00:00
] ;
ModelCtor . sharedCtor . http = [
{ path : '/:id' }
] ;
2013-07-25 00:21:15 +00:00
ModelCtor . sharedCtor . returns = { root : true } ;
2014-05-03 03:04:06 +00:00
2013-07-15 18:32:00 +00:00
return ModelCtor ;
2013-11-20 21:31:30 +00:00
} ;
/ * !
* Get the reference to ACL in a lazy fashion to avoid race condition in require
* /
2014-02-04 00:00:01 +00:00
var _aclModel = null ;
Model . _ACL = function getACL ( ACL ) {
if ( ACL !== undefined ) {
// The function is used as a setter
_aclModel = ACL ;
}
if ( _aclModel ) {
return _aclModel ;
}
var aclModel = require ( './acl' ) . ACL ;
_aclModel = loopback . getModelByType ( aclModel ) ;
return _aclModel ;
} ;
2013-07-15 18:32:00 +00:00
2013-11-20 21:31:30 +00:00
/ * *
* Check if the given access token can invoke the method
*
* @ param { AccessToken } token The access token
* @ param { * } modelId The model id
* @ param { String } method The method name
* @ param callback The callback function
*
2013-12-20 01:49:47 +00:00
* @ callback { Function } callback
2013-11-20 21:31:30 +00:00
* @ param { String | Error } err The error object
* @ param { Boolean } allowed is the request allowed
* /
2014-01-26 22:02:56 +00:00
2013-11-20 21:31:30 +00:00
Model . checkAccess = function ( token , modelId , method , callback ) {
2013-12-10 23:57:55 +00:00
var ANONYMOUS = require ( './access-token' ) . ANONYMOUS ;
token = token || ANONYMOUS ;
2014-02-04 00:00:01 +00:00
var aclModel = Model . _ACL ( ) ;
2013-11-20 21:31:30 +00:00
var methodName = 'string' === typeof method ? method : method && method . name ;
2014-02-04 00:00:01 +00:00
aclModel . checkAccessForToken ( token , this . modelName , modelId , methodName , callback ) ;
2013-11-20 21:31:30 +00:00
} ;
2013-12-20 01:49:47 +00:00
/ * !
2013-12-07 01:04:47 +00:00
* Determine the access type for the given ` RemoteMethod ` .
*
* @ api private
* @ param { RemoteMethod } method
* /
Model . _getAccessTypeForMethod = function ( method ) {
if ( typeof method === 'string' ) {
method = { name : method } ;
}
assert (
typeof method === 'object' ,
'method is a required argument and must be a RemoteMethod object'
) ;
2014-02-04 00:00:01 +00:00
var ACL = Model . _ACL ( ) ;
2013-12-07 01:04:47 +00:00
switch ( method . name ) {
case 'create' :
return ACL . WRITE ;
case 'updateOrCreate' :
return ACL . WRITE ;
case 'upsert' :
return ACL . WRITE ;
case 'exists' :
return ACL . READ ;
case 'findById' :
return ACL . READ ;
case 'find' :
return ACL . READ ;
case 'findOne' :
return ACL . READ ;
case 'destroyById' :
return ACL . WRITE ;
case 'deleteById' :
return ACL . WRITE ;
case 'removeById' :
return ACL . WRITE ;
case 'count' :
return ACL . READ ;
break ;
default :
return ACL . EXECUTE ;
break ;
}
}
2014-02-21 03:43:50 +00:00
/ * *
* Get the ` Application ` the Model is attached to .
*
* @ callback { Function } callback
* @ param { Error } err
* @ param { Application } app
* @ end
* /
Model . getApp = function ( callback ) {
var Model = this ;
if ( this . app ) {
callback ( null , this . app ) ;
} else {
Model . once ( 'attached' , function ( ) {
assert ( Model . app ) ;
callback ( null , Model . app ) ;
} ) ;
}
}
2014-05-16 19:32:54 +00:00
/ * *
* Enable remote invocation for the method with the given name .
*
* @ param { String } name The name of the method .
* ` ` ` js
* // static method example (eg. Model.myMethod())
* Model . remoteMethod ( 'myMethod' ) ;
* // instance method exampe (eg. Model.prototype.myMethod())
* Model . remoteMethod ( 'prototype.myMethod' , { prototype : true } ) ;
* @ param { Object } options The remoting options .
* See [ loopback . remoteMethod ( ) ] ( http : //docs.strongloop.com/display/DOC/Remote+methods+and+hooks#Remotemethodsandhooks-loopback.remoteMethod(fn,[options])) for details.
* /
Model . remoteMethod = function ( name , options ) {
this . sharedClass . defineMethod ( name , options ) ;
}
2013-07-15 18:32:00 +00:00
// setup the initial model
2013-11-11 21:35:54 +00:00
Model . setup ( ) ;