Skip to content

Commit

Permalink
Merge pull request #5 from jeansossmeier/feature/version-2.2.1
Browse files Browse the repository at this point in the history
Fix SQL query value normalizing for multiple quotes and also fix between operator for dates
  • Loading branch information
jeansossmeier authored Apr 18, 2024
2 parents 6a892e6 + b80622e commit 8434b4e
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,8 @@

import java.text.Normalizer;
import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.function.Function;

import static java.util.regex.Matcher.*;
import java.util.stream.Collectors;

public class SqlQueryValueNormalizer {
private static final String SINGLE_QUOTE = "'";
Expand All @@ -15,7 +12,6 @@ public class SqlQueryValueNormalizer {
private static final String PARENTHESIS_CLOSE = ")";
private static final String COMMA = ",";
private static final String RIGHT_SINGLE_QUOTATION_MARK = "’";
private static final Pattern QUOTED_STRING_PATTERN = Pattern.compile("'([^']*)'");

private Function<String, String> queryValueTransformer;

Expand All @@ -32,46 +28,49 @@ public String handle(String queryString) {
return queryString;
}

return isList(queryString) ? sanitizeList(queryString) : sanitizeExpression(queryString);
}
if (isList(queryString)) {
return sanitizeList(queryString);
}

private String sanitizeExpression(String input) {
final Matcher matcher = QUOTED_STRING_PATTERN.matcher(input);
final StringBuilder result = new StringBuilder();
return sanitize(queryString);
}

while (matcher.find()) {
final String quotedContent = matcher.group(1);
final String sanitizedContent = sanitizeQuoted(quotedContent);
matcher.appendReplacement(result, quoteReplacement(SINGLE_QUOTE + sanitizedContent + SINGLE_QUOTE));
private String sanitize(String input) {
if (isQuoted(input)) {
return sanitizeQuoted(input);
} else {
return handleSingleQuotes(input);
}
}

private String sanitizeList(String sanitized) {
final String edgeless = sanitized.substring(1, sanitized.length() - 1);
final String sanitizedList =
Arrays.stream(edgeless.split(COMMA))
.map(this::sanitize)
.collect(Collectors.joining(","));

matcher.appendTail(result);
return result.toString();
return PARENTHESIS_OPEN + sanitizedList + PARENTHESIS_CLOSE;
}

private String sanitizeQuoted(String content) {
final String handledQuotes = handleSingleQuotes(content)
.replace(SINGLE_QUOTE, ESCAPED_SINGLE_QUOTE);
return queryValueTransformer.apply(handledQuotes);
private String sanitizeQuoted(String sanitized) {
final String unquoted =
handleSingleQuotes(sanitized.substring(1, sanitized.length() - 1));

return SINGLE_QUOTE + queryValueTransformer.apply(unquoted) + SINGLE_QUOTE;
}

private static String handleSingleQuotes(String input) {
return Normalizer.normalize(input, Normalizer.Form.NFC)
.replace(RIGHT_SINGLE_QUOTATION_MARK, SINGLE_QUOTE);
.replace(RIGHT_SINGLE_QUOTATION_MARK, SINGLE_QUOTE)
.replace(SINGLE_QUOTE, ESCAPED_SINGLE_QUOTE);
}

private String sanitizeList(String listString) {
final String withoutParentheses = listString.substring(1, listString.length() - 1);
final String[] values = withoutParentheses.split(COMMA + "\\s*");

final String sanitizedList = String.join(COMMA + " ", Arrays.stream(values)
.map(this::sanitizeExpression)
.toArray(String[]::new));

return PARENTHESIS_OPEN + sanitizedList + PARENTHESIS_CLOSE;
private boolean isQuoted(String sanitized) {
return sanitized.startsWith(SINGLE_QUOTE) && sanitized.endsWith(SINGLE_QUOTE);
}

private boolean isList(String input) {
return input.startsWith(PARENTHESIS_OPEN) && input.endsWith(PARENTHESIS_CLOSE);
private boolean isList(String sanitized) {
return sanitized.startsWith(PARENTHESIS_OPEN) && sanitized.endsWith(PARENTHESIS_CLOSE);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,12 @@ public String visitBinaryExpression(final BinaryExpression binaryExpression, fin
final String[] filterValues = rightOperand.replaceAll("[(){}]", "").split(",");
collectMetadata(leftOperand, filterValues);

final String normalizedRightOperand = normalizeString(rightOperand);
if (customExpressionResolver.contains(leftOperand, binaryExpression.getOperator())) {
final String resolvedOperator = resolveOperator(binaryExpression.getOperator());
return formatCustomBinaryExpression(
data, leftOperand, resolvedOperator, normalizedRightOperand, binaryExpression, filterValues);
data, leftOperand, resolvedOperator, rightOperand, binaryExpression, filterValues);
} else {
return formatBinaryExpression(data, leftOperand, binaryExpression, normalizedRightOperand);
return formatBinaryExpression(data, leftOperand, binaryExpression, rightOperand);
}
}

Expand Down Expand Up @@ -175,13 +174,14 @@ public String visitUnaryExpression(final UnaryExpression unaryExpression, final
@Override
public String visitExpressionField(final ExpressionField field, final String data) {
final StringBuilder expressionBuilder = new StringBuilder(data);
if (fieldMap != null && fieldMap.get(field.infix()) != null) {
expressionBuilder.append(fieldMap.get(field.infix()));
} else if (fieldValueTransformer != null && fieldValueTransformer.transformField(field.infix()) != null) {
expressionBuilder.append(fieldValueTransformer.transformField(field.infix()));
final String infix = field.infix();
if (fieldMap != null && fieldMap.get(infix) != null) {
expressionBuilder.append(fieldMap.get(infix));
} else if (fieldValueTransformer != null && fieldValueTransformer.transformField(infix) != null) {
expressionBuilder.append(fieldValueTransformer.transformField(infix));
fieldStack.push(field); //pushing the field for lookup while visiting value.
} else {
expressionBuilder.append(field.infix());
expressionBuilder.append(infix);
}
return expressionBuilder.toString();
}
Expand All @@ -194,20 +194,27 @@ public String visitExpressionField(final ExpressionField field, final String dat
* @return Data of processed node.
*/
@Override
public String visitExpressionValue(
final ExpressionValue<? extends Comparable> expressionValue, final String data) {

final Operator operator = operatorStack.pop();
ExpressionValue<? extends Comparable> value = expressionValue;
public String visitExpressionValue(final ExpressionValue expressionValue, final String data) {
final Operator operator = operatorStack.pop();ExpressionValue value = expressionValue;
if (!fieldStack.isEmpty() && fieldValueTransformer != null) {
ExpressionField field = fieldStack.pop(); // pop the field associated with this value.
FieldValuePair fieldValuePair = fieldValueTransformer.transformValue(field.infix(),value.value());
FieldValuePair fieldValuePair = fieldValueTransformer.transformValue(field.infix(), value.value());
if (fieldValuePair != null && fieldValuePair.getValue() != null) {
value = new ExpressionValue(fieldValuePair.getValue());
final ExpressionValue newExpressionValue = getNormalizedFieldExpressionValue(fieldValuePair.getValue());
expressionValueVisitor.visitExpressionValue(operator, newExpressionValue, data);
}
}

return expressionValueVisitor.visitExpressionValue(operator, value, data);
final ExpressionValue normalizedExpression = getNormalizedFieldExpressionValue(expressionValue.value());
return expressionValueVisitor.visitExpressionValue(operator, normalizedExpression, data);
}

private ExpressionValue getNormalizedFieldExpressionValue(Object value) {
if (value instanceof String) {
return new ExpressionValue(normalizeString((String) value));
} else {
return new ExpressionValue(value);
}
}

private String prepareCustomExpression(
Expand Down
11 changes: 11 additions & 0 deletions src/test/java/com/intuit/graphql/filter/common/TestConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,17 @@ public class TestConstants {
" }\n" +
"}";

public static final String FIRST_NAME_EQUALS_AND_LAST_NAME_CONTAINS_QUOTED = "{\n" +
" searchEmployees(filter: {\n" +
" and : [{ firstName : {equals : \"Jais'wal\"}},{ lastName : {contains : \"Jack o'%antern'\"}}]\n" +
" })\n" +
" {\n" +
" firstName\n" +
" lastName\n" +
" }\n" +
"}";


public static final String LAST_NAME_IN = "{\n" +
" searchEmployees(filter: {\n" +
" lastName: {in: [\"Jaiswal\",\"Gupta\",\"Kumar\"]}\n" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,15 @@ public void filterExpressionWithVariables () {
Assert.assertEquals(expectedExpression,parent);
}

@Test
public void filterExpressionWithFirstNameAndLastNameQuotedEquals() {
ExecutionResult result = getGraphQL().execute(TestConstants.FIRST_NAME_EQUALS_AND_LAST_NAME_CONTAINS_QUOTED);

String expectedExpression = "WHERE ((empFirstName = 'Jais''wal') AND (lastName LIKE 'Jack o''%antern'''))";

Assert.assertEquals(expectedExpression,getEmployeeDataFetcher().getSqlExpression());
}

@Test
public void filterExpressionWithLastNameIn () {
ExecutionResult result = getGraphQL().execute(TestConstants.LAST_NAME_IN);
Expand Down

0 comments on commit 8434b4e

Please sign in to comment.