kunagpal/express-boilerplate

View on GitHub
utils/misc/index.js

Summary

Maintainability
C
1 day
Test Coverage
/**
* @file Contains miscellaneous helpers used throughout the project.
*/
 
var _ = require('lodash'),
 
REQUIRED_VARS = ['GOOGLE_ID', 'GOOGLE_KEY', 'FACEBOOK_ID', 'FACEBOOK_KEY', 'COOKIE_SECRET', 'SESSION_SECRET',
'SENTRY_DSN', 'PORT', 'NODE_ENV'],
 
updateFields = ['$set', '$unset', '$rename'],
 
/**
* Generates an error handler for the provided type.
*
* @param {String} type - The type of the error, assigned to the name property of the err instance.
* @returns {Function} - A function that returns an error for the specified message and status, under the type.
*/
error = function (type) {
return function (message, status) {
var stack,
err = new Error(message);
 
err.name = type;
status && (err.status = status);
 
stack = err.stack.split('\n');
stack.splice(1, 1); // remove the element at index 1
 
err.stack = stack.join('\n');
 
return err;
};
};
 
/**
* Returns the pluralized equivalent of the provided string.
*
* @param {String} str - The string to be pluralized.
* @returns {String} - The pluralized equivalent of the provided string.
*/
exports.pluralize = function (str) {
if (!_.isString(str)) { return ''; }
 
return _.endsWith(str, 'y') ? str.slice(0, -1) + 'ies' : str + 's';
};
 
/**
* Checks for environment sanity right before the app starts.
*/
exports.checkVars = function () {
var subset = _(process.env).pick(REQUIRED_VARS).keys().value(), // eslint-disable-line no-process-env
missingVars = _.difference(REQUIRED_VARS, subset).toString();
 
if (!_.isEmpty(missingVars)) {
throw new Error(`${missingVars.toString()} environment variable(s) missing!`);
}
};
 
/**
* Creates an exportable model pseudo class that can be stubbed with helpers.
*
* @param {Object} modelName - The name of the model to be created.
* @param {Object} db - An instance of a MongoDB connection that can be used for making queries.
* @returns {Object} A model pseudo class that can be used for various CRUD operations.
*/
Function `makeModel` has 53 lines of code (exceeds 25 allowed). Consider refactoring.
Function `makeModel` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.
exports.makeModel = function (modelName, db) {
if (!(modelName && db)) { return {}; }
 
var defaults = {},
attributes = [],
model = require(path.resolve(`./database/${modelName}`)), // eslint-disable-line global-require
config = model && model.config,
autoEditedAt = config && config.autoEditedAt,
autoCreatedAt = config && config.autoCreatedAt,
collection = db.collection(_.toLower(modelName)),
prune = function (datum, field) {
// eslint-disable-next-line security/detect-object-injection
return _(datum && datum[field] || datum).pick(attributes).merge(field === '$set' && autoEditedAt && {
editedAt: new Date().toISOString()
}).value();
},
sanitiseInsert = function (datum) {
return _(datum).pick(attributes).defaults(defaults).merge(autoCreatedAt && {
createdAt: new Date().toISOString()
}).value();
},
sanitiseUpdate = function (datum) {
var result = _.pick(datum, updateFields);
 
if (_.isEmpty(result)) { return { $set: prune(datum) }; }
 
_.forEach(updateFields, function (field) {
var pruned = prune(result, field);
 
// eslint-disable-next-line security/detect-object-injection
!_.isEmpty(pruned) && (result[field] = pruned);
});
 
return result;
};
 
Object.freeze(config);
Object.freeze(config && config.rest);
 
_.forEach(model.fields, function (field) {
var name = field && field.key || field;
 
// Consider adding support for mandatory fields as well, although that is kind of anti noSQL
_.isString(name) && attributes.push(name);
 
// eslint-disable-next-line security/detect-object-injection
field && field.default && (defaults[name] = field.default);
});
 
return !_.isEmpty(model) && _.assignIn(_.pick(model, 'config'), collection, _.defaults(model.helpers, {
 
/**
* Asynchronously inserts one record for the current model into it's corresponding collection.
*
* @param {Object} datum - The record to be inserted.
* @param {?Function} callback - The function invoked to mark the end of record insertion.
* @returns {Promise|*} A promise that can be used to resolve further tasks.
*/
insertOne: function (datum, callback) {
return collection.insertOne(sanitiseInsert(datum), callback);
},
 
/**
* Inserts multiple records for the current model into it's corresponding collection.
*
* @param {Object[]} data - The Array of records to be inserted.
* @param {Function} callback - The function invoked to mark the end of the record creation process.
* @returns {Promise} - A Promise to handle task chaning.
*/
insertMany: function (data, callback) {
return collection
.insertMany(_.map(data, sanitiseInsert), callback);
},
 
/**
* Updates the first record that matches the provided query.
*
* @param {Object} filter - The set of attributes to filter by.
* @param {Object} datum - The set of changes to be made to the matched record in the current collection.
* @param {?Object} datum.$set - Sets to be made to the matched record in the current collection.
* @param {?Object} datum.$unset - Unsets to be made to the matched record in the current collection.
* @param {?Object} datum.$rename - Renames to be made to the matched record in the current collection.
* @param {?Object} options - The options for the current updateOne operation.
* @param {?Function} callback - The function invoked to mark the completion of the update process.
* @returns {Promise|*} - The promise instance for further task chaining.
*/
Similar blocks of code found in 2 locations. Consider refactoring.
updateOne: function (filter, datum, options, callback) {
_.isFunction(options) && (callback = options) && (options = {});
_.isFunction(datum) && (callback = datum) && (options = {}) && (datum = filter) && (filter = {});
 
return collection.updateOne(filter, sanitiseUpdate(datum), options, callback);
},
 
/**
* Updates all records that match the provided query.
*
* @param {Object} filter - The set of attributes to filter by.
* @param {Object} data - The set of changes to be made to the matched records in the current collection.
* @param {?Object} data.$set - Sets to be made to the matched records in the current collection.
* @param {?Object} data.$unset - Unsets to be made to the matched records in the current collection.
* @param {?Object} data.$rename - Renames to be made to the matched records in the current collection.
* @param {?Object} options - The options for the current updateMany operation.
* @param {?Function} callback - The function invoked to mark the completion of the update process.
* @returns {Promise|*} - The promise instance for further task chaining.
*/
Similar blocks of code found in 2 locations. Consider refactoring.
updateMany: function (filter, data, options, callback) {
_.isFunction(options) && (callback = options) && (options = {});
_.isFunction(data) && (callback = data) && (options = {}) && (data = filter) && (filter = {});
 
return collection.updateMany(filter, sanitiseUpdate(data), options, callback);
}
}));
};
 
exports.error = _.transform(['missingId'], function (result, type) {
result[type] = error(type); // eslint-disable-line security/detect-object-injection
}, {});