UNPKG

dsmc

Version:

A node utility for Creating SailsJS Models and Controllers which use a Default controller for common functionality.

593 lines (558 loc) 20.5 kB
#!/usr/bin/env node var fs = require('fs'); var outputFormat = "\r\t\t\t"; var name; var fields = false; var models = false; var collections = false; var dsmcPath = '../../common/_dsmcController.js'; var commonFolder = "./common"; var api = "./api"; var modelFolder = api + "/models"; var controllerFolder = api + "/controllers"; var firstField = true; function writeFile(fileName, contents) { fs.writeFile(fileName, contents, function (err) { if (err) return console.log(err); console.log('File:' + fileName + ' written'); }); } function camelCase (str) { return str.charAt(0).toLowerCase() + str.slice(1);; } var dsmc = createDsmc(); console.log(); console.log(); console.log("Creating Sails Model and Controller"); function mkDir(dir) { if(!fs.existsSync(dir)){ fs.mkdirSync(dir, 0766, function(err){ if(err){ console.log(err); response.send("ERROR! Can't make the directory " + dir + "! \n"); // echo the result back } }); } } process.argv.forEach(function (val, index, array) { if (val.indexOf("-n") === 0) { name = array[index+1]; } if (val.indexOf("-f") === 0) { fields = array[index+1]; } if (val.indexOf("-m") === 0) { models = array[index+1]; } if (val.indexOf("-c") === 0) { collections = array[index+1]; } }); console.log(); console.log(); if (!fields) { console.log("Please add -f with fields in a comma separated list before running."); return; } if (!name) { console.log("Please add -n with ModelName before running."); return; } if (models) { console.log("Models are one to one relationships. Creating " + models); } if (collections) { console.log("Collections are one to many relationships. Creating " + collections); console.log(); console.log("==================================="); console.log("****** NOTICE - PLEASE READ *******"); console.log("==================================="); console.log(); console.log("BEFORE running sails lift"); console.log(); console.log("All field types default to string, please open and modify " + name + ".js."); console.log(); console.log("Open and modify the value for the via property for associated collections and models in " + name + "Controller.js."); console.log(); console.log("Queries type currently include"); console.log(); console.log("contains, startsWith, endsWith"); console.log(); } mkDir(commonFolder); mkDir(api); mkDir(modelFolder); mkDir(controllerFolder); writeFile(commonFolder + "/_dsmcController.js", dsmc); var recordKeepingFields ="createdBy,updatedBy"; // CREATE MODEL.js var model = createModel(name, fields, models, collections); var modelName = name+".js"; writeFile(modelFolder + "/" + modelName, model); // CREATE CONTROLLER.js var controller = createController(name, fields); var controllerName = name+"Controller.js"; writeFile(controllerFolder + "/" + controllerName, controller); // USED TO RETURN RELATED TABLE DATA // SQL PEEPS CONSIDER THIS AS A JOIN // SOLVED BY SAILSJS adding populateAll() var populateOutput; function createFieldDefinition(field) { var addDefault = ""; if (field === 'createdBy' || field === 'updatedBy') { addDefault = "defaultsTo: 'admin'," + outputFormat + "\t"; } else { addDefault = ""; } var addUniqueRequired = (firstField) ? "required: true," + outputFormat + "\tunique: true," + outputFormat + "\t" : ""; firstField = false; return " " + outputFormat + field + ": { " + outputFormat + "\t"+ addUniqueRequired + addDefault+ "type: 'string'" + outputFormat + "}"; } function createPropertiesArray(items, fnct) { var output = []; if (items) { var array = items.split(","); for (var i = 0; i < array.length; i++) { output.push(fnct(array[i])); }; } return output; } function createDefinition(type, collection) { var via = " //via: '"; return " " + outputFormat + collection + ": { " + outputFormat + " " + type + ": '" + collection.toLowerCase() + "'," + outputFormat + via + camelCase(collection) + "'" + outputFormat + "},\r"; } function createCollectionsDefinition(collection) { return createDefinition("collection", collection); } function createModelsDefinition(model) { return createDefinition("model", model); } // SOLVED BY SAILSJS adding populateAll() function createPopulateQuery(model) { return "//.populate('" + model + "')" + outputFormat; } function createModel(controller, fields, models, collections) { var collectionsOutput = createPropertiesArray(collections, createCollectionsDefinition); var modelsOutput = createPropertiesArray(models, createModelsDefinition); var fieldsOutput = createPropertiesArray(fields, createFieldDefinition); var recordKeepingOutput = createPropertiesArray(recordKeepingFields, createFieldDefinition); populateOutput = createPropertiesArray(collections, createPopulateQuery); populateOutput += createPropertiesArray(models, createPopulateQuery) var contents = "/**\r"+ "* "+controller+".js\r"+ "*\r"+ "* @description :: Write something nice. Maybe send a card.\r"+ "*/\r"+ "/*\r"+ "field: {\r"+ " type: 'string',\r"+ " required: true,\r"+ " unique: true\r"+ " // supported field types: string, text, integer, float, date, time, datetime, boolean, binary, array, json\r" + "},\r"+ "*/\r"+ "\r"+ "module.exports = {\r\t"+ "\r\t"+ " attributes: {\r\t"+ " " + fieldsOutput.join(',\r') + ",\r\t"+ " " + collectionsOutput.join('\r') + "\r\t"+ " " + modelsOutput.join('\r') + "\r\t"+ " " + recordKeepingOutput.join(',\r') + "\r\t"+ " }\r\t"+ "};\r\t"; return contents; } function createController(controller, fields) { return "/**\r" + " * " + controller + "Controller\r" + " *\r" + " * @description :: Base API for "+ controller + "\r" + " */\r" + "\r" + "// Search Defaults\r" + "var defaults = {\r" + " skip: 0,\r" + " limit: 20,\r" + " dir: 'desc',\r" + " searchType: 'contains',\r" + " primaryField: '" + fields.split(',')[0] + "'\r" + "}\r" + "\r" + "var methods = require('" + dsmcPath + "')(defaults);\r" + "\r" + "// Add custom methods and method overrides here\r" + "/*\r" + "methods.extra = function (req, res) {\r" + " return res.json({ title: 'custom method', message: 'Create custom methods as needed.'});\r" + " };\r" + "methods.model = function (req, res) {\r" + " return res.json({ title: 'method override', message: 'Override an existing method.'});\r" + " };\r" + "*/\r" + "module.exports = methods;\r"; } function createDsmc() { return ["// =====================================", "// DSMC - Default Sails Model Controller", "// =====================================", "", "var setLimit = function(query, defaults) {", " // limit is the default or as specified in the query", " // if the query value for limit is 0 return all", " var limit = {};", " if (query.limit !== '0') {", " limit = parseInt(query.limit) || defaults.limit;", " }", " return limit;", "};", "", "var setSort = function(query, dir) {", " var sort = {};", " if (query.sort) {", " sort[query.sort] = dir;", " } else {", " sort[config.searchField] = dir;", " }", " return sort;", "};", "", "var setSearchType = function (searchString, query, defaults) {", " // Check start of search string for non word characters", " // determine searchType", " var searchCriteriaHelperArr = ['contains', 'like', 'lessThan', 'lessThanOrEqual', 'greaterThan', 'greaterThanOrEqual', 'not', 'endsWith', 'startsWith'];", " var searchTypeArray = ['', '=', '<', '<=', '>', '>=', '!', '*'];", " var matchNonWordChars = [];", " var searchType;", " var startsWith = false;", "", " if (searchString) {", " matchNonWordChars = searchString.match(/(^\\W+)/);", " // If multiple non word characters are found", " // use like with wild cards on both ends", " if ((searchString.match(/\\*/g)||[]).length === 2) {", " searchType = searchCriteriaHelperArr[0];", "", " // If non word characters are found", " // determine searchType", " } else if (matchNonWordChars) {", " searchType = searchCriteriaHelperArr[searchTypeArray.indexOf(matchNonWordChars[0])];", " }", "", " // If no non word characters are found", " // check the end of the search string for non word characters", " // to determine searchType", " if (!matchNonWordChars) {", " matchNonWordChars = searchString.match(/(\\W$)/);", " if (matchNonWordChars && matchNonWordChars[0] === '*') {", " searchType = searchCriteriaHelperArr[searchCriteriaHelperArr.length-1];", " }", " }", " }", "", " // In SailsJs like is equals unless the searchCriteria includes '%' in the searchCriteria", " searchType = (searchType === '=') ? 'like' : searchType || query.searchType || defaults.searchType;", " var nonWordChar = [];", " if (searchTypeArray.length > 0 && matchNonWordChars) {", " if (searchTypeArray.indexOf(matchNonWordChars[0]) > -1) {", " nonWordChar = matchNonWordChars[0];", " }", " }", " // return searchType and non word character matches", " return [searchType, nonWordChar];", "}", "", "var setQuery = function(searchString, searchField, searchTypeStringArr) {", "", " var query = {};", "", " // Use of special characters requires split / join to remove chars from string", " // var regex = new RegExp(find, 'g'); (regex wont work)", " if (searchTypeStringArr[0]) {", " searchString = searchString.split(searchTypeStringArr[1]).join('');", " }", "", " if (searchString !== undefined && searchString !== 'undefined') {", " var searchObj = {}", " searchObj[searchTypeStringArr[0]] = searchString;", " query[searchField] = searchObj;", " }", " return query;", "}", "", "var setConfig = function(req, defaults) {", "", " var config = {};", " // model config", " config.modelName = req.options.controller;", " config.model = sails.models[config.modelName]._attributes;", "", " // search config", " var query = req.query;", "", " // pagination", " config.skip = parseInt(query.skip) || defaults.skip;", " ", " // search field and criteria", " var searchString = query.search || req.params.id;", " config.searchField = query.field || defaults.primaryField;", "", " // change property name for output obj", " if (query.as) {", " config.as = query.as;", " }", " // returns array with search type and any non-word chars that need to be replaced.", " var searchType = setSearchType(searchString, query, defaults);", "", " // Parse query string to determine search type", " config.query = setQuery(decodeURIComponent(searchString), config.searchField, searchType)", " console.log(config.query);", " // limit is the default or as specified in the query", " // if the query value for limit is 0 return all", " config.limit = setLimit(query, defaults);", "", " // sort order", " var dir = query.dir || defaults.dir || 'desc';", " config.sort = setSort(query, dir);", "", " return config;", "}", "", "// perform search on properties of this model", "var performSearch = function(res, config, type) {", " var output = [];", " // model reference", " return sails.models[config.modelName].find()", " .where(config.query)", " .populateAll()", " .skip(config.skip)", " .limit(config.limit)", " .sort(config.sort)", "", " .exec(function findCB(err,found){", " if (found) {", " while (found.length) {", " var tmp = found.pop();", " // perform search on all properties of this model", " // SLOOOOWWWW", " // convert and check results", " if (config.hasOwnProperty('contains') && type !== 'get') {", " if (output.length >= limit) {", " break;", " }", " var objectAsString = JSON.stringify(tmp);", " if (objectAsString.indexOf(config.contains) > -1) {", " output.push(tmp);", " }", " } else", " // return full object for search", " if (type === 'search') {", " output.push(tmp);", " // return selected search field and id for get", " } else", " // return full object for get", " if (type === 'get') {", " output.push(tmp);", " // return selected search field and id for get", " } else {", " var tmpObj = {}", " tmpObj.id = tmp.id;", " if (!config.as) {", " // customize object property to be the searchField", " tmpObj[config.searchField] = tmp[config.searchField];", " } else {", " // customize returned object property according to value from qs as property", " tmpObj[config.as] = tmp[config.searchField]; ", " }", " output.push(tmpObj);", " }", " }", " }", " return res.json(output);", " });", "}", "", "var searchRelatedModel = function(res, config, searchConfig) {", " var foreignWhereQuery = {};", " foreignWhereQuery[searchConfig.via] = { contains: config.criteria };", " // returned from initial related data query", " // then reset and returned from join table", " var foreignIdArray = [];", " // Models references need to be in lowercase", " var modelRef = config.searchField.toLowerCase();", " return sails.models[modelRef].find()", " .where(foreignWhereQuery)", " .then(function(relatedDocs){", " for (var i = relatedDocs.length - 1; i >= 0; i--) {", " foreignIdArray.push(relatedDocs[i].id);", " };", " //no relatedDocs found", " if(relatedDocs === undefined) {", " return ({notFound:true});", " }", " var whereQuery = {};", " whereQuery[config.searchField] = { '$in':foreignIdArray };", "", " // get primary data using primaryIdArray", " return sails.models[config.modelName].find()", " .where(whereQuery)", " .populateAll()", " .then(function(found){", " return res.json(found);", " });", " });", "}", "", "var searchReleatedCollection = function (res, config, searchConfig) {", " var output = [];", "", " // Result from initial query is an array of ids ", " // that belong to the searched properties collection", " // Join table will be queryed using this array", " var foreignIdArray = []; ", "", " // Result from join/second query is an array of ids ", " // that belong to the primary collection", " var primaryIdArray = [];", "", " // join table config / get references to external collection and its join table", " var foreignTableName = searchConfig.collection;", " var joinTableForeignField = (searchConfig.via) ? searchConfig.via + '_' + searchConfig.via : searchConfig.collection + '_' + config.searchField;", " var joinTableModelField = config.modelName + '_' + config.searchField;", "", " // Check for self-join.", " var selfJoin = '';", " if (searchConfig.collection === config.modelName) {", " selfJoin = '_' + config.modelName.toLowerCase();", " }", "", " var joinTableName = joinTableModelField.toLowerCase() + selfJoin + '__' + joinTableForeignField.toLowerCase();", " var whereQuery = {};", " var searchVia = searchConfig.via;", "", " // change name to match SailsJS self-join schema.", " if (!searchVia) {", " searchVia = searchConfig.collection;", " joinTableForeignField += '_' + searchConfig.collection;", " }", "", " whereQuery[searchVia] = { contains: config.criteria };", "", " // get matching related data and ids", " // get foreign data using ", " // config.criteria", " return sails.models[foreignTableName].find()", " .where(whereQuery)", " .then(function(relatedDocs){", " for (var i = relatedDocs.length - 1; i >= 0; i--) {", " foreignIdArray.push(relatedDocs[i].id);", " };", "", " //no relatedDocs found", " if(relatedDocs === undefined) {", " return ({notFound:true});", " }", "", " // get join table data", " // with primary record", " // using foreignArray ids", " var foreignWhereQuery = {};", " foreignWhereQuery[joinTableForeignField] = foreignIdArray;", " return sails.models[joinTableName].find()", " .where(foreignWhereQuery)", " .then(function(joinData){", " for (var i = joinData.length - 1; i >= 0; i--) {", " primaryIdArray.push(joinData[i][joinTableModelField]);", " };", " // get primary data using primaryidarray", " var whereQuery = {id:{'$in':primaryIdArray}};", " return sails.models[config.modelName].find()", " .where(whereQuery)", " .populateAll()", " .then(function(found){", " return res.json(found);", " });", " });", " });", "}", "", "var arrayFromObject = function(obj) {", " var array = [];", " for (var prop in obj) {", " array.push(prop);", " }", " return array;", "}", "", "module.exports = function(defaults) {", " ", "", " var module = {", "", " get: function (req, res) {", " var config = setConfig(req, defaults);", " return performSearch(res, config, 'get'); ", " },", "", " search: function (req, res) {", "", " // set search criteria", " var config = setConfig(req, defaults);", " // reads fields type from model to determine search type below", " var searchConfig = config.model[config.searchField];", "", " // perform search on properties of this model", " if (!searchConfig.hasOwnProperty('via') && !searchConfig.hasOwnProperty('model') && !searchConfig.hasOwnProperty('collection') ) {", " performSearch(res, config, 'search');", " }", "", " // perform search on properties of external model", " // one to one relationship", " if (searchConfig.hasOwnProperty('model')) {", " searchRelatedModel(res, config, searchConfig);", " }", "", " // perform search on properties of external collection", " // one to many / many to many", " if (searchConfig.hasOwnProperty('collection')) {", " searchReleatedCollection(res, config, searchConfig);", " }", " },", "", " model: function(req, res) {", " var config = setConfig(req, defaults);", " // items starting with _ are admin / configuration properties in the output object", " var array = arrayFromObject(config.model);", " // _displayOrder = creation order of properties in model definition", " config.model._displayOrder = array;", " config.model._primaryField = defaults.primaryField;", " return res.json(config.model);", " },", "", " displayOrder: function(req, res) {", " var config = setConfig(req, defaults);", " var array = arrayFromObject(config.model);", " return res.json(array);", " },", "", " // Does not work with external collections and models", " getCount: function(req, res) {", " var config = setConfig(req, defaults);", " sails.models[config.modelName].count(config.query).exec(function countCB(err, found){", " return res.json(found);", " });", " }", " }", " return module;", "};"].join("\n"); }