const _ = require('lodash'); function _debugModelInfo(model) { if (!model.name) return; console.log("\n\n----------------------------------\n", model.name, "\n----------------------------------"); console.log("\nAttributes"); if (model.rawAttributes) { for (let attr of Object.keys(model.rawAttributes)) { console.log(model.name + '.' + attr); } } console.log("\nAssociations"); if (model.associations) { for (let assoc of Object.keys(model.associations)) { console.log('as: ', model.associations[assoc].as, 'type: ', model.associations[assoc].associationType); for (let accessor of Object.keys(model.associations[assoc].accessors)) { console.log(accessor); console.log(model.name + '.' + model.associations[assoc].accessors[accessor] + '()'); } } } if (model.Instance && model.Instance.super_) { console.log("\nCommon"); for (let func of Object.keys(model.Instance.super_.prototype)) { if (func === 'constructor' || func === 'sequelize') continue; console.log(model.name + '.' + func + '()'); } } console.log("\n\n----------------------------------\n", "END", "\n----------------------------------"); return; } function getAssociationArrayFromModel(model) { const result = []; if (!model || typeof model.associations !== 'object') { throw new Error("Model should have the 'associations' property."); } Object.keys(model.associations).forEach(key => result.push(key)); return result; } function modelAssociationsToArray(model) { const result = []; if (!model || typeof model.associations !== 'object') { throw new Error("Model should be an object with the 'associations' property."); } Object.keys(model.associations).forEach((key) => { result[key] = model.associations[key]; //result.push(key); }); return result; } const parseParamsToFindOptions = (params) => { const result = {}; // Include All result.include = (params.includeAll) ? [{ all: true }] : []; // Query if (params.query) { result.query = params.query } // Where if (params.params) { result.where = params.params } // Paginate if (params.paginate) { result.offset = params.paginate.limit * (params.paginate.page - 1); result.limit = result.offset + params.paginate.limit; } // Order if (params.sort) { result.order = []; Object.keys(params.sort).forEach(key => { let dir = params.sort[key] ? 'ASC' : 'DESC'; result.order.push([key, dir]) }); } // Attributes if (params.fields) { if (params.fields.validFields) { result.attributes = params.fields.validFields } if (params.fields.invalidFields && Array.isArray(params.fields.invalidFields)) { result.attributes = { ...result.attributes, exclude: params.fields.invalidFields } } } return result; } const defaultOptions = {}; const generateService = (model, extraMethods = {}, options = defaultOptions) => { const defaultService = { fetchAll: async (params, context) => { const findOptions = parseParamsToFindOptions(params); console.log(model); return await model.findAndCountAll(findOptions); }, fetchOne: async (params, context) => { const findOptions = parseParamsToFindOptions(params); return await model.findOne({ where: findOptions.where, include: findOptions.include }); }, count: async (params, context) => { const findOptions = parseParamsToFindOptions(params); return await model.count(findOptions); }, create: async (values, context) => { return await model.create(values); }, update: async (params, values, context) => { const findOptions = parseParamsToFindOptions(params); return await model.update(values, findOptions) }, delete: async (params, context) => { const findOptions = parseParamsToFindOptions(params); const numAffectedRows = await model.destroy(findOptions); return (numAffectedRows > 0); }, search: async (params, context) => { // Convert `params` object to filters compatible with Bookshelf. const filters = strapi.utils.models.convertParams('post', params); // Select field to populate. const populate = Post.associations .filter(ast => ast.autoPopulate !== false) .map(ast => ast.alias); const associations = Post.associations.map(x => x.alias); const searchText = Object.keys(Post._attributes) .filter(attribute => attribute !== Post.primaryKey && !associations.includes(attribute)) .filter(attribute => ['string', 'text'].includes(Post._attributes[attribute].type)); const searchNoText = Object.keys(Post._attributes) .filter(attribute => attribute !== Post.primaryKey && !associations.includes(attribute)) .filter(attribute => !['string', 'text', 'boolean', 'integer', 'decimal', 'float'].includes(Post._attributes[attribute].type)); const searchInt = Object.keys(Post._attributes) .filter(attribute => attribute !== Post.primaryKey && !associations.includes(attribute)) .filter(attribute => ['integer', 'decimal', 'float'].includes(Post._attributes[attribute].type)); const searchBool = Object.keys(Post._attributes) .filter(attribute => attribute !== Post.primaryKey && !associations.includes(attribute)) .filter(attribute => ['boolean'].includes(Post._attributes[attribute].type)); const query = (params._q || '').replace(/[^a-zA-Z0-9.-\s]+/g, ''); return Post.query(qb => { // Search in columns which are not text value. searchNoText.forEach(attribute => { qb.orWhereRaw(`LOWER(${attribute}) LIKE '%${_.toLower(query)}%'`); }); if (!_.isNaN(_.toNumber(query))) { searchInt.forEach(attribute => { qb.orWhereRaw(`${attribute} = ${_.toNumber(query)}`); }); } if (query === 'true' || query === 'false') { searchBool.forEach(attribute => { qb.orWhereRaw(`${attribute} = ${_.toNumber(query === 'true')}`); }); } // Search in columns with text using index. switch (Post.client) { case 'mysql': qb.orWhereRaw(`MATCH(${searchText.join(',')}) AGAINST(? IN BOOLEAN MODE)`, `*${query}*`); break; case 'pg': { const searchQuery = searchText.map(attribute => _.toLower(attribute) === attribute ? `to_tsvector(${attribute})` : `to_tsvector('${attribute}')` ); qb.orWhereRaw(`${searchQuery.join(' || ')} @@ to_tsquery(?)`, query); break; } } if (filters.sort) { qb.orderBy(filters.sort.key, filters.sort.order); } if (filters.skip) { qb.offset(_.toNumber(filters.skip)); } if (filters.limit) { qb.limit(_.toNumber(filters.limit)); } }).fetchAll({ withRelated: populate }); } } return { ...defaultService, //...associationControllers ...extraMethods } } module.exports = { _debugModelInfo, generateService, parseParamsToFindOptions, defaultOptions }