Ejemplo n.º 1
0
exports.get = function( done ){

  if (!cachedGetResults){

    // Simple schema
    var BasicSchema =  declare( [ SimpleSchema, hotplate.config.get( 'hotplate.SchemaMixin') ] );

    // Sets the DB Layer
    var DbLayer = declare([ SimpleDbLayer, hotplate.config.get('hotplate.DbLayerMixin') ], {
      db: hotplate.config.get( 'hotplate.db' )
    });

      // Creates a basic DB store based on that layer
    var BasicDbStore = declare( [ JsonRestStores, JsonRestStores.SimpleDbLayerMixin, JsonRestStores.HTTPMixin ], {
      DbLayer: DbLayer,
      chainErrors: 'all'
    });

    // Legacy names
    var HotSchema = BasicSchema;
    var HotStore = BasicDbStore;

    cachedGetResults = { DbLayer, BasicSchema, HotSchema, BasicDbStore, HotStore }
  }

  if( done ) return done( null, cachedGetResults );
  else return( cachedGetResults );
};
Ejemplo n.º 2
0
  function getDbAndDbDriverAndJRS( done ) {

   try {
     require('fs').mkdirSync('/tmp/tests');
   } catch( e ){
   }

    var db = new Db('/tmp/tests', {});

    var DbDriver = declare( [ SimpleDbLayer, TingoMixin ], { db: db } );
    var JRS = declare( J, { DbDriver: DbDriver } );
    done( null, db, DbDriver, JRS );
  },
Ejemplo n.º 3
0
  hotCoreStore.get( function( err, s ){
    if( err ) return done( err );

    var BasicDbStore = s.BasicDbStore;
    var BasicSchema = s.BasicSchema;

    var Log = declare( [ BasicDbStore ], {
      schema: new BasicSchema({

        logLevel   : { type: 'number', required: true, default: 1 },
        error      : { type: 'serialize', required: false },
        errorName  : { type: 'string', required: false },
        errorMessage: { type: 'string', required: false },
        errorStack : { type: 'string', required: false },
        errors     : { type: 'serialize', required: false },
        message    : { type: 'string', required: false },
        data       : { type: 'serialize', required: false },
        loggedOn   : { type: 'date', required: true, default: function(){ return new Date() } },
        workspaceId: { type: 'id' },
        userId     : { type: 'id', required: false },
        system     : { type: 'boolean', required: true, defaule: false },
      }),
      storeName:  'log',
      paramIds: [ 'id' ],
    });
    stores.log = new Log();

    done( null, stores );
  });
Ejemplo n.º 4
0
    exports = module.exports = function( app ){

      var JRS = dbSpecific.JRS;
      var Schema = dbSpecific.Schema;
    
      var People = declare( JRS, {
    
        schema: new Schema({
          _id     : { type: 'id' },
          name   : { type: 'string', trim: 60 },
          surname: { type: 'string', trim: 60 },
        }),
    
        paramIds: [ '_id' ],
        storeName: 'People',
    
        handlePut: true,
        handlePost: true,
        handleGet: true,
        handleGetQuery: true,
        handleDelete: true,
    
        hardLimitOnQueries: 50,
      });
    
      People.onlineAll( app, '/people/', ':_id' );
    }
Ejemplo n.º 5
0
  hotplate.hotEvents.emit('sharedFunctions', function( err, sharedFunctions) {
    if( err ){
      done( err );
    } else {

      // Gets the results, and adds them to the sharedValidators hash which
      // will then get used
      sharedFunctions.onlyResults().forEach( function( functions ){
        for( var k in functions ){
          // If it ends with "Validator", then it's a validator
          if( k.indexOf('Validator', k.length - 'Validator'.length) !== -1 ){
            sharedValidators[ k ] = functions[ k ];
          }
        }
      });

      classes.HotSchemaMixin = declare( null, {

        sharedValidatorTypeParam: function( p ){
          if( typeof( p.parameterValue ) !== 'string' )
            throw( new Error("Validator needs to be a string, found: " + typeof( p.parameterValue ) ) );

     
          var f = sharedValidators[ p.parameterValue + "Validator" ];
 
          if( f && ! f( p.value ) ){
            var msg = f( false );
            p.errors.push( { field: p.fieldName, message: msg, mustChange: true } );
          }
        }
      });

      classes.HotSchema = declare( [ SimpleSchema, hotplate.config.get( 'hotplate.SchemaMixin'), classes.HotSchemaMixin ] );

      classes.HotStoreMixin = declare( null, {

        killComet: false,
        chainErrors: 'all',

        hotGlobalBroadcast: false,

        echoAfterPutNew: true,
        echoAfterPutExisting: true,
        echoAfterPost: true,

        logError: function( error ){
          hotCoreServerLogger.log( error );
        },

        _getTabId: function( req ){

          // If the tab header wasn't there, then there is no way to broadcast -- abord
          if( typeof( this._req ) === 'undefined' ) return null;

          var tabId = this._req.headers['x-hotplate-tabid'];
          if( tabId == '' ){
            tabId = null;
          }
          return tabId; 
        },


        _broadcast: function( killCometOption, type, objectId, object, options ){

          var makeTabIdHash;
          var fromUserId;
          var storeTarget;

          // No comet required (from the option, or for the store's own request
          if( this.killComet || killCometOption ) return;

          // fromUserId taken from the session (if it exists) or set to null (it's an API call)
          fromUserId = ( this._req && this._req.session && this._req.session.userId ) || null;

          // If it's in a multi-home environment, use the specific multi-home filter
          // if( hotplate.config.get( 'hotCoreMultiHome.enabled' ) && object.workspaceId ) makeTabIdHash = exports.makeTabIdHashForMultihome;
          if( hotplate.config.get( 'hotCoreMultiHome.enabled' ) ) makeTabIdHash = hotCoreMultiHome.makeTabIdHashForMultihome;

          var message = { type: type, storeName: this.storeName, objectId: objectId, object: object };

          // If options.beforeId is set, make up a fake record with 'before' set as record with matching ID is idProperty
          // We cannot really send the whole record as we never fetch it fully
          if( options.beforeId ){
            if( options.beforeId === 'null' ){
              message.before = null;
            } else {
              message.before = {};
              message.before[ this.idProperty ] = options.beforeId;
            }
          }
          if( options.justReposition) message.justReposition = true;

          // Actually invoke the broadcast
          hotplate.hotEvents.emit('cometBroadcast', fromUserId, this._getTabId(), makeTabIdHash, message,  function(){}  );


          /*
            // What follows is the attempt to send a comet message for every store that uses the _collection_ modified by the
            // change. Unfortunately, sub-stores that change a subset of the data effectively cannot be done because
            // the comet message would be incomplete (it would only have some of the fields).


          hotCoreStoreRegistry.getAllStores( function( err, allStores ){


            allStores.collections[ self.collectionName ].forEach( function( s ){

              console.log("Setting up a message for ", s.storeObject.storeName );
              var message = { type: type, storeName: s.storeObject.storeName, objectId: objectId, object: object };
              console.log("The message is: ", message );

              // If options.beforeId is set, make up a fake record with 'before' set as record with matching ID is idProperty
              // We cannot really send the whole record as we never fetch it fully
              if( options.beforeId ){
                if( options.beforeId === 'null' ){
                  message.before = null;
                } else {
                  message.before = {};
                  message.before[ this.idProperty ] = options.beforeId;
                }
              }
              if( options.justReposition) message.justReposition = true;

              // Actually invoke the broadcast
              hotplate.hotEvents.emit('cometBroadcast', fromUserId, self._getTabId(), makeTabIdHash, message,  function(){}  );

            });

          } );
          */


        },

        afterPutExisting: function afterPutExisting( params, body, options, doc, fullDoc, docAfter, fullDocAfter, overwrite, done ){
          var self = this;


          this.inheritedAsync( afterPutExisting, arguments, function(){

            self._broadcast( options.killComet, 'storeRecordUpdate', docAfter[ self.idProperty], docAfter, options );
            done( null );
          });
        },    

        afterPost: function afterPost( params, body, options, doc, fullDoc, done ){
          var self = this;
          this.inheritedAsync( afterPost, arguments, function(){
            self._broadcast( options.killComet, 'storeRecordCreate', doc[ self.idProperty], doc, options );
            done( null );
          });
        },

        afterPutNew: function afterPutNew( params, body, options, doc, fullDoc, overwrite, done ){
          var self = this;
          this.inheritedAsync( afterPutNew, arguments, function(){
            self._broadcast( options.killComet, 'storeRecordCreate', doc[ self.idProperty], doc, options );
            done( null );
          });
        },

        afterDelete: function afterDelete( params, body, options, doc, fullDoc, done ){
          var self = this;
          this.inheritedAsync( afterDelete, arguments, function(){
            self._broadcast( options.killComet, 'storeRecordRemove', doc[ self.idProperty ], doc, options ); 
            done( null );
          });
        },    
      });

      classes.HotStore = declare( [ JsonRestStores, classes.HotStoreMixin ],{
        DbLayer: declare( [ SimpleDbLayer, hotplate.config.get('hotplate.DbLayerMixin') ], { db: hotplate.config.get( 'hotplate.db' ) } )
      });

      done( null, classes );

    }
  });
Ejemplo n.º 6
0
exports.BasicPermissionsMixin = declare( null, {

  _checkUserId: function( params, cb ){
    if( params.userId ){

      if( ! self._req.session.userId ) return cb( new self.UnauthorizedError() );

      if( params.userId.toString() !== self._req.session.userId ){
        cb( null, false );
      } else {
        cb( null, true );
      }
    } else {
      cb( null, true );
    }
        
  },

  checkPermissionsPost: function checkPermissionsPost( params, body, options, cb ){
    var self = this;

    this.inheritedAsync( checkPermissionsPost, arguments, function( err, res ){
      if( err ){
        cb( err );
      } else {
        if( ! res ){
          cb( null, false );
        } else {
          self._checkUserId( params, cb );
        }
      }
    });
  },
  checkPermissionsPutNew: function checkPermissionsPutNew( params, body, options, cb ){
    var self = this;

    this.inheritedAsync( checkPermissionsPutNew, arguments, function( err, res ){
      if( err ){
        cb( err );
      } else {
        if( ! res ){
          cb( null, false );
        } else {
          self._checkUserId( params, cb );
        }
      }
    });
  },
  checkPermissionsPutExisting: function checkPermissionsPutExisting( params, body, options, doc, fullDoc, cb ){
    var self = this;

    this.inheritedAsync( checkPermissionsPutExisting, arguments, function( err, res ){
      if( err ){
        cb( err );
      } else {
        if( ! res ){
          cb( null, false );
        } else {
          self._checkUserId( params, cb );
        }
      }
    });
  },
  checkPermissionsDelete: function checkPermissionsDelete( params, body, options, doc, fullDoc, cb ){
    var self = this;

    this.inheritedAsync( checkPermissionsDelete, arguments, function( err, res ){
      if( err ){
        cb( err );
      } else {
        if( ! res ){
          cb( null, false );
        } else {
          self._checkUserId( params, cb );
        }
      }
    });
  },


});
Ejemplo n.º 7
0
  }
};


/* To WRITE (put, post, delete), userId needs to match logged in user */
// Was: BasicPermissionsMixin
exports.OnlyUserIdCanWrite = declare( Object, {

  _onlyUserId: _onlyUserId,

  checkPermissions: function f( request, method, cb ){
    var self = this;

    self.inheritedAsync( f, arguments, function( err, res, message ){
      if( err ) return cb( err );
      if( ! res ) return cb( null, false, message );

      // Only putXXX methods are handled
      if( method != 'put' && method != 'post' && method != 'delete' ) return cb( null, true );

      self._onlyUserId( request, method, cb );
    });
  },

});
exports.BasicPermissionsMixin = exports.OnlyUserIdCanWrite;


/* To READ (get, getQuery), userId needs to match logged in user */
// Was: PrivateUserDataPermissionsMixin
exports.OnlyUserIdCanRead = declare( Object, {
Ejemplo n.º 8
0
hotplate.hotEvents.on( 'stores', 'hotCoreComet', hotplate.cachable( function( done ){

  var stores = {};

  // This module only uses JsonRestStores as a way to access the DB and expose methods,
  // it doesn't mixin with hotJsonRestStores (which would do Comet event emission etc.)

  var BasicDbStore = declare( JsonRestStores, {
    DbLayer: declare( [ SimpleDbLayer, hotplate.config.get('hotplate.DbLayerMixin') ], { db: hotplate.config.get( 'hotplate.db' ) } )
  });

  var BasicDbSchema = declare( [ SimpleSchema, hotplate.config.get( 'hotplate.SchemaMixin') ] );

  // ***********************************
  // *** OPEN TABS   *******************
  // ***********************************

  stores.Tabs = declare( BasicDbStore, {

    schema: new BasicDbSchema({
      id:         { type: 'id' },

      userId:        { type: 'id', required: true },
      lastSync:      { type: 'date', default: function(){ return new Date() } },
    }),

    searchSchema: new BasicDbSchema({
      id:            { type: 'id' },

      userId:        { type: 'id' },
      lastSync:      { type: 'date' },

      // Ordering filters
      fromLastSync:  { type: 'date', searchable: { type: 'gte', field: 'lastSync' } },
      toLastSync:    { type: 'date', searchable: { type: 'lte', field: 'lastSync' }  },
    }),

    handlePost: true,
    checkPermissionsPost: function( params, body, options, cb ){
      cb( null, true );
    },
 
    handleDelete: true,
    checkPermissionsDelete: function( params, body, options, doc, fullDoc, cb ){
      cb( null, true );
    },

    storeName:  'Tabs',
    paramIds: [ 'id' ],

    publicURL: '/tabs/',

    prepareBodyPost: function( body, cb ){

      if( this.remote ){
        if( this._req.session.userId ){
          body.userId = this._req.session.userId;
        }
      }
      cb( null, body );
    },

    // Delete ALL entries in TabMessages when a tab gets deleted
    afterDelete: function( params, body, options, doc, fullDoc, cb ){

      // Delete the tab messages
      (new stores.TabMessages()).dbLayer.delete( { conditions: { and: [ { field: 'tabId', type:'eq', value: body.tabId } ] } }, cb );
    },

  });

  stores.TabMessages = declare( BasicDbStore, {

    schema: new BasicDbSchema({
      id:            { type: 'id' },
      tabId:         { type: 'id' },

      fromUserId:    { type: 'id', required: true },
      message:       { type: 'serialize', required: true },
      added:         { type: 'date', default: function() { return new Date() } },
    }),

    searchSchema: new BasicDbSchema({
      id       : { type: 'id' },
      tabId    : { type: 'id' },

      fromAdded: { type: 'date', searchable: { type: 'gte', field: 'added' }  },
      toAdded  : { type: 'date', searchable: { type: 'lte', field: 'added' }  },
    }),

    storeName:  'TabMessages',
    paramIds: [ 'tabId', 'id' ],

  });

  
  // PSEUDO-STORE, sends an object out with all of the messages
  // It will return an array of unserialised messages in TabMessages for
  // a specific tabId, and then it will DELETE those messages
  stores.TabMessageDispatcher = declare( JsonRestStores, {

    // COMMON
    schema: new SimpleSchema({
      tabId:         { type: 'string' },
      messages:      { type: 'none' },
    }),

    DbLayer: SimpleDbLayer,

    storeName:  'TabMessageDispatcher',

    paramIds: [ 'tabId' ],

    publicURL: '/tabs/dispatcher/',


    handleGet: true,
    checkPermissionsGet: function( params, body, options, doc, fullDoc, done ){

      // Check that the remote user requesting the tab is indeed the tab's owner
      
      var self = this;

      // User needs to be logged in
      if( ! self._req.session.userId ) return done( null, false );

      // TODO: Optimise a little here. Since checkPermissionsGet is called before driverAllDbFetch,
      // try and cache this result
      stores.Tabs.GetQuery( { filters: { fromLastSync: new Date() - TABLIFESPAN, id: params.tabId } }, function( err, tab ){
        if( err ){
          done( err );
        } else {

          // This may seem strange, but always pass authentication if the tab is not
          // there, as the store will need to accept the get and return the "storeReset"
          // message after returning the new tab
          if( tab.length == 0 ){
             done( null, true );

          } else {
            tab = tab[0];
            done( null, tab.userId.toString() === self._req.session.userId.toString() );
          }
        }
      });

 
    },

    execAllDbFetch: function( params, body, options, done ){

      var messages = [];
      var self = this;

      // If it's not a remote call, always return empty result
      if( ! self.remote ) return done( null, { messages: [] } );

      var headersWorkspaceId = self._req.headers[ 'x-hotplate-workspaceid' ];

      // User is not logged in -- goodbye
      if( ! self._req.session.loggedIn || ! self._req.session.userId ){
        hotplate.log("A non-logged in user tried to fetch tabId %s for workspaceId %s, denying...", params.tabId, headersWorkspaceId );
        return done( new self.UnauthorizedError() );
      }

      hotplate.log("Looking for tab %s owned by user %s, x-workspaceId is %s", params.tabId, self._req.session.userId, headersWorkspaceId );

      // Return all messages for that tab, REMOVING after fetching
      stores.Tabs.GetQuery( { filters: { id: params.tabId, userId: self._req.session.userId, fromLastSync: new Date() - TABLIFESPAN } }, function( err, tab ){
        if( err ){
          done( err );
        } else {


          hotplate.log("Returned:");
          hotplate.log( tab );
          if( tab.length == 0 ){

            hotplate.log("Tab was NOT present. Trying to understand if I should create one");

            // At this point, the tab wasn't found. If workspaceId was passed via headers,
            // the person will be returned the configuration for that workspace. We need to check
            // that the user actually has access to that workspaceId.

            hotCoreStoreRegistry.getAllStores( function( err, storesData ){
              if( err ){
                done( err );
              } else {
                hotplate.log("Checking that user has access to the workspaceId she is trying to register for");
                storesData.stores.UsersWorkspaces.Store.GetQuery( { filters: { userId: self._req.session.userId, workspaceId: headersWorkspaceId } }, function( err, uwDocs){
                  if( err ){
                    done( err );
                  } else {
                    if( uwDocs.length == 0 ){
                      hotplate.log("No access -- user needs to (re?)login!");
                      done( new self.UnauthorizedError() );
                    } else {
               
    
                      hotplate.log("OK, access is cleared, creating the tab for the user...");
                      hotplate.log( self._req.session.userId );
                      stores.Tabs.Post( { userId: self._req.session.userId }, {}, function( err, tab ){
                        if( err ){
                           done( err );
                        } else {
    
                          
                          hotplate.log("...and ALSO returning the workspace configuration for that userId");
                          hotCoreStoreConfig.getConfigRecords( headersWorkspaceId, self._req.session.userId, function( err, storeRecords ){
                            if( err ){
                              done( err );
                            } else {
                              done( null, { messages: [ { fromUserId: self._req.session.userId, message: { type: 'resetStores', tabId: tab.id, storeRecords: storeRecords } } ] } );
                            };
                          });
    
                        }
                      }); // Tabs.Post()
    
    
                    }
                  }
                });//UsersWorkspaces.GetQuer ()
              }
            });


          } else {

            // Write the new access time onto the tab's record; 
            tab = tab[ 0 ];
            tab.lastSync = new Date();
            stores.Tabs.Put( tab.id, tab, function( err, tab ){
              if( err ){
                done( err );
              } else {

                // Return all messages for that tab, REMOVING after fetching
                stores.TabMessages.GetQuery( { filters: { tabId: params.tabId, fromAdded: new Date() - TABLIFESPAN }, delete: true }, function( err, tabMessages ){
                  if( err ){
                    done( err );
                  } else {
         
                    tabMessages.forEach( function( tabMessage ){
                      // delete tabMessage._\id;
                      delete tabMessage.messageId;
                      delete tabMessage.tabId;

                      messages.push( tabMessage);
                    });
                    done( null, { messages: messages } );
                  }
                });
              }

            });

           }
        };
      });
    
    },

  });

  done( null,  stores );

}))
Ejemplo n.º 9
0
  hotCoreJsonRestStores.get( function( err, s ){
    if( err ){
      done( err );
    } else {

      var HotStore = s.HotStore;
      var HotSchema = s.HotSchema;

      // ***********************************
      // *** WORKSPACES ********************
      // ***********************************

      stores.Workspaces = declare( HotStore, {

        schema: new HotSchema({
          workspaceName: { type: 'string', required: true, notEmpty: true, trim: 128, searchable: true,
                           sharedValidator: 'workspaceValidator' },
          ownerUserId:   { type: 'id' }, 
        }),

        storeName:  'Workspaces',

        publicURL: '/workspaces/:id',
        hotExpose: true,

        handlePost: true,
        checkPermissionsPost: function( params, body, options, cb ){
          // Needs to be logged in
          if( ! this._req.session.userId ) return cb( null, false );
     
          // Make sure that body.ownerUserId IS indeed the logged in user
          body.ownerUserId = this._req.session.userId;

          cb( null, true );
        },


        // If creating a new workspace, and the user is logged in, then
        // assign the creating user to that workspace
        afterPost: function( params, body, options, doc, fullDoc, cb ){

          if( this.remote && this._req.session.loggedIn ){
            var userId = this._req.session.userId;
            if( userId ){
               // Note: this calls the callback
               stores.WorkspacesUsers.Post( { userId: userId, workspaceId: doc.id }, {}, cb );
            } else {
              cb( null );
            }
          }
        },

      });


/*
  var WorkspaceInvites = exports.WorkspaceInvites = declare( HotStore, {

    schema: new HotSchema({
      inviteCode:  { type: 'blob' },
      email     :  { type: 'blob' },
      name      :  { type: 'blob' },
    }),

    handlePost: true,
    handleGet: true,
    handleGetQuery: true,
    handleDelete: true,

    storeName:  'workspaceInvites',
    paramIds: [ 'workspaceId', 'id' ],
    publicURL: '/workspaces/:workspaceId/invites/:id',
  });
*/


      // The basic schema for the WorkspacesUsers table
      stores.WorkspacesUsersBase = declare( HotStore, {

        schema: new HotSchema({
          userId:      { type: 'id', searchable: true },
          workspaceId: { type: 'id', searchable: true },
        }),

        storeName: 'WorkspacesUsersBase',
        collectionName: 'WorkspacesUsers',

        paramIds: [ 'id' ],
      });


      stores.WorkspacesUsers = declare( stores.WorkspacesUsersBase, {

        onlineSearchSchema: new HotSchema({
          userId:      { type: 'id' },
        }),

        storeName:  'WorkspacesUsers',

        publicURL: '/workspaces/:workspaceId/users/:id',
        hotExpose: true,

        handleGetQuery: true,

        checkPermissionsGetQuery: function( params, body, options, cb ){
          userInWorkspace( this._req.session.userId, params.workspaceId, cb );
        },

      });

      stores.UsersWorkspaces = declare( stores.WorkspacesUsersBase, {

        onlineSearchSchema: new HotSchema({
          workspaceId:   { type: 'id' },
        }),

        storeName:  'UsersWorkspaces',

        publicURL: '/users/:userId/workspaces/:id',
        hotExpose: true,

        handleGetQuery: true,
        checkPermissionsGetQuery: function( params, body, options, cb ){
          // Only their own workspaces
          if( this._req.session.userId != params.userId ) return cb( null, false );
          cb( null, true );
        },


        // Get the much needed workspaceName from the Workspaces table
        prepareBeforeSend: function( doc, cb ){

          var self = this;

          stores.Workspaces.Get( doc.workspaceId, {}, function( err, otherDoc ){
            if( err ){
              cb( err );
            } else {
              doc.workspaceName = otherDoc.workspaceName;
              cb( null, doc );
            }
          })

        },


      });

      done( null,  stores );

    }
  })
Ejemplo n.º 10
0
  hotCoreJsonRestStores.get( function( err, s ){
    if( err ) return done( err );

    var HotStore = s.HotStore;
    var HotSchema = s.HotSchema;
    var BasicDbStore = s.BasicDbStore;
    var BasicSchema = s.BasicSchema;


    // * Case SNUe: natural collection, unfiltered, put with defaultNewToStart=false
    var SNUe = declare( [ HotStore ],  {

      type: 'uncached',

      schema: new HotSchema({
        name: { type: 'string', required: true, notEmpty: true, trim: 10 },
      }),

      storeName:  'snue',
      publicURL: '/testStores/snue/:id',
      position: true,

      // All enabled by default
      handlePut: true,
      handlePost: true,
      handleGet: true,
      handleGetQuery: true,
      handleDelete: true,

      // Exposed through hotplate
      hotExpose: true,

    });
    stores.snue = new SNUe();

    // * Case SNUs: natural collection, unfiltered, put with defaultNewToStart=true
    var SNUs = declare( [ HotStore ],  {

      type: 'uncached',

      schema: new HotSchema({
        name: { type: 'string', required: true, notEmpty: true, trim: 10 },
      }),

      storeName:  'snus',
      publicURL: '/testStores/snus/:id',
      position: true,
      defaultNewToStart: true,

      // All enabled by default
      handlePut: true,
      handlePost: true,
      handleGet: true,
      handleGetQuery: true,
      handleDelete: true,

      // Exposed through hotplate
      hotExpose: true,

    });
    stores.snus = new SNUs();

    // * Case SOU: ordered collection, unfiltered (Refresh)
    var SOU = declare( [ HotStore ],  {

      type: 'uncached',

      schema: new HotSchema({
        name: { type: 'string', required: true, notEmpty: true, trim: 10, searchable: true },
      }),

      storeName:  'sou',
      publicURL: '/testStores/sou/:id',
      position: false,
      sortableFields: [ 'name' ],

      // All enabled by default
      handlePut: true,
      handlePost: true,
      handleGet: true,
      handleGetQuery: true,
      handleDelete: true,

      // Exposed through hotplate
      hotExpose: true,

    });
    stores.sou = new SOU();

    //* Case SOF: ordered collection, filtered (Refresh)
    var SOF = declare( [ HotStore ],  {

      type: 'uncached',

      schema: new HotSchema({
        name: { type: 'string', required: true, notEmpty: true, trim: 10, searchable: true },
      }),

      storeName:  'sof',
      publicURL: '/testStores/sof/:id',
      position: false,
      sortableFields: [ 'name' ],

      // All enabled by default
      handlePut: true,
      handlePost: true,
      handleGet: true,
      handleGetQuery: true,
      handleDelete: true,

      // Exposed through hotplate
      hotExpose: true,

    });
    stores.sof = new SOF();

    // * Case SNFe: natural collection, filtered, put with defaultNewToStart=false (Refresh)
    var SNFe = declare( [ HotStore ],  {

      type: 'uncached',

      schema: new HotSchema({
        name: { type: 'string', required: true, notEmpty: true, trim: 10, searchable: true },
      }),

      storeName:  'snfe',
      publicURL: '/testStores/snfe/:id',
      position: true,

      // All enabled by default
      handlePut: true,
      handlePost: true,
      handleGet: true,
      handleGetQuery: true,
      handleDelete: true,

      // Exposed through hotplate
      hotExpose: true,

    });
    stores.snfe = new SNFe();

    // * Case SNFs: natural collection, filtered, put with defaultNewToStart=true (Refresh)
    var SNFs = declare( [ HotStore ],  {

      type: 'uncached',

      schema: new HotSchema({
        name: { type: 'string', required: true, notEmpty: true, trim: 10, searchable: true },
      }),

      storeName:  'snfs',
      publicURL: '/testStores/snfs/:id',
      position: true,
      defaultNewToStart: true,

      // All enabled by default
      handlePut: true,
      handlePost: true,
      handleGet: true,
      handleGetQuery: true,
      handleDelete: true,

      // Exposed through hotplate
      hotExpose: true,

    });
    stores.snfs = new SNFs();


    // * Case CNUe: natural collection, unfiltered, put with defaultNewToStart=false
    var CNUe = declare( [ HotStore ],  {

      type: 'cached',

      schema: new HotSchema({
        name: { type: 'string', required: true, notEmpty: true, trim: 10 },
      }),

      storeName:  'cnue',
      publicURL: '/testStores/cnue/:id',
      position: true,

      // All enabled by default
      handlePut: true,
      handlePost: true,
      handleGet: true,
      handleGetQuery: true,
      handleDelete: true,

      // Exposed through hotplate
      hotExpose: true,

    });
    stores.cnue = new CNUe();

    // * Case CNUs: natural collection, unfiltered, put with defaultNewToStart=true
    var CNUs = declare( [ HotStore ],  {

      type: 'cached',

      schema: new HotSchema({
        name: { type: 'string', required: true, notEmpty: true, trim: 10 },
      }),

      storeName:  'cnus',
      publicURL: '/testStores/cnus/:id',
      position: true,
      defaultNewToStart: true,

      // All enabled by default
      handlePut: true,
      handlePost: true,
      handleGet: true,
      handleGetQuery: true,
      handleDelete: true,

      // Exposed through hotplate
      hotExpose: true,

    });
    stores.cnus = new CNUs();

    // * Case COU: ordered collection, unfiltered (QueryEngine)
    var COU = declare( [ HotStore ],  {

      type: 'cached',

      schema: new HotSchema({
        name: { type: 'string', required: true, notEmpty: true, trim: 10, searchable: true },
      }),

      storeName:  'cou',
      publicURL: '/testStores/cou/:id',
      position: false,
      sortableFields: [ 'name' ],

      // All enabled by default
      handlePut: true,
      handlePost: true,
      handleGet: true,
      handleGetQuery: true,
      handleDelete: true,

      // Exposed through hotplate
      hotExpose: true,

    });
    stores.cou = new COU();

    //* Case COF: ordered collection, filtered (QueryEngine)
    var COF = declare( [ HotStore ],  {

      type: 'cached',

      schema: new HotSchema({
        name: { type: 'string', required: true, notEmpty: true, trim: 10, searchable: true },
      }),

      storeName:  'cof',
      publicURL: '/testStores/cof/:id',
      position: false,
      sortableFields: [ 'name' ],

      // All enabled by default
      handlePut: true,
      handlePost: true,
      handleGet: true,
      handleGetQuery: true,
      handleDelete: true,

      // Exposed through hotplate
      hotExpose: true,

    });
    stores.cof = new COF();

    // * Case CNFe: natural collection, filtered, put with defaultNewToStart=false (QueryEngine)
    var CNFe = declare( [ HotStore ],  {

      type: 'cached',

      schema: new HotSchema({
        name: { type: 'string', required: true, notEmpty: true, trim: 10, searchable: true },
      }),

      storeName:  'cnfe',
      publicURL: '/testStores/cnfe/:id',
      position: true,

      // All enabled by default
      handlePut: true,
      handlePost: true,
      handleGet: true,
      handleGetQuery: true,
      handleDelete: true,

      // Exposed through hotplate
      hotExpose: true,

    });
    stores.cnfe = new CNFe();

    // * Case CNFs: natural collection, filtered, put with defaultNewToStart=true (QueryEngine)
    var CNFs = declare( [ HotStore ],  {

      type: 'cached',

      schema: new HotSchema({
        name: { type: 'string', required: true, notEmpty: true, trim: 10, searchable: true },
      }),

      storeName:  'cnfs',
      publicURL: '/testStores/cnfs/:id',
      position: true,
      defaultNewToStart: true,

      // All enabled by default
      handlePut: true,
      handlePost: true,
      handleGet: true,
      handleGetQuery: true,
      handleDelete: true,

      // Exposed through hotplate
      hotExpose: true,

    });
    stores.cnfs = new CNFs();


    done( null, stores );
  });
Ejemplo n.º 11
0
  hotCoreStore.get(function (err, s) {
    if (err) return done(err)

    var BasicDbStore = s.BasicDbStore
    var BasicSchema = s.BasicSchema

    var HotStore = s.HotStore
    var HotSchema = s.HotSchema

    // ***********************************
    // *** OPEN TABS   *******************
    // ***********************************

    var Tabs = declare(HotStore, {

      schema: new BasicSchema({
        id: { type: 'string', searchable: true, unique: true },
        lastSync: { type: 'date', searchable: true, default: function () { return new Date() } }
      }),

      handlePost: true,
      handleDelete: true,

      storeName: 'tabs',
      paramIds: [ 'id' ]

      /*
      nested: [
        {
          type: 'multiple',
          store: 'tabMessages',
          join: { tabId: 'tabId'},
        },
      ]
      */

    })
    stores.tabs = new Tabs()

    // Internal store, only used via API
    var TabMessages = declare(BasicDbStore, {

      schema: new BasicSchema({
        message: { type: 'serialize', required: true },
        added: { type: 'date', searchable: true, protected: true, default: function () { return new Date() } },
        tabId: { type: 'string', searchable: true, required: true }
      }),

      paramIds: [ 'id' ],
      storeName: 'tabMessages',

      autoLookup: {
        tabId: 'tabs'
      },

      sortableFields: [ 'added' ],
      defaultSort: { added: 1 }
    })
    stores.tabMessages = new TabMessages()

    // Internal store, only used via API
    var TabSubscriptions = declare(BasicDbStore, {

      schema: new BasicSchema({
        added: { type: 'date', protected: true, searchable: true, default: function () { return new Date() } },
        tabId: { type: 'string', searchable: true, required: true },
        handle: { type: 'string', searchable: true, required: true, trim: 255 },
        p1: { type: 'string', searchable: true, required: false, trim: 1024 },
        p2: { type: 'string', searchable: true, required: false, trim: 1024 },
        p3: { type: 'string', searchable: true, required: false, trim: 1024 },
        p4: { type: 'string', searchable: true, required: false, trim: 1024 },
        hash: { type: 'string', searchable: true, required: false, trim: 1024 }
      }),

      paramIds: [ 'id' ],
      storeName: 'tabSubscriptions'

    })
    stores.tabSubscriptions = new TabSubscriptions()

    // FINISHED
    var UserOnline = declare([JsonRestStores, JsonRestStores.HTTPMixin], {
      schema: new HotSchema({

        userId: {
          type: 'id'
        }

      }),

      handleGet: true,

      storeName: 'userOnline',

      publicURL: '/userOnline/:userId',
      hotExpose: true,

      implementFetchOne: function (request, cb) {
        consolelog('*************************Checking online status of:', request.params.userId)
        consolelog('Connections:', connections)
        for (var k in connections) {
          var $c = connections[ k ]

          consolelog('CHECKING: ', request.params.userId, $c.userId, $c.loggedIn, $c.ws && $c.ws.readyState)
          if ($c && $c.userId && $c.loggedIn && request.params.userId.toString() === $c.userId.toString() && $c.ws && $c.ws.readyState === 1) {
            consolelog('Returning YES!')
            return cb(null, { online: true })
          }
        }
        consolelog('Returning NO!')
        return cb(null, { online: false })
      }

    })
    stores.userOnline = new UserOnline()

    done(null, stores)
  })
Ejemplo n.º 12
0
exports.enableCometEvents = function (Store) {
  return declare([ Store, exports.HotCometEventsMixin ])
}
Ejemplo n.º 13
0
exports.HotCometEventsMixin = declare(Object, {

  // Add from-tab-id if present
  _checkParamIds: function f (request, skipIdProperty, cb) {
    consolelog('REQUEST:', request)

    var method = request._req && request._req.method
    consolelog('METHOD:', method)
    if (method === 'PUT' || method === 'POST' || method === 'DELETE') {
      consolelog('HEADERS:', request._req.headers)
      var tabId = request._req.headers[ 'x-from-tab-id' ]
      consolelog('TAB ID:', tabId)
      if (tabId) {
        consolelog('fromTabId found in body, getting it out and enriching request.data')
        request.data.fromTabId = tabId
        consolelog('Request.data now:', request.data)
      }
    }

    this.inheritedAsync(f, arguments, function (err) {
      if (err) return cb(err)
      cb(null)
    })
  },

  afterEverything: function f (request, method, cb) {
    var storeName = this.storeName
    var self = this

    this.inheritedAsync(f, arguments, function (err, res) {
      if (err) return cb(err)

      consolelog('SPROUT: STORE METHOD afterEverything, method ', method, 'WILL POSSIBLY GENERATE A COMET EVENT.')

      // Suppress comet messages if noComet is on
      if (request.options.noComet) {
        consolelog('Quitting, since noComet is on')
      }

      consolelog('Checking emitCometStoreEvents...', self.emitCometStoreEvents)
      // If emitStoreCometEvents is not on, don't do anything
      if (!self.emitCometStoreEvents) {
        consolelog("This store is comet enabled but it doesn't have emitCometStoreEvents set, nothing to do")
        return cb(null)
      }

      // Do not deal with get-like queries
      if (method === 'getQuery' || method === 'get') return cb(null)

      /*
      stores.tabs.dbLayer.select( { }, function( err, tabs ){
        if( err ) return; // TODO LOG
      */

      var record = request.data.preparedDoc

      var message = {
        type: 'store-change',
        record: record,
        idProperty: self.idProperty,
        op: method,
        storeName: storeName,
        fromStore: true
      }
      if (method === 'put') {
        if (request.putExisting) message.existing = true
        if (request.putNew) message.new = true
      }

      var cometEvent = {
        message: message,
        sessionData: request.session,
        fromTabId: request.data.fromTabId,
        fromClient: false,
        store: self
      }

      consolelog('Store is comet-emabled. Will emit the following comet-event:')
      var ce = cometEvent
      consolelog('Comet event: ', { message: ce.message, sessionData: ce.sessionData, connections: ce.connections, tabs: ce.tabs })

      emitAndSendMessages(cometEvent, function (err) {
        if (err) {
          logger.log({ error: err, system: true, logLevel: 3, message: 'Error on emitting comet event', data: { cometEvent: cometEvent } })
        }

        cb(null)
      })
      // });
    })
  }
})
Ejemplo n.º 14
0
  hotCoreJsonRestStores.get( function( err, s ){
    if( err ){
      done( err );
    } else {

      var HotStore = s.HotStore;
      var HotSchema = s.HotSchema;

      // ***********************************
      // *** USERS *************************
      // ***********************************
    
      var Users = declare( HotStore, {
    
        // COMMON
        schema: new HotSchema({
          recoverToken:        { type: 'blob', searchable: true, notempty: true },
          recoverTokenCreated: { type: 'date', searchable: true, notempty: true },
        }),

        // Nothing is searchable in users by default
        onlineSearchSchema: new HotSchema({
        }),
    
        storeName:  'users',
    
        publicURL: '/users/:id',
        hotExpose: true,

      });
      stores.users = new Users();
    
      var UsersStrategies = declare( HotStore, {
    
        schema: new HotSchema({
          strategyId:  { type: 'blob', searchable: true, required: true, trim: 30 }  ,
          field1:      { type: 'blob', searchable: true, required: false, trim: 255 } ,
          field2:      { type: 'blob', searchable: true, required: false, trim: 255 } ,
          field3:      { type: 'blob', searchable: true, required: false, trim: 255 } ,
          field4:      { type: 'blob', searchable: true, required: false, trim: 255 } ,
        }),
    
        // Nothing is searchable in users by default
        onlineSearchSchema: new HotSchema({
        }),

        storeName:  'usersStrategies',

        publicURL: '/users/:userId/strategies/:id',
        hotExpose: true,
     
        handleGet: true,
        checkPermissionsGet: function( request, doc, fullDoc, cb ){
          // Only their own strategies
          if( request._req.session.userId != request.params.userId ) return cb( null, false );
    
          cb( null, true );
        },
   
 
        handleGetQuery: true,
        checkPermissionsGetQuery: function( request, cb ){
          // Only their own strategies
          if( request._req.session.userId != request.params.userId ) return cb( null, false );
          
          cb( null, true );
        },

    
        // Make sure that, if the request is from the web, field3 and field4 are out of the equation as
        // they often include nice goodies like passwords etc.
        extrapolateDoc: function( request, fullDoc, cb ){
          if( ! request.remote ) return cb( null, fullDoc );
    
          // Copy body onto newBody, leaving out field3 and field4 which are "private"
          var doc = {};
          for( var i in fullDoc ){
            if( i != 'field3' && i != 'field4' ) doc[ i ] = fullDoc[ i ];
          }
          cb( null, doc );
          
        },
    
    
        handleDelete: true,
        checkPermissionsDelete: function( request, doc, fullDoc, cb ){
    
          // Only their own strategies
          if( request._req.session.userId != request.params.userId ) return cb( null, false );
    
          stores.usersStrategies.apiGetQuery( { filters: { userId: doc.userId } }, function( err, queryDocs ){
            if( queryDocs.length > 1 ){
              cb( null, true );
            } else {
              cb( null, false );
            }
          });
    
        },
    
    
      });
      stores.usersStrategies = new UsersStrategies();
      
    
      var AuthStrategies = declare( JsonRestStores, {
    
        schema: new SimpleSchema({
          id:        { type: 'blob', isRequired: true, trim: 30 }  ,
        }),

        // Nothing is searchable in users by default
        onlineSearchSchema: new HotSchema({
        }),

        handleGet: true,
        handleGetQuery: true,
    
        storeName:  'authStrategies',
   
        DbLayer: SimpleDbLayer,

        logError: function( error ){ hotCoreServerLogger.log( error ); },
 
        publicURL: '/authstrategies/:id',
        hotExpose: true,

        execAllDbFetch: function( request, cb ){
          var strategies = hotplate.config.get('hotCoreAuth.strategies');
          var doc;
    
          // No strategies defined in Hotplate, end of story
          if( typeof( strategies ) === 'undefined' ){
            return cb( null, null );     
          }
     
          // Check if the strategy is one of the ones defined in Hotplate
          if( typeof( strategies[ params.id ] ) !== 'undefined' ){
            doc = {}
            doc.id = params.id;
          } else {
            doc = null;
          }
    
          // Return whatever was found
          cb( null, doc );
        },
    
        execGetDbQuery: function( request, cb ){
          var strategies = hotplate.config.get('hotCoreAuth.strategies');
          var doc;
          var docs = [];
          
          for( var strategyId in strategies ){
            docs.push( { id: strategyId } );
          }      
          cb( null, docs );
        },
    
    
      });
    
      stores.authStrategies = new AuthStrategies();
    
      // This is used so that an applicaton can know in advance if a user login is already taken
      var Logins = declare( JsonRestStores, {
    
        schema: new SimpleSchema({
          login     : { type: 'string', required: true, lowercase: true, trim: 30, searchable: true },
        }),
   
        storeName:  'logins',

        DbLayer: SimpleDbLayer,   

        logError: function( error ){ hotCoreServerLogger.log( error ); },

        handleGetQuery: true,

        publicURL: '/logins/:id',
        hotExpose: true,
    
        execGetDbQuery: function( request, cb ){
          
          var self = this;
    
          stores.usersStrategies.apiGetQuery( { filters: { strategyId: 'local', field1: request.options.filters.login } }, function( err, res ){
            if( err ){
              cb( err, null );
            } else {
              if( res.length ){
                cb( null, [ { login: request.options.filters.login } ] );
                // });
              } else {
                cb( null, [ ] );
              }
            }
          });      
    
        },   
    
      });
      stores.login = new Logins();
    
      done( null, stores );

    }
  });
Ejemplo n.º 15
0
var SimpleSchema = declare( Object, {

  constructor: function( structure, options){
    this.structure = structure;
    this.options = typeof( options ) !== 'undefined' ? options : {};
  },


  // Built-in types

  noneTypeCast: function( definition, value, fieldName, options, failedCasts ){
   return value;
  },

  stringTypeCast: function( definition, value, fieldName, options, failedCasts ){

    // Undefined: return '';
    if( typeof( value ) === 'undefined' ) return '';
    if( value === null ) return '';

    // No toString() available: failing to cast
    if( typeof( value.toString ) === 'undefined' ){
      failedCasts[ fieldName ] = true;
      return;
    }

    // Return cast value
    return value.toString();
  },

  blobTypeCast: function( definition, value, fieldName, options, failedCasts ){

    // Undefined: return '';
    if( typeof( value ) === 'undefined' ) return '';
    if( value === null ) return '';

    return value;
  },

  numberTypeCast: function( definition, value,  fieldName, options, failedCasts ){

    // Undefined: return 0;
    if( typeof( value ) === 'undefined' ) return 0;

    // If Number() returns NaN, fail
    var r = Number( value );
    if( isNaN( r ) ){
      failedCasts[ fieldName ] = true;
      return;
    }

    // Return cast value
    return r;

  },

  dateTypeCast:function( definition, value, fieldName, options, failedCasts ){

    // Undefined: return a new date object
    if( typeof( value ) === 'undefined' ){
      return new Date();
    }

    // If new Date() returns NaN, date was not corect, fail
    var r = new Date( value );
    if( isNaN( r ) ){
      failedCasts[ fieldName ] = true;
      return value;
    }

    // return cast value
    return r;

  },

  arrayTypeCast: function( definition, value, fieldName, options, failedCasts){
    return Array.isArray( value ) ? value : [ value ];
  },


  geoTypeCast: function( definition, value, fieldName, options, failedCasts){

    var r = value;

    if( typeof( r ) === 'string' ){
      try {
          // Attempt to stringify
          r = CircularJSON.parse( r );

      } catch( e ){
        failedCasts[ fieldName ] = true;
        return value;
      }
    }

    // Force the type to be the geoType regardless of what was passed
    r.type = definition.geoType;

    // Basically the only allowed key is `coordinates`
    if( Object.keys( r ).length != 2 ){
      failedCasts[ fieldName ] = true;
      return value;
    }

    // There must be a `coordinates` element, and it needs to be an array
    if( ! Array.isArray( r.coordinates ) ){
      failedCasts[ fieldName ] = true;
      return value;
    }

    // It's "point" by default
    definition.geoType = definition.geoType || 'Point';

    switch( definition.geoType ){
      case 'Point':
        // Every coordinate needs to be a number
        if( r.coordinates.length != 2 || ! r.coordinates.every( function( i ){ return typeof( i ) === 'number' } ) ){
          failedCasts[ fieldName ] = true;
          return value;
        }
      break;

      default:
        throw( new Error("Invalid geoType: " + definition.geoType ) );
      break;
    }

    // If it's taking data out, add the type to whatever was taken out
    if( options.deserialize ){
      r.type = definition.geoType;
    }

    // R is good and valid
    return r;
  },


  serializeTypeCast: function( definition, value, fieldName, options, failedCasts ){

    var r;

    if( options.deserialize ){

      if( typeof( value ) !== 'string' ){
        failedCasts[ fieldName ] = true;
        return value;
      }


      try {
          // Attempt to stringify
          r = CircularJSON.parse( value );

          // It worked: return r
          return r;
      } catch( e ){
        failedCasts[ fieldName ] = true;
        return value;
      }

    } else {

      try {
          r = CircularJSON.stringify( value );

          // It worked: return r
          return r;
      } catch( e ){
        failedCasts[ fieldName ] = true;
        return value;
      }

    //
    }
  },

  // Cast an ID for this particular engine. If the object is in invalid format, it won't
  // get cast, and as a result check will fail
  booleanTypeCast: function( definition, value,  fieldName, options, failedCasts ){

    if( typeof( value ) === 'string' ){
      if( value == ( definition.stringFalseWhen || 'false')  ) return false;
      else if( ( value == ( definition.stringTrueWhen || 'true')  ) || (  value == ( definition.stringTrueWhen || 'on')  )  ) return true;
      else return false;

    } else {
      return !!value;
    }
  },

  // Cast an ID for this particular engine. If the object is in invalid format, it won't
  // get cast, and as a result check will fail
  idTypeCast: function( definition, value,  fieldName, options, failedCasts ){
    var n = parseInt( value );
    if( isNaN( n ) ){
      failedCasts[ fieldName ] = true;
      return value;
    } else {
      return n;
    }
  },
  
  jsonTypeCast: function(definition, value, fieldName, options, failedCasts) {
    let json

    try {
      json = JSON.parse(value)
    } catch (e) {
      failedCasts[fieldName] = true
      return
    }

    return json
  },


  // Built-in parameters

  minTypeParam: function( p ){

    if( p.definition.type === 'number' && p.value && p.value < p.parameterValue ){
      p.errors.push( { field: p.fieldName, message: 'Field is too low: ' + p.fieldName } );
    }
    if( p.definition.type === 'string' && p.value && p.value.length < p.parameterValue ){
      p.errors.push( { field: p.fieldName, message: 'Field is too short: ' + p.fieldName } );
    }
  },

  maxTypeParam: function( p ){
    if( p.definition.type === 'number' && p.value && p.value > p.parameterValue ){
      p.errors.push( { field: p.fieldName, message: 'Field is too high: ' + p.fieldName } );
    }

    if( p.definition.type === 'string' && p.value && p.value.length > p.parameterValue ){
      p.errors.push( { field: p.fieldName, message: 'Field is too long: ' + p.fieldName } );
    }

  },

  validatorTypeParam: function( p ){
    if( typeof( p.parameterValue ) !== 'function' )
      throw( new Error("Validator function needs to be a function, found: " + typeof( p.parameterValue ) ) );

    var r = p.parameterValue.call( this, p.object, p.object[ p.fieldName ], p.fieldName );
    if( typeof( r ) === 'string' ) p.errors.push( { field: p.fieldName, message: r } );
  },

  uppercaseTypeParam: function( p ){
    if( typeof( p.value ) !== 'string' ) return;
    return  p.value.toUpperCase();
  },
  lowercaseTypeParam: function( p ){
    if( typeof( p.value ) !== 'string' ) return;
    return  p.value.toLowerCase();
  },

  trimTypeParam: function( p ){
    if( typeof( p.value ) !== 'string' ) return;
    return  p.value.substr( 0, p.parameterValue );
  },

  defaultTypeParam: function( p ){
    var v;
    if( typeof( p.objectBeforeCast[ p.fieldName ] ) === 'undefined' ){
      if( typeof(  p.parameterValue ) === 'function' ){
        v = p.parameterValue.call();
      } else {
        v = p.parameterValue;
      }
      p.object[ p.fieldName ] = v;
    }
  },


  requiredTypeParam: function( p ){
    if( typeof( p.objectBeforeCast[ p.fieldName ]) === 'undefined'  && p.parameterValue ){
      p.errors.push( { field: p.fieldName, message: 'Field required: ' + p.fieldName } );
    }
  },

  notEmptyTypeParam: function( p ){

    // if( ! Array.isArray( p.value ) && ( typeof( p.objectBeforeCast[ p.fieldName ]) === 'undefined' || p.objectBeforeCast[ p.fieldName ] == '')) {
//    if( ! Array.isArray( p.value ) &&  p.objectBeforeCast[ p.fieldName ] == '' && p.parameterValue) {
    var bc = p.objectBeforeCast[ p.fieldName ];
    var bcs = typeof( bc ) !== 'undefined' && bc !== null && bc.toString ? bc.toString() : '';
    if( ! Array.isArray( p.value ) &&  typeof( bc ) !== 'undefined' && bcs === '' && p.parameterValue) {
      p.errors.push( { field: p.fieldName, message: 'Field cannot be empty: ' + p.fieldName } );
    }
  },



  // Options and values used:
  //  * options.onlyObjectValues              -- Will apply cast for existing object's keys rather than the
  //                                             schema itself
  //  * options.skipCast                      -- To know what casts need to be skipped
  //
  //  * this.structure[ fieldName ].required  -- To skip cast if it's `undefined` and it's NOT required
  //  //* this.structure[ fieldName ].protected -- To skip cast if it's `undefined` and it's protected
  //
  _cast: function( object, options, cb ){

    var type, failedCasts = {}, failedRequired = {};
    options = typeof( options ) === 'undefined' ? {} : options;
    var targetObject;
    var resultObject = {};

    // Set the targetObject. If the target is the object itself,
    // then missing fields won't be a problem
    if( options.onlyObjectValues ) targetObject = object;
    else targetObject = this.structure;

    var ignoreFields = options.ignoreFields || [];
    var ignoreFieldsWithAttributes = options.ignoreFieldsWithAttributes || [];

    for( var fieldName in targetObject ){

      // Getting the definition
      definition = this.structure[ fieldName ];

      // The field is ignored: skip check
      if( ignoreFields.indexOf( fieldName ) !== -1  ) continue;

      // Check if the field is to be ignored due to a field having a
      // (truly) attribute listed in ignoreFieldsWithAttributes
      var ignored = false;
      ignoreFieldsWithAttributes.forEach( function( attribute ){
        if( definition[ attribute ] ) ignored = true;
      });
      if( ignored ) continue;

      // Copying the value over
      if( typeof( object[ fieldName ] ) !== 'undefined' ) resultObject[ fieldName ] = object[ fieldName ] ;

      // If the definition is undefined, and it's an object-values only check,
      // then the missing definition mustn't be a problem.
      if( typeof( definition ) === 'undefined' && options.onlyObjectValues ) continue;

      // Skip casting if so required by the skipCast array
      if( Array.isArray( options.skipCast )  && options.skipCast.indexOf( fieldName ) != -1  ){
        continue;
      }

      // Skip casting if value is `undefined` AND it's not required
      if( !definition.required && typeof( object[ fieldName ] ) === 'undefined' ){
        continue;
      }

      // Skip casting if value is `undefined` and it's "protected"
      // == NOTE: TODO: Not sure we need this just yet===
      //if( definition.protected && typeof( object[ fieldName ] ) === 'undefined' ){
      //  continue;
      //}

      // Skip casting if value is `undefined` AND it IS required
      // Also, set failedRequired for that field, so that no param will be applied to it except `required`
      if( definition.required && typeof( object[ fieldName ] ) === 'undefined' ){
        failedRequired[ fieldName ] = true;
        continue;
      }

      // Run the xxxTypeCast function for a specific type
      if( typeof( this[ definition.type + 'TypeCast' ]) === 'function' ){
        var result = this[ definition.type + 'TypeCast' ].call( this, definition, object[ fieldName ], fieldName, options, failedCasts );
        if( typeof( result ) !== 'undefined' ) resultObject[ fieldName ] = result;

      } else {
        throw( new Error("No casting function found, type probably wrong: " + definition.type ) );
      }

    }

    // That's it -- return resulting Object
    cb( null, resultObject, failedCasts, failedRequired );

  },

  // Options and values used:
  //  * options.onlyObjectValues             -- Will skip appling parameters if undefined and
  //                                            options.onlyObjectValues is true
  //  * options.skipParams                   -- Won't apply specific params for specific fields
  //  * options.

  _params: function( object, objectBeforeCast, options, failedCasts, failedRequired, cb ){

    var type;
    var options = typeof(options) === 'undefined' ? {} : options;

    var errors = [];
    var resultObject = {}

    // First of all, if it's not in the schema, it's not allowed
    var ignoreFields = options.ignoreFields || [];
    var ignoreFieldsWithAttributes = options.ignoreFieldsWithAttributes || [];

    for( var k in objectBeforeCast ){

      // The field is ignored: skip check
      if( ignoreFields.indexOf( k ) !== -1  ) continue;

      if( typeof( this.structure[ k ] ) === 'undefined' ){
        errors.push( { field: k, message: 'Field not allowed: ' + k } );
      }
    }

    // Copying object into resultObject
    for( k in object ){
      if( typeof( object[ k ]) !== 'undefined' ) resultObject[ k ] = object[ k ];
    }

    // Scan schema
    for( var fieldName in this.structure ){

      // Field is to be ignored: skip everything
      if( ignoreFields.indexOf( k ) !== -1  ) continue;

      // Check if the field is to be ignored due to a field having a
      // (truly) attribute listed in ignoreFieldsWithAttributes
      var definition = this.structure[ fieldName ];
      var ignored = false;
      ignoreFieldsWithAttributes.forEach( function( attribute ){
        if( definition[ attribute ] ) ignored = true;
      })
      if( ignored ) continue;

      // The `onlyObjectValues` option is on: skip anything that is not in the object
      if( options.onlyObjectValues && typeof( object[ fieldName ] ) === 'undefined' ) continue;

      if( ! failedCasts[ fieldName ] ) {
        definition = this.structure[ fieldName ];

         // Run specific functions based on the passed options
        for( var parameterName in definition ){

          // If it's to be skipped, we shall skip -- e.g. `options.skipParams == { tabId: 'required' }` to
          // skip `required` parameter for `tabId` field
          if( typeof( options.skipParams ) === 'object' && options.skipParams !== null ){
            var skipParams = options.skipParams[ fieldName ];
            if( Array.isArray( skipParams ) && skipParams.indexOf( parameterName) !== -1  ) continue;
          }

          if( parameterName != 'type' && typeof( this[ parameterName + 'TypeParam' ]) === 'function' ){

            // If `required` failed during casting, then skip other parameters --
            // `required` is the ONLY parameter that will actually get called
            if( !( failedRequired[ fieldName] && parameterName !== 'required' ) ){

              // Store the length of errors; later, it will use this to check that it hasn't grown
              var errLength = errors.length;

              var result = this[ parameterName + 'TypeParam' ].call( this, {
                value: resultObject[ fieldName ],
                valueBeforeParams: object[ fieldName ],
                object: resultObject,
                objectBeforeCast: objectBeforeCast,
                objectBeforeParams: object,
                fieldName: fieldName,
                definition: definition,
                parameterName: parameterName,
                parameterValue: definition[ parameterName ],
                errors: errors,
                options: options,
              } );

              if( typeof( result ) !== 'undefined' ) resultObject[ fieldName ] = result;

              // If `errors` grew, the following parameters will not be applied
              if( errors.length != errLength ) break;
            }

          }
        }
      }
    }
    cb( null, resultObject, errors );

  },

  _validate: function( finalObject, originalObject, castObject, options, cb ){

    if( typeof( this.options ) === 'object'  && typeof( this.options.validator) === 'function' ){
      this.options.validator.call( this, finalObject, originalObject, castObject, options, cb );
    } else {
      cb( null, [] );
    }
  },

  // Options and values used (the ones used by _cast() and _params() together)
  //
  //  * options.onlyObjectValues             -- Will apply cast for existing object's keys rather than the schema itself
  //  * options.skip                         -- Skip the whole process
  //  * options.skipCast                     -- To know what casts need to be skipped
  //  * options.skipParams                   -- Won't apply specific params for specific fields
  //  * options.skipValidation               -- Skip the call to validate()
  //
  //  * this.structure[ fieldName ].required -- To skip cast if it's `undefined` and it's NOT required
  //
  // Note that the only special parameter is 'required' -- it's only special because _cast() won't cast
  // it if it's `undefined` and it's not required. Otherwise, casting will make validation fail for unrequired and absent values
  //
  // This will run _cast, _param and _validate
  validate: function( originalObject, options, cb ){

    var self = this;

    if( typeof( cb ) === 'undefined' ){
      cb = options;
      options = {};
    }

    options = typeof( options ) === 'undefined' ? {} : options;

    // If `option.skipValidation` is set, then validation is actually skipped,
    // an exact copy of `originalObject` is provided instead.
    // This provides an easy way, for callers, to have validation as an option easily
    // (they still call `validate()`, it just doesn't do anything)
    if( options.skip ){
      var t = {};
      for( var k in originalObject ) t[ k ] = originalObject[ k ];
      return cb( null, t, [] );
    }

    self._cast( originalObject, options, function( err, castObject, failedCasts, failedRequired ){

      if( err ) return cb( err );

      self._params( castObject, originalObject, options, failedCasts, failedRequired, function( err, paramObject, errors ){
        if( err ) return cb( err );

        Object.keys( failedCasts ).forEach( function( fieldName ){
          errors.push( { field: fieldName, message: "Error during casting" } );
        });

        // If validation is to be skipped, the function ends here. Only casts were done!
        if( options.skipValidation ){
          return cb( null, paramObject, errors );
        }

        self._validate( paramObject, originalObject, castObject, options, function( err, validateErrors ) {
          if( err ) return cb( err );

          if( Array.isArray( validateErrors ) ){
            cb( null, paramObject, Array.prototype.concat( errors, validateErrors ) );
          } else {

            cb( null, paramObject, errors );
          }

        });
      });

    });

  },


  cleanup: function( object, parameterName ){
    newObject = {};
    for( var k in object ){
       //if( ! this.structure[ k ] ) throw( new Error("FATAL: attempted to deal with field " + k + " which is not in the schema"));
       if( ! this.structure[ k ] ) continue;
       if( this.structure[ k ][parameterName] ) {
         delete object [ k ];
         newObject[ k ] = object[ k ];
       }
    }
    return newObject;
  },


  // The default id maker (just return a random number )
  // available as an object method
  makeId: function( object, cb ){
    SimpleSchema.makeId( object, cb );
  },

});
Ejemplo n.º 16
0
  hotplate.hotEvents.emit('sharedFunctions', function( err, sharedFunctions) {
    if( err ){
      done( err );
    } else {

      // Gets the results, and adds them to the sharedValidators hash which
      // will then get used
      sharedFunctions.onlyResults().forEach( function( functions ){
        for( var k in functions ){
          // If it ends with "Validator", then it's a validator
          if( k.indexOf('Validator', k.length - 'Validator'.length) !== -1 ){
            sharedValidators[ k ] = functions[ k ];
          }
        }
      });


      classes.HotSchemaMixin = declare( null, {

        sharedValidatorTypeParam: function( p ){
          if( typeof( p.parameterValue ) !== 'string' )
            throw( new Error("Validator needs to be a string, found: " + typeof( p.parameterValue ) ) );

      
          var f = sharedValidators[ p.parameterValue ];
 
          if( f && ! f( p.value ) ){
            var msg = f( false );
            p.errors.push( { field: p.fieldName, message: msg, mustChange: true } );
          }
        }
      });

      classes.HotSchema = declare( [ SimpleSchema, hotplate.config.get( 'hotplate.SchemaMixin'), classes.HotSchemaMixin ] );

      classes.HotStoreMixin = declare( null, {

        killComet: false,
        chainErrors: 'all',

        echoAfterPutNew: true,
        echoAfterPutExisting: true,
        echoAfterPost: true,

        _getTabId: function( req ){

          // If the tab header wasn't there, then there is no way to broadcast -- abord
          if( typeof( this._req ) === 'undefined' ) return null;

          var tabId = this._req.headers['x-hotplate-tabid'];
          if( tabId == '' ){
            tabId = null;
          }
          return tabId; 
        },


        _broadcast: function( killCometOption, type, objectId, object, options ){

          var makeTabIdHash;
          var fromUserId;
          var storeTarget;

          // No comet required (from the option, or for the store's own request
          if( this.killComet || killCometOption ) return;

          // Sets the userId if it's a remote request
          if( this.remote ){
             fromUserId = this._req.session.userId;
          } else {
             fromUserId = null;
          }

          // If it's in a multi-home environment, use the specific multi-home filter
          // if( hotplate.config.get( 'hotCoreMultiHome.enabled' ) && object.workspaceId ) makeTabIdHash = exports.makeTabIdHashForMultihome;
          if( hotplate.config.get( 'hotCoreMultiHome.enabled' ) ) makeTabIdHash = hotCoreMultiHome.makeTabIdHashForMultihome;

          var message = { type: type, storeName: this.storeName, objectId: objectId, object: object };

          // If options.beforeId is set, make up a fake record with 'before' set as record with matching ID is idProperty
          // We cannot really send the whole record as we never fetch it fully
          if( options.beforeId ){
            if( options.beforeId === 'null' ){
              message.before = null;
            } else {
              message.before = {};
              message.before[ this.idProperty ] = options.beforeId;
            }
          }
          if( options.relocation) message.relocation = true;

          // Actually invoke the broadcast
          hotplate.hotEvents.emit('cometBroadcast', fromUserId, this._getTabId(), makeTabIdHash, message,  function(){}  );

        },

        afterPutExisting: function afterPutExisting( params, body, options, doc, fullDoc, docAfter, fullDocAfter, overwrite, done ){
          var self = this;
          this.inheritedAsync( afterPutExisting, arguments, function(){
            self._broadcast( options.killComet, 'storeRecordUpdate', docAfter[ self.idProperty], docAfter, options );
            done( null );
          });
        },    

        afterPost: function afterPost( params, body, options, doc, fullDoc, done ){
          var self = this;
          this.inheritedAsync( afterPost, arguments, function(){
            self._broadcast( options.killComet, 'storeRecordCreate', doc[ self.idProperty], doc, options );
            done( null );
          });
        },

        afterPutNew: function afterPutNew( params, body, options, doc, fullDoc, overwrite, done ){
          var self = this;
          this.inheritedAsync( afterPutNew, arguments, function(){
            self._broadcast( options.killComet, 'storeRecordCreate', doc[ self.idProperty], doc, options );
            done( null );
          });
        },

        afterDelete: function afterDelete( params, body, options, doc, fullDoc, done ){
          var self = this;
          this.inheritedAsync( afterDelete, arguments, function(){
            self._broadcast( options.killComet, 'storeRecordRemove', doc[ self.idProperty ], doc, options ); 
            done( null );
          });
        },    
      });

      classes.HotStore = declare( [ JsonRestStores, classes.HotStoreMixin ],{
        DbLayer: declare( [ SimpleDbLayer, hotplate.config.get('hotplate.DbLayerMixin') ], { db: hotplate.config.get( 'hotplate.db' ) } )
      });

      done( null, classes );

    }
  });
Ejemplo n.º 17
0
exports.MultiHomeBasicPermissionsMixin = declare( null, {

  _checkWorkspaceId: function( params, cb ){
     var self = this;
 
    if( ! self._req.session.userId ) return cb( new self.UnauthorizedError() );

    if( params.workspaceId ){
      userInWorkspace( self._req.session.userId, params.workspaceId, function( err, there ){
        if( ! there ){
          cb( null, false );
        } else {
          cb( null, true );
        }
      });
    } else {
      cb( null, true );
    }
  },

  _checkUserIdMultiHome: function( params, cb ){
    var self = this;

    if( ! self._req.session.userId ) return cb( new self.UnauthorizedError() );

    if( params.userId ){

      if( params.userId.toString() !== self._req.session.userId.toString() ){
        cb( null, false );
      } else {
        cb( null, true );
      }
    } else {
      cb( null, true );
    }
        
  },

  _checkWorkspaceIdAndUserId: function( params, cb ){
    var self = this;

    self._checkWorkspaceId( params, function( err, res ){
      if( err ){
        cb( err );
      } else {
        if( ! res ){
          cb( null, false );
        } else {
         self._checkUserIdMultiHome( params, function( err, res ){
            if( err ){
              cb( err );
            } else {
              cb( null, res );
            }
          });

        }
      }
    });
  },
  checkPermissionsPost: function checkPermissionsPost( params, body, options, cb ){
    var self = this;

    this.inheritedAsync( checkPermissionsPost, arguments, function( err, res ){
      if( err ){
        cb( err );
      } else {
        if( ! res ){
          cb( null, false );
        } else {
          self._checkWorkspaceIdAndUserId( params, cb );
        }
      }
    });
  },
  checkPermissionsPutNew: function checkPermissionsPutNew( params, body, options, cb ){
    var self = this;

    this.inheritedAsync( checkPermissionsPutNew, arguments, function( err, res ){
      if( err ){
        cb( err );
      } else {
        if( ! res ){
          cb( null, false );
        } else {
          self._checkWorkspaceIdAndUserId( params, cb );
        }
      }
    });
  },
  checkPermissionsPutExisting: function checkPermissionsPutExisting( params, body, options, doc, fullDoc, cb ){
    var self = this;

    this.inheritedAsync( checkPermissionsPutExisting, arguments, function( err, res ){
      if( err ){
        cb( err );
      } else {
        if( ! res ){
          cb( null, false );
        } else {
          self._checkWorkspaceIdAndUserId( params, cb );
        }
      }
    });
  },
  checkPermissionsGet: function checkPermissionsGet( params, body, options, doc, fullDoc, cb ){
    var self = this;

    this.inheritedAsync( checkPermissionsGet, arguments, function( err, res ){
      if( err ){
        cb( err );
      } else {
        if( ! res ){
          cb( null, false );
        } else {
          self._checkWorkspaceId( params, cb );
        }
      }
    });
  },
  checkPermissionsGetQuery: function checkPermissionsGetQuery( params, body, options, cb ){
    var self = this;

    this.inheritedAsync( checkPermissionsGetQuery, arguments, function( err, res ){
      if( err ){
        cb( err );
      } else {
        if( ! res ){
          cb( null, false );
        } else {
          self._checkWorkspaceId( params, cb );
        }
      }
    });
  },
  checkPermissionsDelete: function checkPermissionsDelete( params, body, options, doc, fullDoc, cb ){
    var self = this;

    this.inheritedAsync( checkPermissionsDelete, arguments, function( err, res ){
      if( err ){
        cb( err );
      } else {
        if( ! res ){
          cb( null, false );
        } else {
          self._checkWorkspaceIdAndUserId( params, cb );
        }
      }
    });
  },


});