Created
May 4, 2014 14:05
-
-
Save Vadorequest/11517177 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
///<reference path='./../../../lib/def/defLoader.d.ts'/> | |
/** | |
* Package that contains all Models used to interact with the database. | |
*/ | |
export module Models { | |
/** | |
* Interface for all Models, except the parent class. | |
*/ | |
export interface CoreIModel{ | |
/** | |
* Name of the model. | |
* It's a helper to always get the name, from instance or static. | |
* MUST start by uppercase letter! | |
*/ | |
modelName: string; | |
/** | |
* Contains the static value of the public schema as object. | |
* It's a helper to always get the schema, from instance or static. | |
*/ | |
schema: mongoose.Schema; | |
/** | |
* Contains the static value of the object used to manipulate an instance of the model. | |
* It's a helper to always get the model, from instance or static. | |
*/ | |
model: any; | |
} | |
/** | |
* Parent class for all models. | |
* A model contains a mongoose schema and a mongoose model and other things. | |
*/ | |
export class CoreModel{ | |
/** | |
* Suffix used to load automatically models. | |
*/ | |
public static suffix: string = 'Model'; | |
/** | |
* Suffix used to load automatically models. | |
* It's a helper to always get the schema, from instance or static. | |
*/ | |
public suffix: string; | |
/** | |
* Name of the model. | |
* MUST start by uppercase letter! | |
*/ | |
public static modelName: string = ''; | |
/** | |
* Readable schema as object. | |
*/ | |
public static schema: any; | |
/** | |
* Object that contains all the indexes configuration to apply when a schema is created. | |
* Each configuration is inside an array, composed in two objects. | |
* The first object contains the fields. | |
* The second object contains the field options. | |
* | |
* @see http://mongoosejs.com/docs/api.html#schema_Schema-index | |
*/ | |
public static indexes: any = []; | |
/** | |
* Schema mongoose options. | |
* /!\ Don't share this object between child, that would become a MESS /!\ | |
* | |
* @see http://mongoosejs.com/docs/guide.html#options | |
* - autoIndex | |
* - capped | |
* - collection | |
* - id | |
* - _id | |
* - read | |
* - safe | |
* - shardKey | |
* - strict | |
* - toJSON http://mongoosejs.com/docs/api.html#document_Document-toObject | |
* - toObject http://mongoosejs.com/docs/api.html#document_Document-toObject | |
* - versionKey | |
*/ | |
public static schemaOptions: any = { | |
autoIndex: true, | |
id: true, | |
_id: true, | |
strict: true | |
}; | |
/** | |
* Source code executed to modify the mongoose Schema middleware. | |
* | |
* @see http://mongoosejs.com/docs/middleware.html | |
*/ | |
public static applyMiddleware = function(schema: mongoose.Schema){ | |
return schema; | |
}; | |
/** | |
* Schema as mongoose Schema type. | |
*/ | |
public static Schema: mongoose.Schema; | |
/** | |
* The mongoose model that uses the mongoose schema. | |
*/ | |
public static model: any; | |
/** | |
* Use static values as instance values. | |
*/ | |
constructor(){ | |
// Use static values as instance values. | |
this.suffix = CoreModel.suffix; | |
} | |
/** | |
* Returns a new mongoose.Schema customized instance. | |
* | |
* @param ChildModel Child model that made the call. | |
* @returns {*} | |
* @see http://mongoosejs.com/docs/2.7.x/docs/methods-statics.html | |
*/ | |
public static getNewSchemaInstance(ChildModel: any): mongoose.Schema{ | |
// Load the schema options, but don't save any modification in case of the schema wouldn't be overloaded by some childs and the values would be shared. | |
var config = JSON.parse(JSON.stringify(ChildModel.schemaOptions)); | |
if(!config.collection){ | |
// Use model name as default. | |
config.collection = _.lcFirst(ChildModel.modelName); | |
} | |
var schema: any = new mongoose.Schema(ChildModel.schema, config); | |
// Generate all indexes. | |
CoreModel._generateIndexes(schema, ChildModel); | |
// Overload methods. | |
/*schema.methods.toObject = function(callback){ | |
// Ts doesn't recognize this as a model, properties aren't accessible, we need to declare it as a any type. | |
var self: any = this; | |
console.log(self.id) | |
};*/ | |
// Return overloaded instance. | |
return ChildModel.applyMiddleware(schema); | |
} | |
/** | |
* Retrieves a new Model instance and overload it to add statics methods available for all Models. | |
* | |
* @param ChildModel | |
* @returns {*} | |
* @see http://mongoosejs.com/docs/2.7.x/docs/methods-statics.html | |
*/ | |
public static getNewModelInstance(ChildModel: any): any{ | |
// Get the Model class. | |
var Model: any = mongoose.model(ChildModel.modelName, ChildModel.Schema); | |
/** | |
************************************************************************************************** | |
************************ Extended Model static methods for all Models **************************** | |
************************************************************************************************** | |
*/ | |
/** | |
* Handler for all database/mongoose errors. | |
* | |
* /!\ Cannot return Message instance because of some limitations due to the location of the script in some way. /!\ | |
* | |
* @param err Error. | |
* @param trace Must contain an instance of traceback that contains everything useful to debug. | |
* @param callback Callback function to execute. | |
*/ | |
Model.errorHandler = (err: any, trace: any, callback: (message: (any) => any) => any) => { | |
// If this array contains useful information then use it, otherwise use the other, | |
// that will depend on the way the script is executed but only one of the two array is useful to debug. | |
var origin = trace[1].name !== null || trace[1].method !== null ? 1 : 0; | |
// Extract data from the trace. | |
var __filename = trace[origin].file; | |
var __path_filename = trace[origin].path; | |
var __function = trace[origin].name || trace[origin].method; | |
var __line = trace[origin].line; | |
// Will contains the error. | |
var message: any = []; | |
// Mongo error. | |
if(err && err.name && err.name == 'MongoError'){ | |
var _err = CoreMongoError.parseMongoError(err); | |
if(err.code == 11000){ | |
// Duplicate key on create. | |
message[0] = '__19'; | |
message[1] = {duplicateValue: _err.value, field: _err.field}; | |
}else if(err.code == 11001){ | |
// Duplicate key on update. | |
message[0] = '__20'; | |
message[1] = {duplicateValue: _err.value, field: _err.field}; | |
}else{ | |
// Non-managed mongo error. | |
if(dev()){ | |
// Return not only the message but also some information about the error. | |
message[0] = [['__21'], {err: _err}]; | |
}else{ | |
message = '__21'; | |
} | |
} | |
fs.appendFile(__config.path.base + __config.mongo.error.log, new Date() + ': ' + JSON.stringify({error: err, model: _err.model, _err: _err, filename: __filename, path_filename: __path_filename, function: __function, line: __line}) + '\n'); | |
}else if(err && err.name && err.name == 'ValidationError'){ | |
// Validation error from mongoose. | |
var _err = CoreMongoError.parseValidationError(err); | |
message[0] = []; | |
if(_err[0].type === 'required'){ | |
// Missing required value. Message. [0][1] could be args. | |
message[0][0] = '__33'; | |
message[0][1] = {field: _.ucFirst(_err[0].field), error: _err[0].type}; | |
}else{ | |
// Duplicate value. Message. | |
message[0][0] = '__24'; | |
message[0][1] = {duplicateValue: _err[0].value, field: _err[0].field, error: _err[0].type}; | |
} | |
if(dev()){ | |
// Will be send as args but not displayed in the message. | |
message[1] = {err: _err}; | |
} | |
fs.appendFile(__config.path.base + __config.mongo.error.log, new Date() + ': ' + JSON.stringify({error: err, model: _err.model, _err: _err, filename: __filename, path_filename: __path_filename, function: __function, line: __line}) + '\n'); | |
}else{ | |
// Another error? I don't know if that could happens, but manage it anyway. | |
message[0] = '__22'; | |
if(dev()){ | |
message[1] = {err: err};// Will be send as args but not displayed in the message. | |
} | |
fs.appendFile(__config.path.base + __config.mongo.error.log, new Date() + ': ' + JSON.stringify({error: err, model: _err.model}) + '\n'); | |
} | |
if(message.length){ | |
// Log errors in dev mod. | |
consoleDev('A DB error happened, consult ' + __config.mongo.error.log + ' file to get more information.'); | |
consoleDev(JSON.stringify(message) + ' in the file ' + __filename + ' at the function ' + __function + ':' + __line); | |
} | |
// Return an error, this IS NOT a Message instance! | |
callback(message); | |
}; | |
/** | |
* Check if the object exists and returns it in this case. | |
* | |
* @param object Object to find. | |
* @param callback Callback to execute. | |
* @return | |
* err Error if it happens. [null] | |
* found Found object or false. | |
*/ | |
Model.exists = (object, callback): any => { | |
// If object is null or false or empty or whatever, don't do the research, the result could be wrong! | |
if(!object){ | |
callback (null, false); | |
}else{ | |
Model.findOne(object, function (err, found) { | |
if (err){ | |
Model.errorHandler(err, ChildModel, callback); | |
}else if (found){ | |
callback(null, found); | |
}else{ | |
callback (null, false); | |
} | |
}); | |
} | |
}; | |
// Return overloaded instance. | |
return Model; | |
} | |
/** | |
* Generate all schma indexes. Basically complex indexes such s compound indexes. | |
* Simples indexes are defined inside the schema itself. | |
* | |
* @param schema Mongoose Schema. | |
* @param ChildModel Model static class where find the indexes configuration to apply. | |
* @private | |
*/ | |
public static _generateIndexes(schema: mongoose.Schema, ChildModel: any): void{ | |
ChildModel.indexes.forEach(function(index){ | |
var fields = index[0] || {}; | |
var options = index[1] || {}; | |
schema.index(fields, options); | |
}); | |
} | |
} | |
/** | |
* Class that manage MongoDb errors, used statically. | |
*/ | |
export class CoreMongoError{ | |
/** | |
* Parse a mongo error to returns data from it because Mongo returns really bad errors. | |
* | |
* @param err The mongo error. | |
* @returns { | |
* database, | |
* model, | |
* field, | |
* value | |
* } | |
*/ | |
public static parseMongoError(err): any{ | |
var _err: any = {}; | |
var _message: string = err.err; | |
/** | |
* E11000 - Duplicate key on create. | |
* E11001 - Duplicate key on update. | |
*/ | |
if(err.code == 11000 || err.code == 11001){ | |
var message = _message.split(':'); | |
// Get the database where the error was generated. | |
_err.database = message[0].split('.')[0]; | |
// Get the model where the error was generated. | |
_err.model = message[1].split('.')[1]; | |
// Get the field name where the error was generated. | |
_err.field = message[1].split('.')[2].split(' ')[0].replace('$', ''); | |
_err.field = _err.field.substr(0, _err.field.lastIndexOf('_')); | |
// Get the value, if exists. | |
_err.value = null; | |
if(message[3]){ | |
var _value: any = message[3].split('"'); | |
if(_value[1]){ | |
_err.value = _value[1].replace('\\', ''); | |
}else{ | |
_err.value = _value[0].split(' ')[1].trim(); | |
} | |
} | |
} | |
return _err; | |
} | |
/** | |
* Parse a mongoose validation error, probably generated during a save/update function. | |
* | |
* @param err The mongoose error. | |
* @returns {*} | |
*/ | |
public static parseValidationError(err): any{ | |
var _errors: any = new Array(); | |
var i = 0; | |
for(var error in err.errors){ | |
_errors[i] = []; | |
_errors[i]['field'] = err.errors[error]['path']; | |
_errors[i]['value'] = err.errors[error]['value']; | |
_errors[i]['type'] = err.errors[error]['type']; | |
i++; | |
} | |
return _errors; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment