ForestAdmin/lumber

View on GitHub
services/analyzer/sequelize-default-value.js

Summary

Maintainability
A
2 hrs
Test Coverage
A
96%
const Sequelize = require('sequelize');
 
/**
* Convert SQL expression to Javascript value when possible.
* Otherwise, default to using Sequelize.literal(...) which will always work.
*/
class DefaultValueExpression {
constructor(dialect, type, expression) {
this.dialect = dialect;
this.type = type;
this.expression = expression;
}
 
Function `parse` has a Cognitive Complexity of 8 (exceeds 5 allowed). Consider refactoring.
parse() {
if (this.expression === null || this.expression === undefined) {
return null;
}
 
try {
let result;
if (this.dialect === 'postgres') {
result = this.parsePostgres();
} else if (this.dialect === 'mysql') {
result = this.parseMysql();
} else if (this.dialect === 'mssql') {
result = this.parseMsSql();
}
 
if (result === undefined) {
result = this.parseGeneric();
}
 
return result !== undefined ? result : Sequelize.literal(this.expression);
} catch (e) {
return Sequelize.literal(this.expression);
}
}
 
Function `parseGeneric` has a Cognitive Complexity of 9 (exceeds 5 allowed). Consider refactoring.
parseGeneric() {
const nulls = ['NULL'];
const falses = ['false', 'FALSE', 'b\'0\'', '((0))'];
const trues = ['true', 'TRUE', 'b\'1\'', '((1))'];
const isDate = ['TIMESTAMP', 'DATETIME', 'DATE', 'TIME'];
 
let result;
if (nulls.includes(this.expression) || this.expression.startsWith('NULL::')) {
result = null;
} else if (falses.includes(this.expression)) {
result = false;
} else if (trues.includes(this.expression)) {
result = true;
} else if (/^-?\d+(\.\d+)?$/.test(this.expression)) {
result = Number.parseFloat(this.expression);
if (result.toString() !== this.expression) {
result = Sequelize.literal(this.expression);
}
} else if (/^'.*'$/.test(this.expression)) {
result = this.expression.substr(1, this.expression.length - 2).replace(/''/g, "'");
} else if (isDate) {
result = this.literalUnlessMatch(/^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})|(\d{4}-\d{2}-\d{2})|(\d{2}:\d{2}:\d{2})$/);
}
 
return result;
}
 
parseMysql() {
// We have no way of differenciating expressions from constants
// => Just make some guesses and default to Sequelize.literal to pass the tests.
const isString = this.type.startsWith('VARCHAR') || this.type === 'TEXT' || this.type === 'CHAR';
 
let result;
if (this.type.startsWith('ENUM')) {
result = this.expression;
} else if (isString) {
result = this.literalUnlessMatch(/^[^()]+$/);
} else if (this.type === 'JSON') {
const match = this.expression.match(/^[_a-z0-9]+\\'(.+)\\'$/);
if (match) {
result = JSON.parse(match[1]);
}
}
 
return result;
}
 
parsePostgres() {
let result;
 
if (/^'.*'::jsonb?$/i.test(this.expression)) {
// Special case for json/jsonb
const [, content] = this.expression.match(/^'(.*)'::jsonb?$/i);
result = JSON.parse(content.replace(/''/g, "'"));
} else if (/^'.*'::[a-z_ ]+$/i.test(this.expression)) {
// Catches types containing only alpha and spaces (int, varchar, timestamp with timezone, ...)
// This excludes arrays or other compound types (int[], ...).
const [, content] = this.expression.match(/^'(.*)'::[a-z_ ]+$/i);
result = content.replace(/''/g, "'");
}
 
return result;
}
 
Function `parseMsSql` has a Cognitive Complexity of 6 (exceeds 5 allowed). Consider refactoring.
parseMsSql() {
// Remove wrapping parentheses
while (/^\(.*\)$/.test(this.expression)) {
this.expression = this.expression.substr(1, this.expression.length - 2);
}
 
if (this.type === 'BIT') {
if (this.expression === '1') return true;
if (this.expression === '0') return false;
}
 
return undefined;
}
 
literalUnlessMatch(regexp) {
return regexp.test(this.expression) ? this.expression : Sequelize.literal(this.expression);
}
}
 
module.exports = DefaultValueExpression;