const Sequelize = require('sequelize');
const _ = require('lodash');
const fs = require('fs');
const translation = require('./translation');
const { Op } = Sequelize;
const getLines = (filename, lineCount, callback) => {
const stream = fs.createReadStream(filename, {
flags: 'r',
encoding: 'utf-8',
fd: null,
mode: 438, // 0666 in Octal
bufferSize: 64 * 1024,
});
let data = '';
let lines = [];
stream.on('data', (moreData) => {
data += moreData;
lines = data.split('\n');
if (lines.length > lineCount + 1) {
stream.destroy();
lines = lines.slice(0, lineCount); // junk as above
callback(false, lines.join('\n'));
}
});
stream.on('error', () => {
callback('Error');
});
stream.on('end', () => {
callback(false, lines.join('\n'));
});
};
const nullableLines = (filename, callback) => {
const stream = fs;
stream.createWriteStream(filename, {
flags: 'w',
encoding: 'utf-8',
fd: null,
mode: 438, // 0666 in Octal
bufferSize: 64 * 1024,
});
stream.write(0, 'asdasd', (err) => {
if (err) {
callback('Error');
}
callback(false, '');
});
};
const getRequestLanguage = cookies => (cookies && cookies.language) || 'en-us';
const getTranslation = params => (translation[getRequestLanguage(params.cookies || {})]);
const getTranslationIdByCode = (languages, code) => {
const lang = (languages || []).find(language => language.code === code);
if (!lang) {
console.log('Not able to find lang'); // eslint-disable-line
return 1;
}
return lang.id;
};
/**
* parseQueryParam
* adds basic params to query
* @param query
* @param search
* @param field
* @param likes
* @param equals
* @returns {*}
*/
const parseQueryParam = (query, search, field, likes, equals) => {
if (likes.indexOf(field) !== -1) {
Object.assign(query, { [field]: { [Op.iLike]: `%${search[field]}%` } });
} else if (equals.indexOf(field) !== -1) {
Object.assign(query, { [field]: search[field] });
}
return query;
};
/**
* addOrCondition
* adds OR conditions to the query
* @param query
* @param search
* @param field
* @param likes
* @param equals
*/
const addOrCondition = (query, search, field, likes, equals) => {
let result = query;
if (field.indexOf('_OR_') !== -1) {
const parts = field.split('_OR_');
if (likes.indexOf(parts[0]) !== -1 && likes.indexOf(parts[1]) !== -1) {
return Object.assign(query, {
[Op.or]: {
[parts[0]]: { [Op.iLike]: `%${search[field]}%` }, [parts[1]]: { [Op.iLike]: `%${search[field]}%` },
},
});
} else if (equals.indexOf(parts[0]) !== -1 && equals.indexOf(parts[1]) !== -1) { // eslint-disable-line
return Object.assign(query, {
[Op.or]: {
[parts[0]]: search[field], [parts[1]]: search[field],
},
});
}
result = parseQueryParam(query, search, parts[0], likes, equals);
result = parseQueryParam(query, search, parts[1], likes, equals);
}
return result;
};
const addOrAndOrCondition = (query, search, field, likes, equals) => {
let result = query;
if (field.indexOf('_ORANDOR_') !== -1) {
const parts = field.split('_ORANDOR_');
if (likes.indexOf(parts[0]) !== -1 && likes.indexOf(parts[1]) !== -1) {
const qPart = search[field].split(' ');
return Object.assign(query, {
[Op.or]: {
[parts[0]]: { [Op.iLike]: `%${qPart[0]}%` },
[parts[0]]: { [Op.iLike]: `%${qPart[1]}%` },
[parts[1]]: { [Op.iLike]: `%${qPart[0]}%` },
[parts[1]]: { [Op.iLike]: `%${qPart[1]}%` },
},
});
} else if (equals.indexOf(parts[0]) !== -1 && equals.indexOf(parts[1]) !== -1) { // eslint-disable-line
return Object.assign(query, {
[Op.or]: {
[parts[0]]: search[field], [parts[1]]: search[field],
},
});
}
result = parseQueryParam(query, search, parts[0], likes, equals);
result = parseQueryParam(query, search, parts[1], likes, equals);
}
return result;
};
/**
* addDateCondition
* add dates conditions to the query
* @param query
* @param search
* @param field
* @param likes
* @param equals
* @returns {*}
*/
const addDateCondition = (query, search, field, likes, equals) => {
if (equals.indexOf(field) !== -1 && (field === 'createdAt' || field === 'updatedAt')) {
const value = search[field];
if (value.indexOf('_to_') !== -1) {
const splited = value.split('_to_');
if (splited[0] && splited[1]) {
return Object.assign(query, {
[field]: {
[Op.between]: [new Date(parseFloat(splited[0])), new Date(parseFloat(splited[1]))],
},
});
} else if (splited[0] || splited[1]) { // eslint-disable-line
return Object.assign(query, {
[field]: new Date(parseFloat(splited[0] || splited[1])),
});
}
} else if (search[field]) {
return Object.assign(query, {
[field]: new Date(parseFloat(search[field])),
});
}
}
return query;
};
/**
* withLikes
* convert query to like params
* @param hook
* @param likes {Array} params which match with likes
* @param equals {Array} params which match with equals
* @returns {Object}
*/
const withLikes = (hook, likes = [], equals = []) => {
const $search = _.get(hook, 'params.query.$search');
if (!$search || !Object.keys($search).length) {
return {};
}
return ({
where: Object.keys($search).reduce((acc, field) => {
let query = acc;
query = addOrAndOrCondition(query, $search, field, likes, equals);
query = addOrCondition(query, $search, field, likes, equals);
query = parseQueryParam(query, $search, field, likes, equals);
query = addDateCondition(query, $search, field, likes, equals);
return query;
}, {}),
});
};
/**
* prepareHook
* removes support params from hook
* @param hook
*/
const prepareHook = (hook) => {
if (_.has(hook, 'params.query.list')) {
delete hook.params.query.list; // eslint-disable-line
}
if (_.has(hook, 'params.query.$search')) {
delete hook.params.query.$search; // eslint-disable-line
}
if (_.has(hook, 'params.query.$limit') && parseFloat(hook.params.query.$limit) === -1) {
Object.assign(hook.params, { paginate: false });
delete hook.params.query.$limit; // eslint-disable-line
}
return hook;
};
/**
* targetTable
* clarifies the table if it is transmitted
* @param tableName {String} table name of database
* @param currentElement {String} column name
* @returns String
*/
const targetTable = (tableName, currentElement) => (tableName.length > 0 ? `"${tableName}"."${currentElement}"` : `"${currentElement}"`);
/**
* allowedFields
* clarifies the table if it is transmitted
* @param search {Object} of search
* @param allowed {Array} of allowed fields
* @returns Object
*/
const allowedFields = (search, allowed) => {
const newObject = search || {};
if (newObject && 'group_by' in newObject) {
delete newObject.group_by;
}
const keys = Object.keys(newObject);
keys.forEach((key) => {
if (allowed.indexOf(key) === -1) throw new Error(`Parameter ${key} does not exist`);
});
return newObject;
};
/**
* searchToWhereString
* convert query to part of SQL String
* @param search
* @param allowed {Array} of allowed fields
* @param table table of database
* @param like {Array} params which match with likes
* @param where {Boolean}
* @returns String
*/
const searchToWhereString = (search, allowed, table = '', like = [], where = true) => {
let query = '';
const searching = allowedFields(search, allowed);
if (!searching) {
return query;
}
query += where ? 'WHERE ' : ' AND ';
const searched = Object.keys(searching).reduce((acc, key) => {
if ((key === 'createdAt' || key === 'updatedAt' || key === 'date_end' || key === 'date_start') && searching[key].indexOf('_to_') !== -1) {
acc.push(`${targetTable(table, key)} BETWEEN :${key}First AND :${key}Last `);
} else if (like.length > 0 && like.indexOf(key) !== -1) {
acc.push(`${targetTable(table, key)} ILIKE :${key} `);
} else {
acc.push(`${targetTable(table, key)} = :${key} `);
}
return acc;
}, []);
if (searched.length === 0) return '';
return query + searched.join(' AND ');
};
/**
* objectQueries
* remove unusable in query group_by year, and separate date if they period
* @param search {Object}
* @param allowed {Array} of allowed fields
* @param like {Array} of like fields
* @returns Object
*/
const objectQueries = (search, allowed, like = []) => {
const newObj = allowedFields(search, allowed);
const date = ['createdAt', 'updatedAt', 'date_end', 'date_start'];
return Object.keys(newObj).reduce((acc, key) => {
const value = newObj[key] || '';
if (date.indexOf(key) !== -1 && value.indexOf('_to_') !== -1) {
const splitter = value.split('_to_');
return Object.assign(acc, { [`${key}First`]: splitter[0], [`${key}Last`]: splitter[1] });
}
if (like.length > 0 && like.indexOf(key)) {
return Object.assign(acc, { [key]: `%${value}%` });
}
return Object.assign(acc, { [key]: value });
}, {});
};
/**
* createImageRegexp, create a regexp for image files
* @param formats
* @returns {RegExp}
*/
const createImageRegexp = formats => (new RegExp(`${_.reduce(formats.split('\r\n'), (acc, format) => (acc ? `${acc}|.${format}` : `${acc}.${format}`), '')}$`, 'gi'));
module.exports = {
nullableLines,
getLines,
createImageRegexp,
getRequestLanguage,
getTranslation,
getTranslationIdByCode,
addOrCondition,
parseQueryParam,
addDateCondition,
withLikes,
prepareHook,
searchToWhereString,
objectQueries,
};