UNPKG

synapse-react-client

Version:

[![Build Status](https://travis-ci.com/Sage-Bionetworks/Synapse-React-Client.svg?branch=main)](https://travis-ci.com/Sage-Bionetworks/Synapse-React-Client) [![npm version](https://badge.fury.io/js/synapse-react-client.svg)](https://badge.fury.io/js/synaps

180 lines (178 loc) 8.06 kB
"use strict"; 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