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;