diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/GenerateReportAction.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/GenerateReportAction.java index cb828123..aad1aaa1 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/GenerateReportAction.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/reporting/GenerateReportAction.java @@ -557,7 +557,7 @@ public class GenerateReportAction extends AbstractQActionFunction fieldOperatorMap = new HashMap<>(); + for(QFilterCriteria criterion : getCriteria()) + { + if(criterion.getValues() != null) + { + int criteriaIndex = 1; + int valueIndex = 0; + for(Serializable value : criterion.getValues()) + { + /////////////////////////////////////////////////////////////////////////////// + // keep track of what the index is for this criterion, this way if there are // + // more than one with the same id/operator values, we can differentiate // + /////////////////////////////////////////////////////////////////////////////// + String backendName = getBackendName(criterion, valueIndex); + if(!fieldOperatorMap.containsKey(backendName)) + { + fieldOperatorMap.put(backendName, criteriaIndex); + } + else + { + criteriaIndex = fieldOperatorMap.get(backendName) + 1; + fieldOperatorMap.put(backendName, criteriaIndex); + } + + if(value instanceof FilterVariableExpression fve) + { + if(criteriaIndex > 1) + { + backendName += criteriaIndex; + } + fve.setVariableName(backendName); + } + + valueIndex++; + } + } + } + } + + + + /******************************************************************************* + ** builds up a backend name for a field variable expression + ** + *******************************************************************************/ + private String getBackendName(QFilterCriteria criterion, int valueIndex) + { + StringBuilder backendName = new StringBuilder(criterion.getFieldName()); + for(String operatorParts : criterion.getOperator().name().split("_")) + { + backendName.append(StringUtils.ucFirst(operatorParts.toLowerCase())); + } + + if(criterion.getOperator().equals(QCriteriaOperator.BETWEEN) || criterion.getOperator().equals(QCriteriaOperator.NOT_BETWEEN)) + { + if(valueIndex == 0) + { + backendName.append("From"); + } + else + { + backendName.append("To"); + } + } + + return (backendName.toString()); + } + + + /******************************************************************************* ** Replace any criteria values that look like ${input.XXX} with the value of XXX ** from the supplied inputValues map. @@ -419,7 +500,7 @@ public class QQueryFilter implements Serializable, Cloneable ** QQueryFilter - e.g., if it's one that defined in metaData, and that we don't ** want to be (permanently) changed!! *******************************************************************************/ - public void interpretValues(Map inputValues) + public void interpretValues(Map inputValues) throws QException { QMetaDataVariableInterpreter variableInterpreter = new QMetaDataVariableInterpreter(); variableInterpreter.addValueMap("input", inputValues); @@ -433,11 +514,18 @@ public class QQueryFilter implements Serializable, Cloneable { if(value instanceof AbstractFilterExpression) { - ///////////////////////////////////////////////////////////////////////// - // todo - do we want to try to interpret values within the expression? // - // e.g., greater than now minus ${input.noOfDays} // - ///////////////////////////////////////////////////////////////////////// - newValues.add(value); + /////////////////////////////////////////////////////////////////////// + // if a filter variable expression, evaluate the input values, which // + // will replace the variables with the corresponding actual values // + /////////////////////////////////////////////////////////////////////// + if(value instanceof FilterVariableExpression filterVariableExpression) + { + newValues.add(filterVariableExpression.evaluateInputValues(inputValues)); + } + else + { + newValues.add(value); + } } else { diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/AbstractFilterExpression.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/AbstractFilterExpression.java index cc58e9fa..fc4a2034 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/AbstractFilterExpression.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/AbstractFilterExpression.java @@ -23,6 +23,8 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions; import java.io.Serializable; +import java.util.Map; +import com.kingsrook.qqq.backend.core.exceptions.QException; /******************************************************************************* @@ -33,7 +35,17 @@ public abstract class AbstractFilterExpression implement /******************************************************************************* ** *******************************************************************************/ - public abstract T evaluate(); + public abstract T evaluate() throws QException; + + + + /******************************************************************************* + ** + *******************************************************************************/ + public T evaluateInputValues(Map inputValues) throws QException + { + return (T) this; + } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/FilterVariableExpression.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/FilterVariableExpression.java new file mode 100644 index 00000000..43ad0e76 --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/FilterVariableExpression.java @@ -0,0 +1,213 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2023. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions; + + +import java.io.Serializable; +import java.util.Map; +import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException; + + +/******************************************************************************* + ** + *******************************************************************************/ +public class FilterVariableExpression extends AbstractFilterExpression +{ + private String variableName; + private String fieldName; + private String operator; + private int valueIndex = 0; + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public Serializable evaluate() throws QException + { + throw (new QUserFacingException("Missing variable value.")); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Override + public Serializable evaluateInputValues(Map inputValues) throws QException + { + if(!inputValues.containsKey(variableName)) + { + throw (new QUserFacingException("Missing variable value.")); + } + return (inputValues.get(variableName)); + } + + + + /******************************************************************************* + ** Constructor + ** + *******************************************************************************/ + public FilterVariableExpression() + { + } + + + + /******************************************************************************* + ** Constructor + ** + *******************************************************************************/ + private FilterVariableExpression(String fieldName, int valueIndex) + { + this.fieldName = fieldName; + this.valueIndex = valueIndex; + } + + + + /******************************************************************************* + ** Getter for valueIndex + *******************************************************************************/ + public int getValueIndex() + { + return (this.valueIndex); + } + + + + /******************************************************************************* + ** Setter for valueIndex + *******************************************************************************/ + public void setValueIndex(int valueIndex) + { + this.valueIndex = valueIndex; + } + + + + /******************************************************************************* + ** Fluent setter for valueIndex + *******************************************************************************/ + public FilterVariableExpression withValueIndex(int valueIndex) + { + this.valueIndex = valueIndex; + return (this); + } + + + + /******************************************************************************* + ** Getter for fieldName + *******************************************************************************/ + public String getFieldName() + { + return (this.fieldName); + } + + + + /******************************************************************************* + ** Setter for fieldName + *******************************************************************************/ + public void setFieldName(String fieldName) + { + this.fieldName = fieldName; + } + + + + /******************************************************************************* + ** Fluent setter for fieldName + *******************************************************************************/ + public FilterVariableExpression withFieldName(String fieldName) + { + this.fieldName = fieldName; + return (this); + } + + + + /******************************************************************************* + ** Getter for variableName + *******************************************************************************/ + public String getVariableName() + { + return (this.variableName); + } + + + + /******************************************************************************* + ** Setter for variableName + *******************************************************************************/ + public void setVariableName(String variableName) + { + this.variableName = variableName; + } + + + + /******************************************************************************* + ** Fluent setter for variableName + *******************************************************************************/ + public FilterVariableExpression withVariableName(String variableName) + { + this.variableName = variableName; + return (this); + } + + + + /******************************************************************************* + ** Getter for operator + *******************************************************************************/ + public String getOperator() + { + return (this.operator); + } + + + + /******************************************************************************* + ** Setter for operator + *******************************************************************************/ + public void setOperator(String operator) + { + this.operator = operator; + } + + + + /******************************************************************************* + ** Fluent setter for operator + *******************************************************************************/ + public FilterVariableExpression withOperator(String operator) + { + this.operator = operator; + return (this); + } + +} diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/Now.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/Now.java index 9ab54ef7..56edba25 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/Now.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/Now.java @@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions; import java.time.Instant; +import com.kingsrook.qqq.backend.core.exceptions.QException; /******************************************************************************* @@ -35,7 +36,7 @@ public class Now extends AbstractFilterExpression ** *******************************************************************************/ @Override - public Instant evaluate() + public Instant evaluate() throws QException { return (Instant.now()); } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/NowWithOffset.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/NowWithOffset.java index bf36d971..51f64c99 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/NowWithOffset.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/NowWithOffset.java @@ -28,6 +28,7 @@ import java.time.ZoneId; import java.time.ZoneOffset; import java.time.temporal.ChronoUnit; import java.util.concurrent.TimeUnit; +import com.kingsrook.qqq.backend.core.exceptions.QException; /******************************************************************************* @@ -119,7 +120,7 @@ public class NowWithOffset extends AbstractFilterExpression ** *******************************************************************************/ @Override - public Instant evaluate() + public Instant evaluate() throws QException { ///////////////////////////////////////////////////////////////////////////// // Instant doesn't let us plus/minus WEEK, MONTH, or YEAR... // diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/ThisOrLastPeriod.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/ThisOrLastPeriod.java index 28e53c37..15fd1bfa 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/ThisOrLastPeriod.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/ThisOrLastPeriod.java @@ -28,6 +28,7 @@ import java.time.LocalDateTime; import java.time.ZoneId; import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; +import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException; import com.kingsrook.qqq.backend.core.utils.ValueUtils; @@ -95,7 +96,7 @@ public class ThisOrLastPeriod extends AbstractFilterExpression ** *******************************************************************************/ @Override - public Instant evaluate() + public Instant evaluate() throws QException { ZoneId zoneId = ValueUtils.getSessionOrInstanceZoneId(); diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/savedreports/SavedReportTableCustomizer.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/savedreports/SavedReportTableCustomizer.java index 73679dcf..c3c6899f 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/savedreports/SavedReportTableCustomizer.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/savedreports/SavedReportTableCustomizer.java @@ -35,6 +35,7 @@ import com.kingsrook.qqq.backend.core.model.actions.reporting.pivottable.PivotTa import com.kingsrook.qqq.backend.core.model.actions.reporting.pivottable.PivotTableGroupBy; import com.kingsrook.qqq.backend.core.model.actions.reporting.pivottable.PivotTableValue; import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput; import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; @@ -110,10 +111,12 @@ public class SavedReportTableCustomizer implements TableCustomizerInterface { try { - //////////////////////////////////////////////////////////////// - // nothing to validate on filter, other than, we can parse it // - //////////////////////////////////////////////////////////////// - SavedReportToReportMetaDataAdapter.getQQueryFilter(queryFilterJson); + ///////////////////////////////////////////////////////////////////////// + // validate that we can parse the filter, then prep it for the backend // + ///////////////////////////////////////////////////////////////////////// + QQueryFilter filter = SavedReportToReportMetaDataAdapter.getQQueryFilter(queryFilterJson); + filter.prepForBackend(); + record.setValue("queryFilterJson", filter); } catch(IOException e) { diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/utils/BackendQueryFilterUtils.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/utils/BackendQueryFilterUtils.java index e8b09615..8f8303b6 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/utils/BackendQueryFilterUtils.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/modules/backend/implementations/utils/BackendQueryFilterUtils.java @@ -32,6 +32,8 @@ import java.util.ListIterator; import java.util.Map; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; +import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.logging.QLogger; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; @@ -50,6 +52,9 @@ import org.apache.commons.lang.NotImplementedException; *******************************************************************************/ public class BackendQueryFilterUtils { + private static final QLogger LOG = QLogger.getLogger(BackendQueryFilterUtils.class); + + /******************************************************************************* ** Test if record matches filter. @@ -138,7 +143,14 @@ public class BackendQueryFilterUtils Serializable criteriaValue = valueListIterator.next(); if(criteriaValue instanceof AbstractFilterExpression expression) { - valueListIterator.set(expression.evaluate()); + try + { + valueListIterator.set(expression.evaluate()); + } + catch(QException qe) + { + LOG.warn("Unexpected exception caught evaluating expression", qe); + } } } diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/tables/QQueryFilterTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/tables/QQueryFilterTest.java new file mode 100644 index 00000000..01b997d2 --- /dev/null +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/tables/QQueryFilterTest.java @@ -0,0 +1,100 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2022. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.kingsrook.qqq.backend.core.actions.tables; + + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import com.kingsrook.qqq.backend.core.BaseTest; +import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions.AbstractFilterExpression; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions.FilterVariableExpression; +import org.junit.jupiter.api.Test; +import static com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator.BETWEEN; +import static com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator.EQUALS; +import static com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator.IS_BLANK; +import static org.junit.jupiter.api.Assertions.assertEquals; + + +/******************************************************************************* + ** Unit test for QQueryFilter + ** + *******************************************************************************/ +class QQueryFilterTest extends BaseTest +{ + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + public void testInterpretValues() throws QException + { + Map inputValues = new HashMap<>(); + inputValues.put("clientIdEquals1", "value"); + + AbstractFilterExpression expression = new FilterVariableExpression() + .withVariableName("clientIdEquals1"); + + QQueryFilter qQueryFilter = new QQueryFilter(new QFilterCriteria("id", EQUALS, expression)); + qQueryFilter.interpretValues(inputValues); + + assertEquals("value", qQueryFilter.getCriteria().get(0).getValues().get(0)); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + public void testPrepForBackend() throws QException + { + FilterVariableExpression fve0 = new FilterVariableExpression(); + FilterVariableExpression fve1 = new FilterVariableExpression(); + FilterVariableExpression fve2 = new FilterVariableExpression(); + FilterVariableExpression fve3 = new FilterVariableExpression(); + FilterVariableExpression fve4 = new FilterVariableExpression(); + FilterVariableExpression fve5 = new FilterVariableExpression(); + FilterVariableExpression fve6 = new FilterVariableExpression(); + + QQueryFilter qQueryFilter = new QQueryFilter( + new QFilterCriteria("id", EQUALS, fve0), + new QFilterCriteria("value", IS_BLANK, fve1), + new QFilterCriteria("id", EQUALS, fve2), + new QFilterCriteria("id", BETWEEN, fve3, fve4), + new QFilterCriteria("id", BETWEEN, fve5, fve6) + ); + qQueryFilter.prepForBackend(); + + assertEquals("idEquals", fve0.getVariableName()); + assertEquals("valueIsBlank", fve1.getVariableName()); + assertEquals("idEquals2", fve2.getVariableName()); + assertEquals("idBetweenFrom", fve3.getVariableName()); + assertEquals("idBetweenTo", fve4.getVariableName()); + assertEquals("idBetweenFrom2", fve5.getVariableName()); + assertEquals("idBetweenTo2", fve6.getVariableName()); + } + +} diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/NowWithOffsetTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/NowWithOffsetTest.java index 8ef5737d..9f043d1f 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/NowWithOffsetTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/actions/tables/query/expressions/NowWithOffsetTest.java @@ -30,7 +30,7 @@ import static org.assertj.core.api.Assertions.assertThat; /******************************************************************************* - ** Unit test for NowWithOffset + ** Unit test for NowWithOffset *******************************************************************************/ class NowWithOffsetTest extends BaseTest { @@ -42,7 +42,7 @@ class NowWithOffsetTest extends BaseTest ** *******************************************************************************/ @Test - void test() + void test() throws Exception { long now = System.currentTimeMillis(); @@ -65,4 +65,4 @@ class NowWithOffsetTest extends BaseTest assertThat(oneYearFromNowMillis).isCloseTo(now + (730 * DAY_IN_MILLIS), Offset.offset(10_000L + 3 * DAY_IN_MILLIS)); } -} \ No newline at end of file +} diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/savedreports/SavedReportTableCustomizerTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/savedreports/SavedReportTableCustomizerTest.java index 6bf5d188..570a3f4d 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/savedreports/SavedReportTableCustomizerTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/model/savedreports/SavedReportTableCustomizerTest.java @@ -37,7 +37,9 @@ import com.kingsrook.qqq.backend.core.model.actions.reporting.pivottable.PivotTa import com.kingsrook.qqq.backend.core.model.actions.reporting.pivottable.PivotTableValue; import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput; import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter; +import com.kingsrook.qqq.backend.core.model.actions.tables.query.expressions.FilterVariableExpression; import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput; import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput; import com.kingsrook.qqq.backend.core.model.data.QRecord; @@ -45,11 +47,12 @@ import com.kingsrook.qqq.backend.core.utils.JsonUtils; import com.kingsrook.qqq.backend.core.utils.TestUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator.EQUALS; import static org.assertj.core.api.Assertions.assertThat; /******************************************************************************* - ** Unit test for SavedReportTableCustomizer + ** Unit test for SavedReportTableCustomizer *******************************************************************************/ class SavedReportTableCustomizerTest extends BaseTest { @@ -98,6 +101,31 @@ class SavedReportTableCustomizerTest extends BaseTest + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testPrepareFilterVariable() + { + QQueryFilter qQueryFilter = new QQueryFilter(new QFilterCriteria("id", EQUALS, new FilterVariableExpression())); + + QRecord record = new SavedReport() + .withTableName(TestUtils.TABLE_NAME_PERSON_MEMORY) + .withQueryFilterJson(JsonUtils.toJson(qQueryFilter)) + .withColumnsJson(JsonUtils.toJson(new ReportColumns() + .withColumn("id") + .withColumn("firstName") + .withColumn("lastName") + .withColumn("birthDate"))) + .toQRecord(); + + new SavedReportTableCustomizer().preValidateRecord(record); + + assertThat(record.getValueString("queryFilterJson").contains("idEquals")); + } + + + /******************************************************************************* ** *******************************************************************************/ @@ -224,5 +252,4 @@ class SavedReportTableCustomizerTest extends BaseTest List.of("Missing function for at least one pivot table value")); } - -} \ No newline at end of file +} diff --git a/qqq-backend-module-mongodb/src/main/java/com/kingsrook/qqq/backend/module/mongodb/actions/AbstractMongoDBAction.java b/qqq-backend-module-mongodb/src/main/java/com/kingsrook/qqq/backend/module/mongodb/actions/AbstractMongoDBAction.java index 3f5f58ab..d1a28479 100644 --- a/qqq-backend-module-mongodb/src/main/java/com/kingsrook/qqq/backend/module/mongodb/actions/AbstractMongoDBAction.java +++ b/qqq-backend-module-mongodb/src/main/java/com/kingsrook/qqq/backend/module/mongodb/actions/AbstractMongoDBAction.java @@ -189,7 +189,7 @@ public class AbstractMongoDBAction Map values = record.getValues(); for(QFieldMetaData field : table.getFields().values()) { - String fieldName = field.getName(); + String fieldName = field.getName(); String fieldBackendName = getFieldBackendName(field); if(fieldBackendName.contains(".")) @@ -554,7 +554,14 @@ public class AbstractMongoDBAction Serializable value = valueListIterator.next(); if(value instanceof AbstractFilterExpression expression) { - valueListIterator.set(expression.evaluate()); + try + { + valueListIterator.set(expression.evaluate()); + } + catch(QException qe) + { + LOG.warn("Unexpected exception caught evaluating expression", qe); + } } /* todo - is this needed?? diff --git a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java index 6c54f5be..afb3b5d6 100644 --- a/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java +++ b/qqq-backend-module-rdbms/src/main/java/com/kingsrook/qqq/backend/module/rdbms/actions/AbstractRDBMSAction.java @@ -43,6 +43,7 @@ import java.util.stream.Collectors; import com.kingsrook.qqq.backend.core.actions.ActionHelper; import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter; import com.kingsrook.qqq.backend.core.context.QContext; +import com.kingsrook.qqq.backend.core.exceptions.QException; import com.kingsrook.qqq.backend.core.exceptions.QValueException; import com.kingsrook.qqq.backend.core.logging.QLogger; import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput; @@ -617,7 +618,14 @@ public abstract class AbstractRDBMSAction Serializable value = valueListIterator.next(); if(value instanceof AbstractFilterExpression expression) { - valueListIterator.set(expression.evaluate()); + try + { + valueListIterator.set(expression.evaluate()); + } + catch(QException qe) + { + LOG.warn("Unexpected exception caught evaluating expression", qe); + } } else { diff --git a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java index fc9bdda5..86a6ed08 100644 --- a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java +++ b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinImplementation.java @@ -32,6 +32,7 @@ import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -1244,7 +1245,9 @@ public class QJavalinImplementation } if(filter != null) { - queryInput.setFilter(JsonUtils.toObject(filter, QQueryFilter.class)); + QQueryFilter qQueryFilter = JsonUtils.toObject(filter, QQueryFilter.class); + queryInput.setFilter(qQueryFilter); + qQueryFilter.interpretValues(Collections.emptyMap()); } Integer skip = QJavalinUtils.integerQueryParam(context, "skip"); @@ -1962,6 +1965,7 @@ public class QJavalinImplementation } + /******************************************************************************* ** *******************************************************************************/