Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@
import com.amazonaws.appflow.custom.connector.model.metadata.FieldDefinition;
import com.amazonaws.appflow.custom.connector.queryfilter.antlr.CustomConnectorQueryFilterParser;
import com.amazonaws.appflow.custom.connector.queryfilter.antlr.CustomConnectorQueryFilterParserBaseVisitor;
import org.antlr.v4.runtime.RuleContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;

import java.time.Instant;
import java.time.LocalDate;
Expand All @@ -50,6 +51,7 @@ public class SalesForceQueryFilterExpressionVisitor extends CustomConnectorQuery

// helps build the WHERE-clause for Salesforce SOQL
private final StringBuilder queryBuilder = new StringBuilder();
private final StringBuilder orderByBuilder = new StringBuilder();
private final StringBuilder limitBuilder = new StringBuilder();

// Caller will provide the entity definition for the queried entity. This holds field level metadata for the entity.
Expand Down Expand Up @@ -297,11 +299,27 @@ public StringBuilder visitCountValueExpression(final CustomConnectorQueryFilterP
return limitBuilder.append(ctx.getText()).append(SPACE);
}

@Override
public StringBuilder visitOrderByExpression(final CustomConnectorQueryFilterParser.OrderByExpressionContext ctx) {
if (ctx.identifier().size() > 0) {
List<CustomConnectorQueryFilterParser.IdentifierContext> orderByIdentifiers = ctx.identifier();
String identifiers = orderByIdentifiers.stream().map(RuleContext::getText).collect(Collectors.joining(","));
orderByBuilder.append(identifiers).append(SPACE);
orderByBuilder.append(ctx.right.getText()).append(SPACE);
}

// Only visit the left expression instead of visiting all children
if (ctx.left != null) {
visit(ctx.left);
}
return orderByBuilder;
}

/**
* Returns the final query expression built for Salesforce. Separated into (where, limit) clauses.
*/
public Pair<String, String> getResult() {
return Pair.of(queryBuilder.toString().trim(), limitBuilder.toString().trim());
public Triple<String, String, String> getResult() {
return Triple.of(queryBuilder.toString().trim(), orderByBuilder.toString().trim(), limitBuilder.toString().trim());
}

@FunctionalInterface
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
import org.antlr.v4.runtime.tree.ParseTree;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.apache.logging.log4j.util.Strings;

import java.util.ArrayList;
Expand All @@ -51,6 +51,7 @@ private SalesforceQueryBuilder() {
private static final String FROM_CLAUSE = "from";
private static final String SELECT_CLAUSE = "select";
private static final String LIMIT_CLAUSE = "limit";
private static final String ORDER_BY_CLAUSE = "order by";

public static String buildQuery(final QueryObject queryObject) {

Expand All @@ -66,12 +67,17 @@ public static String buildQuery(final QueryObject queryObject) {
// QueryData allows data filtering based on filter expression.
if (Strings.isNotBlank(queryObject.filterExpression())) {
// adding filter expression in the query
Pair<String, String> whereAndLimitClauses = translateFilterExpression(queryObject.filterExpression(), queryObject.entityDefinition());
String whereClause = whereAndLimitClauses.getLeft();
String limitClause = whereAndLimitClauses.getRight();
Triple<String, String, String>
queryClauses = translateFilterExpression(queryObject.filterExpression(), queryObject.entityDefinition());
String whereClause = queryClauses.getLeft();
String orderByClause = queryClauses.getMiddle();
String limitClause = queryClauses.getRight();
if (StringUtils.isNotBlank(whereClause)) {
clauses.add(String.format(CLAUSE_STRING_FORMAT, WHERE_CLAUSE, whereClause));
}
if (StringUtils.isNotBlank(orderByClause)) {
clauses.add(String.format(CLAUSE_STRING_FORMAT, ORDER_BY_CLAUSE, orderByClause));
}
if (StringUtils.isNotBlank(limitClause)) {
clauses.add(String.format(CLAUSE_STRING_FORMAT, LIMIT_CLAUSE, limitClause));
}
Expand Down Expand Up @@ -141,7 +147,7 @@ private static String addSingleQuotes(final String string) {
return '\'' + string + '\'';
}

private static Pair<String, String> translateFilterExpression(final String filterExpression, final EntityDefinition entityDefinition) {
private static Triple<String, String, String> translateFilterExpression(final String filterExpression, final EntityDefinition entityDefinition) {
if (StringUtils.isNotBlank(filterExpression)) {
ParseTree parseTree = CustomConnectorParseTreeBuilder.parse(filterExpression);
SalesForceQueryFilterExpressionVisitor salesForceQueryFilterExpressionVisitor = new SalesForceQueryFilterExpressionVisitor(entityDefinition);
Expand All @@ -151,6 +157,6 @@ private static Pair<String, String> translateFilterExpression(final String filte
return salesForceQueryFilterExpressionVisitor.getResult();
}
// no filter expression is defined
return Pair.of(StringUtils.EMPTY, StringUtils.EMPTY);
return Triple.of(StringUtils.EMPTY, StringUtils.EMPTY, StringUtils.EMPTY);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import com.amazonaws.appflow.custom.connector.model.metadata.ImmutableEntityDefinition;
import com.amazonaws.appflow.custom.connector.model.metadata.ImmutableFieldDefinition;
import com.amazonaws.appflow.custom.connector.queryfilter.CustomConnectorParseTreeBuilder;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
Expand Down Expand Up @@ -56,35 +56,41 @@ void setupTestCase() {
void testConversionFromFilterExpressionToSalesforceQuery(
final String filterExpression,
final String expectedWhereExpression,
final String expectedOrderByExpression,
final String expectedLimitExpression) {
salesForceQueryFilterExpressionVisitor.visit(CustomConnectorParseTreeBuilder.parse(filterExpression));
Pair<String, String> result = salesForceQueryFilterExpressionVisitor.getResult();
Triple<String, String, String> result = salesForceQueryFilterExpressionVisitor.getResult();
Assertions.assertEquals(expectedWhereExpression, result.getLeft());
Assertions.assertEquals(expectedOrderByExpression, result.getMiddle());
Assertions.assertEquals(expectedLimitExpression, result.getRight());
}

private static Stream<Arguments> getTestFilterExpression() {
return Stream.of(
Arguments.of("Name = \"TestAccountName\"", "Name = 'TestAccountName'", ""),
Arguments.of("Name = \"TestAccountName\" limit 100", "Name = 'TestAccountName'", "100"),
Arguments.of("Name = \"TestAccountName\"", "Name = 'TestAccountName'", "", ""),
Arguments.of("Name = \"TestAccountName\" limit 100", "Name = 'TestAccountName'", "", "100"),
Arguments.of("Name = \"TestAccountName\" order by Name asc", "Name = 'TestAccountName'", "Name asc", ""),
Arguments.of("Name = \"TestAccountName\" order by Name, OS asc", "Name = 'TestAccountName'", "Name,OS asc", ""),
Arguments.of("Name = \"TestAccountName\" order by Name, OS asc limit 100", "Name = 'TestAccountName'", "Name,OS asc", "100"),
Arguments.of("order by Name, OS asc", "", "Name,OS asc", ""),
Arguments.of("Id != '0016g00001cyrfiAAA' AND AccountNumber = 40",
"Id != '0016g00001cyrfiAAA' AND AccountNumber = 40", ""),
"Id != '0016g00001cyrfiAAA' AND AccountNumber = 40", "", ""),
Arguments.of("CreatedDate > 2021-04-20T10:30:35Z AND AccountNumber = 40",
"CreatedDate > 2021-04-20T10:30:35Z AND AccountNumber = 40", ""),
"CreatedDate > 2021-04-20T10:30:35Z AND AccountNumber = 40", "", ""),
Arguments.of("CreatedDate between 2021-04-20T10:30:35Z and 2021-04-25T10:30:35Z",
"CreatedDate > 2021-04-20T10:30:35.000+0000 and CreatedDate < 2021-04-25T10:30:35.000+0000", ""),
"CreatedDate > 2021-04-20T10:30:35.000+0000 and CreatedDate < 2021-04-25T10:30:35.000+0000", "", ""),
Arguments.of("Id in (5, 7, 9, 10)",
"Id IN ('5','7','9','10')", ""),
"Id IN ('5','7','9','10')", "", ""),
Arguments.of("Name = \"TestAccountName\" and Id in (5, 7, 9, 10) and AccountNumber > 100",
"Name = 'TestAccountName' and Id IN ('5','7','9','10') and AccountNumber > 100", ""),
"Name = 'TestAccountName' and Id IN ('5','7','9','10') and AccountNumber > 100", "", ""),
Arguments.of(
"(AccountNumber > 100 and ((CreatedDate < 2021-04-20T12:30:45Z and CreatedDate > 2021-04-21T15:45:49.234Z) and Name contains \"TestAccountName\"))",
"AccountNumber > 100 and CreatedDate < 2021-04-20T12:30:45Z and CreatedDate > " +
"2021-04-21T15:45:49.234Z and Name LIKE '%TestAccountName%'", ""),
"2021-04-21T15:45:49.234Z and Name LIKE '%TestAccountName%'", "", ""),
Arguments.of(
"(AccountNumber > 100 and ((CreatedDate < 2021-04-20T12:30:45Z and CreatedDate > 2021-04-21T15:45:49.234Z) and Name contains \"TestAccountName\")) limit 100",
"AccountNumber > 100 and CreatedDate < 2021-04-20T12:30:45Z and CreatedDate > " +
"2021-04-21T15:45:49.234Z and Name LIKE '%TestAccountName%'", "100"));
"2021-04-21T15:45:49.234Z and Name LIKE '%TestAccountName%'", "", "100"));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@ RPAREN : ')' ;
NULL : 'null';
IN : 'IN' | 'in';
LIMIT : 'LIMIT' | 'limit';
ORDERBY : 'ORDER BY' | 'order by';
ASC : 'ASC' | 'asc';
DESC : 'DESC' | 'desc';
COMMA : ',';

// represents identifier string in filter expression.
IDENTIFIER : [a-zA-Z][A-Za-z0-9_.-]*;

// represents a positive non-zero integer
POS_INTEGER: [1-9][0-9]+
POS_INTEGER: [1-9]+[0-9]*
;

// represents decimal values like 5.0 or -5.0 etc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,19 @@ options { tokenVocab=CustomConnectorQueryFilterLexer; }
// 'queryfilter' is the root node for the filter expression
queryfilter
: expression EOF
| limitexpression EOF
| orderbyexpr EOF
| limitexpr EOF
;

limitexpression
: op=limit right=count #limitExpression
| left=expression op=limit right=count #limitExpression // SQL 'LIMIT xyz' operator
limitexpr
: op=limit right=count #limitExpression
| left=expression op=limit right=count #limitExpression
| orderbyexpr op=limit right=count #limitExpression // SQL 'LIMIT xyz' operator
;

orderbyexpr
: op=orderby identifier (COMMA identifier)* right=order #orderByExpression
| left=expression op=orderby identifier (COMMA identifier)* right=order #orderByExpression // SQL 'ORDER BY xyz, abc ASC|DESC' operator
;

expression
Expand Down Expand Up @@ -84,6 +91,12 @@ in
limit
:LIMIT ;

orderby
:ORDERBY ;

order
:ASC | DESC;

// Following is to support different String formats in the value expression
string
: SINGLE_STRING
Expand Down
Loading