Git Product home page Git Product logo

mongoose-multitenancy's People

Contributors

jorgecuesta avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Forkers

shreyasdn

mongoose-multitenancy's Issues

Discriminators Voiding Search Results

Hi there,

I'm using your package to fix some old code which used the original mongoose-multitenant package, and everything's working great except for the discriminator plugin. Whenever I search for something inside a model it automatically appends a search query for __t.

Example:
req.foo.getModel('Bar').find()

becomes the query:

foo__bars.findOne({ __t: 'foo.Bar' })

The __t search query does not return any results while a manual empty query does. I'm not sure if it's because the original models were created without discriminators and the options are now set in mongo, or if I'm doing something wrong elsewhere.

Or do I just need to write a script and append all existing documents with the appropriate __t data? Is there a way to toggle the discriminator functionality using multitenancy.setup()?

Any help you can offer is appreciated. Relevant code snippets below.

boot.js - Registers models, connects to dbs, and starts express app

if (process.env.NODE_ENV === 'production'){require('newrelic');}
var mt = require('mongoose-multitenancy');
global.log = require('./lib/services/log.js');

var deferred = Q.defer();

mt.setup('__');

require('./lib/services/modelIndex').register().then(function() {
  var err, error;
  log.info('Registered models');
  try {
    return Q.all([require('./lib/mongo').connect(), require('./lib/redis').connect()]).then(function() {
      var app, err, error;
      console.log('Connected!!!');
      log.info('Connected to databases');
      try {
        app = require('./lib/server').boot();
      } catch (error) {
        err = error;
        console.log(err.stack);
      }
      return deferred.resolve(app);
    }).fail(function(err) {
      log.error('ERROR');
      return deferred.reject(err);
    }).done();
  } catch (error) {
    err = error;
    return deferred.reject(err);
  }
}, function(err) {
  console.log('ERROR:', err);
  return deferred.reject(err);
});

module.exports = deferred.promise;

mongo.js - Connects to mongodb

var mongooseTypes = require('mongoose-types');
var mt = require('mongoose-multitenancy');
var Q = require('q');

log.info('Pulled in mongo');

module.exports = {
  connection: null,
  connect: function() {
    var deferred = Q.defer();
    mongooseTypes.loadTypes(mongoose);
    if (process.env.NODE_ENV !== 'production') {
      mongoose.connect('mongodb://localhost/db');
    } else {
      console.log('Mongo connecting to:', process.env.MONGODB_URI);
      mongoose.connect(process.env.MONGODB_URI);
    }
    if (process.env.NODE_ENV !== 'production') {
      mongoose.set('debug', true);
    }
    mt.setup('__');
    this.connection = mongoose.connection;
    mongoose.connection.on('error', function(error) {
      deferred.reject(error);
      return log.error(error);
    });
    mongoose.connection.on('connected', function() {
      log.info('Mongo connected - resolving promise');
      return deferred.resolve();
    });
    return deferred.promise;
  },
  $$dropCollection: function(col) {
    var deferred = Q.defer();
    if (process.env.NODE_ENV !== 'test') {
      return log.error('Cannot drop production collection!');
    }
    col = col.name.split('.');
    if (col[1] === 'system') {
      deferred.resolve();
      return deferred.promise;
    }
    var collectionName = col.pop();
    mongoose.connection.db.dropCollection(collectionName, function(err) {
      if (err) {
        return deferred.reject(err);
      }
      return deferred.resolve();
    });
    log.info('Returning promise');
    return deferred.promise;
  },
  drop: function() {
    var deferred = Q.defer();
    if (process.env.NODE_ENV !== 'test') {
      return log.error('Cannot drop production DB!');
    }
    mongoose.connection.db.collectionNames((function(_this) {
      return function(err, collections) {
        var col, i, len;
        if (err) {
          return deferred.reject(err);
        }
        var promises = [];
        for (i = 0, len = collections.length; i < len; i++) {
          col = collections[i];
          promises.push(_this.$$dropCollection(col));
        }
        return Q.all(promises).then(function() {
          return deferred.resolve();
        }, function(err) {
          return deferred.reject(err);
        });
      };
    })(this));
    return deferred.promise;
  }
};

foo.js - Defines foo schema

var Schema = mongoose.Schema;
var Q = require('q');

var schema = new Schema({
  // lots of JSON
});

schema.methods.getModel = function(model) {
  return mongoose.mtModel(this.uniqid + '.' + model);
};

schema.pre('save', function(done) {
  var user, userClass;
  if (this.isNew) {
    userClass = mongoose.mtModel(this.uniqid + '.User');
    user = new userClass({
      username: username,
      password: password,
      role: 'global_admin',
      activated: true
    });
    return user.save(function(err) {
      if (err) {
        return done(err);
      }
      return done();
    });
  } else {
    return done();
  }
});

module.exports = mongoose.model('Foo', schema);

bar.js - Defines bar schema.

var cascade = require('cascading-relations');
var _ = require('underscore');
var Q = require('q');

var barSchema = new mongoose.Schema({
  // JSON
});

barSchema.plugin(cascade);

barSchema.pre('save', function(next) {
  var i, len, baz, promises, ref;
  this.changedC = !this.isNew && this.isModified('_c');
  if (!this.isNew && (this.isModified('type') || this.isModified('_c'))) {
    promises = [];
    ref = this._baz;
    for (i = 0, len = ref.length; i < len; i++) {
      baz= ref[i];
      promises.push(this.getModel('Baz').findByIdAndUpdate(baz, {
        // JSON
      }, function(err, res) {
        return console.log(err, res);
      }));
    }
    return Q.all(promises).then(function() {
      return next();
    }, function(err) {
      return next(err);
    });
  } else {
    return next();
  }
});
  return deferred.promise;
};

module.exports = mongoose.mtModel('Bar', barSchema);

server.js - Starts express app and establishes api endpoints. Creates data from requests.

var passport = require('passport');
var _ = require('underscore');
var localPath = require('fs')
  .realpathSync('');
var fs = require('fs');
var data = JSON.parse(fs.readFileSync(localPath + '/package.json'));
var buildNumber = data.build_number;

module.exports = {
  boot: function() {
    var createDataFromReq, foos, err, error, error1, error2,
      error3, error4, error5, error6, error7, error8, error9, mongoose, 
      publicPath;
    log.info('Booting server');

    require('babel-register');
    var path = require('path');
    var express = require('express');
    var favicon = require('serve-favicon');
    var async = require('async');
    var bodyParser = require('body-parser');
    var colors = require('colors');
    var compression = require('compression');
    var cookieParser = require('cookie-parser');
    var errorHandler = require('errorhandler');
    var fs = require('fs');
    var logger = require('morgan');
    var methodOverride = require('method-override');
    var mongoose = require('mongoose');
    var mongooseTypes = require('mongoose-types');
    var passport = require('passport');
    var request = require('request');
    var React = require('react');
    var ReactDOM = require('react-dom/server');
    var Router = require('react-router');
    var session = require('express-session');
    var swig = require('swig');
    var url = require('url');
    var xml2js = require('xml2js');
    var _ = require('underscore');
    var routes = require('../../app/routes');

    // Setup Server
    var app = express();
    var err, error;

      mongoose = require('mongoose');
      mongoose.model('Foo')
        .count(function(err, count) {
          var foo;
          if (count < 1) {
            foo = new(mongoose.model('Foo'))({
              // JSON
            });
            return foo.save(function(err, res) {
              var error1, uClass, user;
              try {
                uClass = res.getModel('User');
                user = new uClass({
                  username: username,
                  password: password,
                  role: 'global_admin',
                  activated: true
                });
                return user.save(function() {
                  return console.log('Saved user!');
                });
              } catch (error1) {
                err = error1;
                return console.log(err.stack);
              }
            });
          }
        });
    }

    var PORT = 3000;
    if (app.get('env') === 'test') {
      PORT = 3001;
    }
    app.set('port', process.env.PORT || PORT);
    app.use(errorHandler());
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({
      extended: false
    }));
    app.use(methodOverride());
    app.use(cookieParser());
    foos= require('./components/foos');
    try {
      require('./redis')
        .setupSession(app);
    } catch (error1) {
      err = error1;
      console.log(err.stack);
    }
    app.use(express.static(path.join(__dirname, '../../public')));
    app.use(foos.setFooConfig());
    app.use(passport.initialize());
    app.use(passport.session());

    try {
      foos.registerEndpoints(app);
    } catch (error2) {
      error = error2;
      console.log(error.stack);
    }
    try {
      require('./components/bars')
        .registerEndpoints(app);
    } catch (error5) {
      error = error5;
      console.log(error.stack);
    }
    createDataFromReq = function(req) {
      var ad1, ad2, foo, index1, index2, staticAds;
      foo= req._foo.toObject();
      staticAds = _.clone(foo.ads["static"]);
      if (staticAds.length) {
        index1 = Math.floor(Math.random() * staticAds.length);
        ad1 = staticAds.splice(index1, 1);
        if (ad1.length) {
          ad1 = ad1[0];
        } else {
          ad1 = null;
        }
        index2 = Math.floor(Math.random() * staticAds.length);
        ad2 = staticAds.splice(index2, 1);
        if (ad2.length) {
          ad2 = ad2[0];
        } else {
          ad2 = null;
        }
      }
      data = {
        // JSON
      };
      if (req.user) {
        data.user = {
          role: req.user.role,
          username: req.user.username,
          _id: req.user._id,
          loggedIn: true
        };
      }
      return data;
    };

    app.get('/api/bar/:id', function(req, res, next) {
        return req._foo.getModel('Bar').findById(req.params.id).populate(['_c', '_baz']).exec(function(err, bar) {
            if (err) return next(err);
            if (!bar) {
                return res.status(404);
            } else {
                data = createDataFromReq(req);
                data.page = {
                    bar: bar.toObject()
                };
                return res.send(data);
            }
        });

    server.listen(app.get('port'), function() {
      console.log('Express server listening on port ' + app.get('port'));
    });
  }
};

components/foos.js - Establishes API endpoints for foo. Configures which tenant should be called.

var mongoose = require('mongoose');
var mre = require('mongoose-rest-endpoints');
var auth = require('./auth');
var _ = require('underscore');
var Q = require('q');

var FooEndpoint;

var extend = function(child, parent) {
  for (var key in parent) {
    if (hasProp.call(parent, key)) child[key] = parent[key];
  }
  function ctor() {
    this.constructor = child;
  }
  ctor.prototype = parent.prototype;
  child.prototype = new ctor();
  child.__super__ = parent.prototype;
  return child;
}

var hasProp = {}.hasOwnProperty;

var Foos = {
  endpoint: FooEndpoint = (function(superClass) {
    extend(FooEndpoint, superClass);

    function FooEndpoint(url, model, config) {
      FooEndpoint.__super__.constructor.apply(this, arguments);
      if (model !== 'R' && model !== 'User' && model !== 'B' && model !== 'S') {
        this.tap('pre_filter', 'post', function(req, data, next) {
          var err;
          if (req.user.role === 'global_admin') {
            if (!data._c && req.url.indexOf('/api/cs') !== 0) {
              err = new Error('error');
              err.code = 400;
              throw err;
            }
          }
          if (req.user.role === 'administrator') {
            data._c = req.user._c;
          }
          return next(data);
        });
        this.tap('pre_filter', 'put', function(req, data, next) {
          var err;
          if (req.user.role === 'global_admin') {
            if (!data._c && req.url.indexOf('/api/cs') !== 0) {
              err = new Error('error');
              err.code = 400;
              throw err;
            }
          }
          if (req.user.role === 'administrator') {
            data._c = req.user._c;
          }
          return next(data);
        });
      }
    }

    FooEndpoint.prototype.register = function(app) {
      var request;
      request = require('mongoose-rest-endpoints').request;
      app.get(this.path + '/:id', this.$$middleware.fetch, (function(_this) {
        return function(req, res) {
          res.$mre.method = 'fetch';
          return new request(_this, req._foo.getModel(_this.modelId)).$fetch(req, res).then(function(response) {
            return res.send(response, 200);
          }, function(err) {
            if (err.code) {
              return res.send(err.message, err.code);
            } else {
              return res.send(500);
            }
          });
        };
      })(this));
      app.get(this.path, this.$$middleware.list, (function(_this) {
        return function(req, res) {
          res.$mre.method = 'list';
          return new request(_this, req._foo.getModel(_this.modelId)).$list(req, res).then(function(response) {
            return res.send(response, 200);
          }, function(err) {
            if (err.code) {
              return res.send(err.message, err.code);
            } else {
              return res.send(500);
            }
          });
        };
      })(this));
      app.post(this.path, this.$$middleware.post, (function(_this) {
        return function(req, res) {
          res.$mre.method = 'post';
          return new request(_this, req._foo.getModel(_this.modelId)).$post(req, res).then(function(response) {
            return res.send(response, 201);
          }, function(err) {
            if (err.code) {
              return res.send(err.message, err.code);
            } else {
              return res.send(500);
            }
          });
        };
      })(this));
      app.put(this.path + '/:id', this.$$middleware.put, (function(_this) {
        return function(req, res) {
          res.$mre.method = 'put';
          return new request(_this, req._foo.getModel(_this.modelId)).$put(req, res).then(function(response) {
            return res.send(response, 200);
          }, function(err) {
            if (err.code) {
              return res.send(err.message, err.code);
            } else {
              return res.send(500);
            }
          });
        };
      })(this));
      return app["delete"](this.path + '/:id', this.$$middleware["delete"], (function(_this) {
        return function(req, res) {
          res.$mre.method = 'delete';
          return new request(_this, req._foo.getModel(_this.modelId)).$delete(req, res).then(function() {
            return res.send(200);
          }, function(err) {
            if (err.code) {
              return res.send(err.message, err.code);
            } else {
              return res.send(500);
            }
          });
        };
      })(this));
    };

    return FooEndpoint;

  })(mre.endpoint),
  setFooConfig: function() {
    return function(req, res, next) {
      var host, port, url;
      url = req.get('host');
      url = url.split(':');
      host = url[0];
      port = url[1];

      return mongoose.model('Foo').findOne({
        $or: [
          {
            host: host
          }, {
            devhost: host
          }
        ]
      }, function(err, foo) {
        if (err) {
          res.status(500);
          if (req.url.indexOf('/api') === 0) {
            return res.send('Server error', 500);
          } else {
            return res.status(500);
          }
        } else if (!foo) {
          res.status(400);
          if (req.url.indexOf('/api') === 0) {
            return res.send('Bad request (Foo not found)', 400);
          } else {
            return res.status(400);
          }
        } else {
          req._foo = foo;

          next();
          return foo.getModel('User').count(function(err, count) {
            var uClass, user;
            if (!count) {
              uClass = foo.getModel('User');
              user = new uClass({
                // JSON
              });
              return user.save();
            }
          });
        }
      });
    };
  },
  registerEndpoints: function(app) {
    auth = require('./auth');
    return new mre.endpoint('/api/foos', 'Foo').addMiddleware('post', auth.requireRole('global_admin')).addMiddleware('put', auth.requireRole('global_admin')).addMiddleware('delete', auth.requireRole('global_admin')).register(app);
  },
};

module.exports = Foos;

components/bars.js - Registers endpoints for Bar schema. Connects to other schemas for aggregation/population.

var mongoose = require('mongoose');
var mre = require('mongoose-rest-endpoints');
var mt = require('mongoose-multitenancy');
var auth = require('./auth');
var moment = require('moment');
var foos = require('./foos');
var Q = require('q');

mt.setup('__');

var Bars= {
  registerEndpoints: function(app) {
    var clearFeatured;
    clearFeatured = function(req, res, next) {
      if (req.user.role !== 'global_admin') {
        delete req.body.featured;
      }
      return next();
    };
    this.$$barEndpoint = new foos.endpoint('/api/bars', 'Bar', {
      pagination: null
    }).populate('_baz').populate('_c').allowQueryParam(['$in_search.brs', '$in_search.bts', 'type', '_c', 'visible', 'featuredMobile']).cascade(['_baz'], function(data, path) {
      data._f = this._ff;
      return data;
    }).tap('pre_filter', 'list', function(req, filter, next) {
      if (req.query.detail && req.query.attribute) {
        filter['$or'] = [
          {
            'search.attribute': {
              $gte: req.query.attribute,
              $lte: req.query.detail
            }
          }, {
            'search.detail': {
              $gte: req.query.attribute,
              $lte: req.query.detail
            }
          }, {
            'search.attribute': {
              $gte: req.query.attribute
            },
            'search.detail': {
              $lte: req.query.detail
            }
          }
        ];
      } else if (req.query.attribute) {
        filter['search.detail'] = {
          $lte: req.query.detail
        };
      } else if (req.query.attribute) {
        filter['search.detail'] = {
          $gte: req.query.attribute
        };
      }
      return next(filter);
    }).tap('post_retrieve', 'put', auth.requireCAdmin).tap('post_retrieve', 'delete', auth.requireCAdmin).addMiddleware('post', auth.requireC()).addMiddleware('post', auth.requireRole('administrator')).addMiddleware('post', clearFeatured).addMiddleware('post', function(req, res, next) {
      if (req.user.role !== 'global_admin') {
        req.body._c = req.user._c;
      }
      return next();
    }).addMiddleware('put', clearFeatured).register(app);
    this.$$bazEndpoint = new foos.endpoint('/api/bazs', 'baz', {
      pagination: null
    }).allowQueryParam(['bType', 'lType', 'pType', '$lte_p', '$gte_p', '$lt_p', '$gt_p', 'bts', '$gt_bts', '$gte_bs', '$lt_bts', '$lte_bts', 'bds', '$gt_bds', '$gte_bds', '$lt_bds', '$lte_bds', 'availability']).tap('post_retrieve', 'put', auth.requireCAdmin).tap('post_retrieve', 'delete', auth.requireCAdmin).populate('_bar').populate('_c').addMiddleware('post', auth.requireC()).addMiddleware('post', auth.requireCAdminMiddleware()).addMiddleware('post', function(req, res, next) {
      var check, bar;
      bar= req.body._bar;
      if (!bar) {
        return res.send(400);
      }
      check = require('../utils/validId');
      if (!check(bar)) {
        return res.send('Bad ID', 400);
      }
      return req._foo.getModel('Bar').findById(bar, function(err, prop) {
        if (err) {
          return res.send(500);
        }
        if (!prop) {
          return res.send('Bar not found', 404);
        }
        if (req.user.role !== 'global_admin' && (prop._c.toString() !== req.user._c.toString() || req.body._c !== req.user._c.toString())) {
          return res.send(403);
        }
        req.body.bType = prop.type;
        return next();
      });
    }).register(app);
    new foos.endpoint('/api/rs', 'R').allowQueryParam('_bar').addMiddleware('post', function(req, res, next) {
      delete req.body.confirmed;
      if (!req.body.email) {
        return res.send('Email required', 400);
      }
      if (!req.body.headline) {
        return res.send('Headline required', 400);
      }
      if (!req.body.comments) {
        return res.send('Comments required', 400);
      }
      if (req._foo.requireEmaiDomain && req.body.email.indexOf(req._foo.universityEmail) < 1) {
        return res.send('Email must be through university.', 400);
      }
      return req._foo.getModel('R').count({
        _bar: req.body._bar,
        email: req.body.email
      }, function(err, count) {
        if (count > 0) {
          return res.send('You have already r this bar!', 403);
        }
        return next();
      });
    }).tap('pre_filter', 'fetch', function(req, data, next) {
      data.confirmed = true;
      return next(data);
    }).tap('pre_filter', 'list', function(req, data, next) {
      data.confirmed = true;
      return next(data);
    }).addMiddleware('put', auth.requireRole('global_admin')).addMiddleware('delete', auth.requireRole('global_admin')).tap('pre_response', 'post', function(req, data, next) {
      var email;
      email = require('../services/email');
      return email.send(data.email, req._foo.meta.title + ': Confirm Your R', 'r-confirm', {
        // JSON
          }
        },
        confirmUrl: 'http://' + req.host + '/r/' + data._id + '/confirm?code=' + data.confirmationString,
        staticUrl: function(url) {
          return req._foo.staticAssetsUrl + url;
        }
      }).then(function(result) {
        delete data.confirmationString;
        return next(data);
      }, function(err) {
        return next(err);
      });
    }).register(app);
    return app.get('/api/categories', function(req, res) {
      var total;
      total = {
        // JSON
        }
      };
      return req._foo.getModel('Baz').find({
        availability: {
          $ne: ''
        }
      }, 'bType lType', function(err, bazs) {
        var i, len, baz;
        for (i = 0, len = bazs.length; i < len; i++) {
          baz= baz[i];
          if (baz.bType === 'h') {
            total.hs.total++;
            if (baz.lType === 'wh') {
              total.hs.whole++;
            } else if (baz.lType === 'rm') {
              total.hs.rm++;
            }
          } else if (baz.bType === 'at') {
            total.ats.total++;
            if (baz.lType === 'wa') {
              total.ats.whole++;
            } else if (baz.lType === 'rm') {
              total.ats.room++;
            } else if (baz.lType === 'bd') {
              total.ats.bd++;
            }
          }
        }
        return res.send(total, 200);
      });
    });
  }
};

module.exports = Bars;

Usage of _id as String fails

hi,
First off great library and thanks for writing this. When using your library if we have _id as String in this case there is no way to add this to the new schema and this does get copied over from the schema definition. Seems like the issue is due to creation of schema without parameters in index.js.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.