From 659d7c3866ab2ea3ecab159d98b71baf21d9da34 Mon Sep 17 00:00:00 2001 From: Trisha Anand Date: Sun, 19 Sep 2021 20:33:47 +0530 Subject: [PATCH] feat: Where condition helper library using H2 in memory database (#7592) --- app/server/appsmith-interfaces/pom.xml | 6 + .../constants/ConditionalOperator.java} | 4 +- .../appsmith/external/models/Condition.java | 61 +++ .../external/services/FilterDataService.java | 431 ++++++++++++++++++ .../services/FilterDataServiceTest.java | 281 ++++++++++++ .../external/utils/WhereConditionUtils.java | 6 +- 6 files changed, 784 insertions(+), 5 deletions(-) rename app/server/{appsmith-plugins/firestorePlugin/src/main/java/com/external/plugins/Op.java => appsmith-interfaces/src/main/java/com/appsmith/external/constants/ConditionalOperator.java} (61%) create mode 100644 app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/Condition.java create mode 100644 app/server/appsmith-interfaces/src/main/java/com/appsmith/external/services/FilterDataService.java create mode 100644 app/server/appsmith-interfaces/src/test/java/com/appsmith/external/services/FilterDataServiceTest.java diff --git a/app/server/appsmith-interfaces/pom.xml b/app/server/appsmith-interfaces/pom.xml index 439d2e11da..944db31b54 100644 --- a/app/server/appsmith-interfaces/pom.xml +++ b/app/server/appsmith-interfaces/pom.xml @@ -126,6 +126,12 @@ 2.4.7 compile + + com.h2database + h2 + 1.4.200 + + diff --git a/app/server/appsmith-plugins/firestorePlugin/src/main/java/com/external/plugins/Op.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/ConditionalOperator.java similarity index 61% rename from app/server/appsmith-plugins/firestorePlugin/src/main/java/com/external/plugins/Op.java rename to app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/ConditionalOperator.java index ac177c01d0..502d8f3b60 100644 --- a/app/server/appsmith-plugins/firestorePlugin/src/main/java/com/external/plugins/Op.java +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/constants/ConditionalOperator.java @@ -1,6 +1,6 @@ -package com.external.plugins; +package com.appsmith.external.constants; -public enum Op { +public enum ConditionalOperator { LT, LTE, EQ, diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/Condition.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/Condition.java new file mode 100644 index 0000000000..774c771d3a --- /dev/null +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/models/Condition.java @@ -0,0 +1,61 @@ +package com.appsmith.external.models; + +import com.appsmith.external.constants.ConditionalOperator; +import com.appsmith.external.constants.DataType; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; +import java.util.stream.Collectors; + +import static com.appsmith.external.helpers.DataTypeStringUtils.stringToKnownDataTypeConverter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class Condition { + + String path; + + ConditionalOperator operator; + + String value; + + @JsonIgnore + DataType valueDataType; + + public Condition(String path, String operator, String value) { + this.path = path; + this.operator = ConditionalOperator.valueOf(operator); + this.value = value; + } + + public static List addValueDataType(List conditionList) { + + return conditionList + .stream() + .map(condition -> { + String value = condition.getValue(); + DataType dataType = stringToKnownDataTypeConverter(value); + condition.setValueDataType(dataType); + return condition; + }) + .collect(Collectors.toList()); + } + + public static Boolean isValid(Condition condition) { + + if (StringUtils.isEmpty(condition.getPath()) || + (condition.getOperator() == null) || + StringUtils.isEmpty(condition.getValue())) { + return false; + } + + return true; + } +} diff --git a/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/services/FilterDataService.java b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/services/FilterDataService.java new file mode 100644 index 0000000000..c99996e107 --- /dev/null +++ b/app/server/appsmith-interfaces/src/main/java/com/appsmith/external/services/FilterDataService.java @@ -0,0 +1,431 @@ +package com.appsmith.external.services; + +import com.appsmith.external.constants.ConditionalOperator; +import com.appsmith.external.constants.DataType; +import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError; +import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException; +import com.appsmith.external.models.Condition; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import lombok.extern.slf4j.Slf4j; +import org.bson.types.ObjectId; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.appsmith.external.helpers.DataTypeStringUtils.stringToKnownDataTypeConverter; +import static com.appsmith.external.models.Condition.addValueDataType; + +@Component +@Slf4j +public class FilterDataService { + + private static FilterDataService instance = null; + private ObjectMapper objectMapper; + private Connection connection; + + private static final String URL = "jdbc:h2:mem:filterDb"; + + private static final Map SQL_DATATYPE_MAP = Map.of( + DataType.INTEGER, "INT", + DataType.LONG, "BIGINT", + DataType.FLOAT, "REAL", + DataType.DOUBLE, "DOUBLE", + DataType.BOOLEAN, "BOOLEAN", + DataType.STRING, "VARCHAR" + ); + + private static final Map SQL_OPERATOR_MAP = Map.of( + ConditionalOperator.LT, "<", + ConditionalOperator.LTE, "<=", + ConditionalOperator.EQ, "=", + ConditionalOperator.NOT_EQ, "<>", + ConditionalOperator.GT, ">", + ConditionalOperator.GTE, ">=", + ConditionalOperator.IN, "IN", + ConditionalOperator.NOT_IN, "NOT IN" + ); + + private FilterDataService() { + + objectMapper = new ObjectMapper(); + + try { + connection = DriverManager.getConnection(URL); + } catch (SQLException e) { + log.error(e.getMessage()); + throw new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, "Failed to connect to the in memory database. Unable to perform filtering"); + } + } + + public static FilterDataService getInstance() { + + if (instance == null) { + instance = new FilterDataService(); + } + + return instance; + } + + public ArrayNode filterData(ArrayNode items, List conditionList) { + + if (items == null || items.size() == 0) { + return items; + } + + if (!validConditionList(conditionList)) { + throw new AppsmithPluginException(AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, "Conditions for filtering were incomplete or incorrect."); + } + + List conditions = addValueDataType(conditionList); + + // Generate the schema of the table using the first object + JsonNode jsonNode = items.get(0); + + Map schema = generateSchema(jsonNode); + + String tableName = generateTable(schema); + + // insert the data + insertData(tableName, items, schema); + + // Filter the data + List> finalResults = executeFilterQuery(tableName, conditions); + + // Now that the data has been filtered. Clean Up. Drop the table + dropTable(tableName); + + ArrayNode finalResultsNode = objectMapper.valueToTree(finalResults); + + return finalResultsNode; + } + + public List> executeFilterQuery(String tableName, List conditions) { + StringBuilder sb = new StringBuilder("SELECT * FROM " + tableName); + + String whereClause = generateWhereClause(conditions); + sb.append(whereClause); + + sb.append(";"); + + String selectQuery = sb.toString(); + log.debug("{} : Executing Query on H2 : {}", Thread.currentThread().getName(), selectQuery); + + List> rowsList = new ArrayList<>(50); + + Connection conn = checkAndGetConnection(); + + try { + Statement statement = conn.createStatement(); + ResultSet resultSet = statement.executeQuery(selectQuery); + + ResultSetMetaData metaData = resultSet.getMetaData(); + int colCount = metaData.getColumnCount(); + + while (resultSet.next()) { + Map row = new LinkedHashMap<>(colCount); + for (int i = 1; i <= colCount; i++) { + row.put(metaData.getColumnName(i), resultSet.getObject(i)); + } + rowsList.add(row); + } + + } catch (SQLException e) { + // Getting a SQL Exception here means that our generated query is incorrect. Raise an alarm! + log.error(e.getMessage()); + throw new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, "Filtering failure seen : " + e.getMessage()); + } + + return rowsList; + } + + private String generateWhereClause(List conditions) { + + StringBuilder sb = new StringBuilder(); + + Boolean firstCondition = true; + for (Condition condition : conditions) { + + if (firstCondition) { + // Append the WHERE keyword before adding the conditions + sb.append(" WHERE "); + firstCondition = false; + } else { + // This is not the first condition. Append an `AND` before adding the next condition + sb.append(" AND "); + } + + String path = condition.getPath(); + ConditionalOperator operator = condition.getOperator(); + String value = condition.getValue(); + + String sqlOp = SQL_OPERATOR_MAP.get(operator); + if (sqlOp == null) { + throw new AppsmithPluginException(AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, + operator.toString() + " is not supported currently for filtering."); + } + + sb.append(path); + sb.append(" "); + sb.append(sqlOp); + sb.append(" "); + + // These are array operations. Convert value into appropriate format and then append + if (operator == ConditionalOperator.IN || operator == ConditionalOperator.NOT_IN) { + + StringBuilder valueBuilder = new StringBuilder("("); + + // The array could be an array of Strings + if (value.contains("\"")) { + try { + List stringValues = objectMapper.readValue(value, List.class); + List updatedStringValues = stringValues.stream().map(stringValue -> "\'" + stringValue + "\'").collect(Collectors.toList()); + String finalValues = String.join(",", updatedStringValues); + valueBuilder.append(finalValues); + } catch (IOException e) { + log.error(e.getMessage()); + } + } else { + // Removes the outer square brackets from the string to leave behind just the values separated by comma + String trimmedValue = value.replaceAll("^\\[|]$", ""); + valueBuilder.append(trimmedValue); + } + + valueBuilder.append(")"); + value = valueBuilder.toString(); + sb.append(value); + + } else { + // Since the value is not an array, surround the same with single quotes and append + sb.append("'"); + sb.append(value); + sb.append("'"); + } + } + + return sb.toString(); + } + + // INSERT INTO tableName (columnName1, columnName2) VALUES (data1, data2) + public void insertData(String tableName, ArrayNode items, Map schema) { + + List columnNames = schema.keySet().stream().collect(Collectors.toList()); + + StringBuilder insertQueryBuilder = new StringBuilder("INSERT INTO "); + insertQueryBuilder.append(tableName); + + StringBuilder columnNamesBuilder = new StringBuilder("("); + columnNamesBuilder.append(String.join(", ", columnNames)); + columnNamesBuilder.append(")"); + + insertQueryBuilder.append(columnNamesBuilder); + insertQueryBuilder.append(" VALUES "); + + StringBuilder valuesMasterBuilder = new StringBuilder(); + + int counter = 0; + for (JsonNode item : items) { + + // If the number of values inserted is greater than 1000, the insert would fail. Once we have reached 1000 + // rows, execute the insert for rows so far and start afresh for the rest of the rows + if (counter == 1000) { + String insertQueryString = finalInsertQueryString(insertQueryBuilder.toString(), valuesMasterBuilder); + executeDbQuery(insertQueryString); + + // Reset the values builder and counter for new insert queries. + valuesMasterBuilder = new StringBuilder(); + counter = 0; + } + + StringBuilder valuesBuilder = new StringBuilder(); + + if (counter != 0) { + // If not the first row, add a separator between rows + valuesBuilder.append(","); + } + + // Start the row + valuesBuilder.append("("); + + Boolean firstEntry = true; + for (String columnName : columnNames) { + + if (!firstEntry) { + // Add a separator before adding a new entry + valuesBuilder.append(","); + } else { + // For future iterations, set flag to false + firstEntry = false; + } + + JsonNode fieldNode = item.get(columnName); + if (fieldNode != null) { + valuesBuilder.append("'"); + valuesBuilder.append(fieldNode.asText()); + valuesBuilder.append("'"); + } + } + + // End the row + valuesBuilder.append(")"); + + valuesMasterBuilder.append(valuesBuilder); + counter++; + } + + if (valuesMasterBuilder.length() > 0) { + String insertQueryString = finalInsertQueryString(insertQueryBuilder.toString(), valuesMasterBuilder); + executeDbQuery(insertQueryString); + } + } + + private void executeDbQuery(String query) { + + Connection conn = checkAndGetConnection(); + + try { + conn.createStatement().execute(query); + } catch (SQLException e) { + log.error(e.getMessage()); + // Getting a SQL Exception here means that our generated query is incorrect. Raise an alarm! + throw new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, "Filtering failure seen during insertion of data : " + e.getMessage()); + } + } + + private String finalInsertQueryString(String partialInsertQuery, StringBuilder valuesBuilder) { + + StringBuilder insertQueryBuilder = new StringBuilder(partialInsertQuery); + + insertQueryBuilder.append(valuesBuilder); + insertQueryBuilder.append(";"); + + String finalInsertQuery = insertQueryBuilder.toString(); + + return finalInsertQuery; + } + + private Connection checkAndGetConnection() { + try { + if (connection == null || connection.isClosed() || !connection.isValid(5)) { + connection = DriverManager.getConnection(URL); + } + } catch (SQLException e) { + throw new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, "Failed to connect to the in memory database. Unable to perform filtering"); + } + + return connection; + } + + public String generateTable(Map schema) { + + // Generate table name + String generateUniqueId = new ObjectId().toString().toUpperCase(); + + // Appending tbl_ before the generated unique id since using the string directly was throwing a SQL error + // which I couldnt solve. Just appending a string to it though works perfectly. + String tableName = new StringBuilder("tbl_").append(generateUniqueId).toString(); + + StringBuilder sb = new StringBuilder("CREATE TABLE "); + + sb.append(tableName); + + sb.append(" ("); + + Boolean columnsAdded = false; + for (Map.Entry entry : schema.entrySet()) { + + if (columnsAdded) { + // If columns have been added before, add a separator + sb.append(","); + } + + String fieldName = entry.getKey(); + DataType dataType = entry.getValue(); + + String sqlDataType = SQL_DATATYPE_MAP.get(dataType); + if (sqlDataType == null) { + // the data type recognized does not have a native support in appsmith right now + // default to String + sqlDataType = SQL_DATATYPE_MAP.get(DataType.STRING); + } + columnsAdded = true; + sb.append(fieldName); + sb.append(" "); + sb.append(sqlDataType); + } + + sb.append(");"); + + String createTableQuery = sb.toString(); + + executeDbQuery(createTableQuery); + + return tableName; + + } + + public void dropTable(String tableName) { + + String dropTableQuery = "DROP TABLE " + tableName + ";"; + + executeDbQuery(dropTableQuery); + } + + + private Map generateSchema(JsonNode jsonNode) { + + Iterator fieldNamesIterator = jsonNode.fieldNames(); + /* + * For an object of the following type : + * { + * "field1" : "stringValue", + * "field2" : "true", + * "field3" : "12" + * } + * + * The schema generated would be a Map as follows : + * { + * field1 : DataType.STRING + * field2 : DataType.BOOLEAN + * field3 : DataType.INTEGER + * } + */ + Map schema = Stream.generate(() -> null) + .takeWhile(x -> fieldNamesIterator.hasNext()) + .map(n -> fieldNamesIterator.next()) + .collect(Collectors.toMap( + Function.identity(), + name -> { + String value = jsonNode.get(name).asText(); + DataType dataType = stringToKnownDataTypeConverter(value); + return dataType; + })); + + return schema; + } + + + public boolean validConditionList(List conditionList) { + + return conditionList + .stream() + .allMatch(condition -> Condition.isValid(condition)); + } + +} + diff --git a/app/server/appsmith-interfaces/src/test/java/com/appsmith/external/services/FilterDataServiceTest.java b/app/server/appsmith-interfaces/src/test/java/com/appsmith/external/services/FilterDataServiceTest.java new file mode 100644 index 0000000000..29e9d6c8df --- /dev/null +++ b/app/server/appsmith-interfaces/src/test/java/com/appsmith/external/services/FilterDataServiceTest.java @@ -0,0 +1,281 @@ +package com.appsmith.external.services; + +import com.appsmith.external.constants.DataType; +import com.appsmith.external.models.Condition; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.Assert.assertEquals; + +public class FilterDataServiceTest { + + private final ObjectMapper objectMapper = new ObjectMapper(); + private final FilterDataService filterDataService = FilterDataService.getInstance(); + + @Test + public void testGenerateTable() { + Map schema = Map.of( + "id", DataType.INTEGER, + "name", DataType.STRING, + "status", DataType.BOOLEAN + ); + + String table = filterDataService.generateTable(schema); + + assertThat(table).isNotNull(); + } + + @Test + public void testFilterSingleCondition() { + String data = "[\n" + + " {\n" + + " \"id\": 2381224,\n" + + " \"email\": \"michael.lawson@reqres.in\",\n" + + " \"userName\": \"Michael Lawson\",\n" + + " \"productName\": \"Chicken Sandwich\",\n" + + " \"orderAmount\": 4.99,\n" + + " \"orderStatus\": \"READY\"\n" + + " },\n" + + " {\n" + + " \"id\": 2736212,\n" + + " \"email\": \"lindsay.ferguson@reqres.in\",\n" + + " \"userName\": \"Lindsay Ferguson\",\n" + + " \"productName\": \"Tuna Salad\",\n" + + " \"orderAmount\": 9.99,\n" + + " \"orderStatus\": \"READY\"\n" + + " },\n" + + " {\n" + + " \"id\": 6788734,\n" + + " \"email\": \"tobias.funke@reqres.in\",\n" + + " \"userName\": \"Tobias Funke\",\n" + + " \"productName\": \"Beef steak\",\n" + + " \"orderAmount\": 19.99,\n" + + " \"orderStatus\": \"READY\"\n" + + " }\n" + + "]"; + + try { + ArrayNode items = (ArrayNode) objectMapper.readTree(data); + + List conditionList = new ArrayList<>(); + + Condition condition = new Condition("orderAmount", "LT", "15"); + conditionList.add(condition); + + ArrayNode filteredData = filterDataService.filterData(items, conditionList); + + assertEquals(filteredData.size(), 2); + + + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Test + public void testFilterMultipleConditions() { + String data = "[\n" + + " {\n" + + " \"id\": 2381224,\n" + + " \"email\": \"michael.lawson@reqres.in\",\n" + + " \"userName\": \"Michael Lawson\",\n" + + " \"productName\": \"Chicken Sandwich\",\n" + + " \"orderAmount\": 4.99,\n" + + " \"orderStatus\": \"READY\"\n" + + " },\n" + + " {\n" + + " \"id\": 2736212,\n" + + " \"email\": \"lindsay.ferguson@reqres.in\",\n" + + " \"userName\": \"Lindsay Ferguson\",\n" + + " \"productName\": \"Tuna Salad\",\n" + + " \"orderAmount\": 9.99,\n" + + " \"orderStatus\": \"NOT READY\"\n" + + " },\n" + + " {\n" + + " \"id\": 6788734,\n" + + " \"email\": \"tobias.funke@reqres.in\",\n" + + " \"userName\": \"Tobias Funke\",\n" + + " \"productName\": \"Beef steak\",\n" + + " \"orderAmount\": 19.99,\n" + + " \"orderStatus\": \"READY\"\n" + + " }\n" + + "]"; + + try { + ArrayNode items = (ArrayNode) objectMapper.readTree(data); + + List conditionList = new ArrayList<>(); + + Condition condition = new Condition("orderAmount", "LT", "15"); + conditionList.add(condition); + + Condition condition1 = new Condition("orderStatus", "EQ", "READY"); + conditionList.add(condition1); + + ArrayNode filteredData = filterDataService.filterData(items, conditionList); + + assertEquals(filteredData.size(), 1); + + + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Test + public void testFilterInConditionForStrings() { + String data = "[\n" + + " {\n" + + " \"id\": 2381224,\n" + + " \"email\": \"michael.lawson@reqres.in\",\n" + + " \"userName\": \"Michael Lawson\",\n" + + " \"productName\": \"Chicken Sandwich\",\n" + + " \"orderAmount\": 4.99,\n" + + " \"orderStatus\": \"READY\"\n" + + " },\n" + + " {\n" + + " \"id\": 2736212,\n" + + " \"email\": \"lindsay.ferguson@reqres.in\",\n" + + " \"userName\": \"Lindsay Ferguson\",\n" + + " \"productName\": \"Tuna Salad\",\n" + + " \"orderAmount\": 9.99,\n" + + " \"orderStatus\": \"NOT READY\"\n" + + " },\n" + + " {\n" + + " \"id\": 6788734,\n" + + " \"email\": \"tobias.funke@reqres.in\",\n" + + " \"userName\": \"Tobias Funke\",\n" + + " \"productName\": \"Beef steak\",\n" + + " \"orderAmount\": 19.99,\n" + + " \"orderStatus\": \"READY\"\n" + + " }\n" + + "]"; + + try { + ArrayNode items = (ArrayNode) objectMapper.readTree(data); + + List conditionList = new ArrayList<>(); + + Condition condition = new Condition("orderAmount", "LT", "15"); + conditionList.add(condition); + + Condition condition1 = new Condition("orderStatus", "IN", "[\"READY\", \"NOT READY\"]"); + conditionList.add(condition1); + + ArrayNode filteredData = filterDataService.filterData(items, conditionList); + + assertEquals(filteredData.size(), 2); + + + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Test + public void testFilterInConditionForNumbers() { + String data = "[\n" + + " {\n" + + " \"id\": 2381224,\n" + + " \"email\": \"michael.lawson@reqres.in\",\n" + + " \"userName\": \"Michael Lawson\",\n" + + " \"productName\": \"Chicken Sandwich\",\n" + + " \"orderAmount\": 4.99,\n" + + " \"orderStatus\": \"READY\"\n" + + " },\n" + + " {\n" + + " \"id\": 2736212,\n" + + " \"email\": \"lindsay.ferguson@reqres.in\",\n" + + " \"userName\": \"Lindsay Ferguson\",\n" + + " \"productName\": \"Tuna Salad\",\n" + + " \"orderAmount\": 9.99,\n" + + " \"orderStatus\": \"NOT READY\"\n" + + " },\n" + + " {\n" + + " \"id\": 6788734,\n" + + " \"email\": \"tobias.funke@reqres.in\",\n" + + " \"userName\": \"Tobias Funke\",\n" + + " \"productName\": \"Beef steak\",\n" + + " \"orderAmount\": 19.99,\n" + + " \"orderStatus\": \"READY\"\n" + + " }\n" + + "]"; + + try { + ArrayNode items = (ArrayNode) objectMapper.readTree(data); + + List conditionList = new ArrayList<>(); + + Condition condition = new Condition("orderAmount", "LT", "15"); + conditionList.add(condition); + + Condition condition1 = new Condition("orderAmount", "IN", "[4.99, 19.99]"); + conditionList.add(condition1); + + ArrayNode filteredData = filterDataService.filterData(items, conditionList); + + assertEquals(filteredData.size(), 1); + + + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Test + public void testFilterNotInConditionForNumbers() { + String data = "[\n" + + " {\n" + + " \"id\": 2381224,\n" + + " \"email\": \"michael.lawson@reqres.in\",\n" + + " \"userName\": \"Michael Lawson\",\n" + + " \"productName\": \"Chicken Sandwich\",\n" + + " \"orderAmount\": 4.99,\n" + + " \"orderStatus\": \"READY\"\n" + + " },\n" + + " {\n" + + " \"id\": 2736212,\n" + + " \"email\": \"lindsay.ferguson@reqres.in\",\n" + + " \"userName\": \"Lindsay Ferguson\",\n" + + " \"productName\": \"Tuna Salad\",\n" + + " \"orderAmount\": 9.99,\n" + + " \"orderStatus\": \"NOT READY\"\n" + + " },\n" + + " {\n" + + " \"id\": 6788734,\n" + + " \"email\": \"tobias.funke@reqres.in\",\n" + + " \"userName\": \"Tobias Funke\",\n" + + " \"productName\": \"Beef steak\",\n" + + " \"orderAmount\": 19.99,\n" + + " \"orderStatus\": \"READY\"\n" + + " }\n" + + "]"; + + try { + ArrayNode items = (ArrayNode) objectMapper.readTree(data); + + List conditionList = new ArrayList<>(); + + Condition condition = new Condition("orderAmount", "LT", "15"); + conditionList.add(condition); + + Condition condition1 = new Condition("orderAmount", "NOT_IN", "[5.99, 19.00]"); + conditionList.add(condition1); + + ArrayNode filteredData = filterDataService.filterData(items, conditionList); + + assertEquals(filteredData.size(), 2); + + + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/app/server/appsmith-plugins/firestorePlugin/src/main/java/com/external/utils/WhereConditionUtils.java b/app/server/appsmith-plugins/firestorePlugin/src/main/java/com/external/utils/WhereConditionUtils.java index 579e4aa430..e2dc18c4c3 100644 --- a/app/server/appsmith-plugins/firestorePlugin/src/main/java/com/external/utils/WhereConditionUtils.java +++ b/app/server/appsmith-plugins/firestorePlugin/src/main/java/com/external/utils/WhereConditionUtils.java @@ -2,7 +2,7 @@ package com.external.utils; import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError; import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException; -import com.external.plugins.Op; +import com.appsmith.external.constants.ConditionalOperator; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.cloud.firestore.FieldPath; import com.google.cloud.firestore.Query; @@ -26,9 +26,9 @@ public class WhereConditionUtils { ); } - Op operator; + ConditionalOperator operator; try { - operator = StringUtils.isEmpty(operatorString) ? null : Op.valueOf(operatorString); + operator = StringUtils.isEmpty(operatorString) ? null : ConditionalOperator.valueOf(operatorString); } catch (IllegalArgumentException e) { throw new AppsmithPluginException( AppsmithPluginError.PLUGIN_ERROR,