synapse-react-client
Version:
[](https://travis-ci.com/Sage-Bionetworks/Synapse-React-Client) [](https://badge.fury.io/js/synaps
180 lines (178 loc) • 8.06 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.resultToJson = exports.parseEntityIdFromSqlStatement = exports.formatSQLFromParser = exports.insertConditionsFromSearchParams = exports.getWhereInsertIndex = exports.isGroupByInSql = void 0;
var tslib_1 = require("tslib");
var sql_parser_1 = require("sql-parser");
var RegularExpressions_1 = require("../functions/RegularExpressions");
// look for "group by", multi-line and case insensitive
var GROUP_BY_REGEX = /group by/im;
var isGroupByInSql = function (sql) {
return GROUP_BY_REGEX.test(sql);
};
exports.isGroupByInSql = isGroupByInSql;
var WITHOUT_SYN_PREFIX = 3;
var generateTokenUsingOperator = function (literal, operator, match) {
var usedMatchForLike = match;
if (match.match(RegularExpressions_1.SYNAPSE_ENTITY_ID_REGEX)) {
// If we use a LIKE statement with a synId the backend will look for a string with the first three
// characters being 'syn', however, it stores synIds without 'syn', so the query will fail
// The backend usually parses 'syn' out, but not with the LIKE clause since its expecting a regex, so we
// parse this out. This will cause a bug if something matches the synId regex but is in free text.
usedMatchForLike = match.substring(WITHOUT_SYN_PREFIX);
}
// form the has clause, e.g sql = ".... HAS ('condition1', 'condition2',...,'conditionN')
var matchForHas = match
.split(',')
// NOTE - Using single quotes to surround the search term is necessary for the backend parser.
.map(function (el) { return "'" + el + "'"; })
.join(',');
switch (operator) {
case 'LIKE':
return [
['LITERAL', literal, '1'],
['OPERATOR', operator, '1'],
['STRING', "%" + usedMatchForLike + "%", '1'],
];
case 'HAS':
return [
['LITERAL', literal, '1'],
['OPERATOR', operator, '1'],
/*
Using PARAMETER as hack, the parser will use the exact value for a PARAMETER value,
it won't add quotes around the argument or remove parens (which is the standard behavior
for type STRING) that would cause an error on the backend
*/
['PARAMETER', "(" + matchForHas + ")", '1'],
];
default:
// default use operator as-is
return [
['LITERAL', literal, '1'],
['OPERATOR', operator, '1'],
['STRING', match, '1'],
];
}
};
var getWhereInsertIndex = function (tokens) {
var existingWhereIndex = tokens.findIndex(function (el) { return el[0] === 'WHERE'; });
if (existingWhereIndex !== -1) {
return existingWhereIndex;
}
var targetIndex = tokens.findIndex(function (el) { return el[0] === 'GROUP'; });
if (targetIndex !== -1) {
return targetIndex;
}
targetIndex = tokens.findIndex(function (el) { return el[0] === 'HAVING'; });
if (targetIndex !== -1) {
return targetIndex;
}
targetIndex = tokens.findIndex(function (el) { return el[0] === 'ORDER'; });
if (targetIndex !== -1) {
return targetIndex;
}
//else insert it at the end
targetIndex = tokens.findIndex(function (el) { return el[0] === 'EOF'; });
return targetIndex;
};
exports.getWhereInsertIndex = getWhereInsertIndex;
// This will construct a sql query by adding the conditions in searchParams
// to the WHERE clause, preserving all other clauses.
// If the searchParams are not defined, this will simply return the given sql.
var insertConditionsFromSearchParams = function (sql, searchParams, operator) {
if (operator === void 0) { operator = 'LIKE'; }
// if there are no search params, or if all search params are QueryWrapper queries
if (!searchParams) {
return sql;
}
var isQueryWrapperKey = function (key) { return key.startsWith('QueryWrapper'); };
var searchParamKeys = Object.keys(searchParams);
if (searchParamKeys.length === 0 ||
searchParamKeys.every(isQueryWrapperKey)) {
return sql;
}
var tokens = sql_parser_1.lexer.tokenize(sql);
// we want to either create a where clause or insert into the where clause
var foundIndex = tokens.findIndex(function (el) { return el[0] === 'WHERE'; });
var whereClauseIndex = (0, exports.getWhereInsertIndex)(tokens);
var indexAfterWhereClause = whereClauseIndex + 1;
if (foundIndex === -1) {
// insert a where clause
tokens.splice(whereClauseIndex, 0, ['WHERE', 'WHERE', '1']);
}
else {
// if this is inserting into a where clause then we have to make sure that the logic is chained
tokens.splice(indexAfterWhereClause, 0, ['CONDITIONAL', 'AND', '1']);
}
var searchParamsLen = Object.keys(searchParams).length;
Object.keys(searchParams).forEach(function (key, index) {
var token = generateTokenUsingOperator(key, operator, searchParams[key]);
if (index < searchParamsLen - 1) {
// make sure to chain the ANDs until the last one
token.unshift(['CONDITIONAL', 'AND', '1']);
}
tokens.splice.apply(tokens, (0, tslib_1.__spreadArray)([indexAfterWhereClause, 0], token, false));
});
return (0, exports.formatSQLFromParser)(tokens);
};
exports.insertConditionsFromSearchParams = insertConditionsFromSearchParams;
var formatSQLFromParser = function (tokens) {
// replace all DBLSTRINGs (escaped strings) with LITERALs
tokens.forEach(function (value) {
if (value[0] === 'DBLSTRING') {
value[0] = 'LITERAL';
}
});
// if synId has a DOT (e.g. 'syn123.2') then we have to alter the sql produced
var dotIndex = tokens.findIndex(function (val) { return val[0] === 'DOT'; });
if (dotIndex !== -1) {
// Given sql with a versioned entity, e.g. "select * from syn123.2"
// Tokens has the form:
/*
[
["SELECT" , "select"],
..
["FROM", "from"],
["LITERAL", "syn123"],
["DOT", "."],
["LITERAL", "2"],
which we need to transform to
["SELECT" , "select"],
..
["FROM", "from"],
["LITERAL", "syn123.2"],
*/
var synId_1 = tokens[dotIndex - 1][1];
var version = tokens[dotIndex + 1][1];
var synIdWithVersion = synId_1 + "." + version;
tokens.splice(dotIndex, 2);
tokens[dotIndex - 1] = ['LITERAL', synIdWithVersion];
}
var newSql = sql_parser_1.parser.parse(tokens).toString();
// construct the sql using their formatter and then alter it to remove erroneous
// backticks from the table identifier: e.g. (their output) `syn1234` -> (our output) syn1234
var synId = tokens[tokens.findIndex(function (el) { return el[0] === 'FROM'; }) + 1][1];
var synIdWithBackticks = "`" + synId + "`";
return newSql.replace(synIdWithBackticks, synId);
};
exports.formatSQLFromParser = formatSQLFromParser;
//parses synapse entity id from a sql query string
//look for a pattern of 'from[some number of spaces]syn[somenumbers]` case insensitive
var parseEntityIdFromSqlStatement = function (sql) {
var matches = sql.match(/(from)\s+(syn)\d+/gi);
return matches && matches[0] ? matches[0].substr(5).trim() : '';
};
exports.parseEntityIdFromSqlStatement = parseEntityIdFromSqlStatement;
var resultToJson = function (headerColumns, rowColumns) {
var result = [];
var rows = rowColumns.map(function (row) { return row.values; });
var headers = headerColumns.map(function (column) { return column.name; });
rows.forEach(function (row, index) {
result[index] = {};
row.forEach(function (text, cellIndex) {
result[index][headers[cellIndex]] = text;
});
});
return result;
};
exports.resultToJson = resultToJson;
//# sourceMappingURL=sqlFunctions.js.map