mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
Build out all query operators
This commit is contained in:
@ -23,8 +23,8 @@ package com.kingsrook.qqq.backend.core.modules.backend.implementations.utils;
|
|||||||
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
@ -35,6 +35,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
|||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.collections.MutableList;
|
||||||
import org.apache.commons.lang.NotImplementedException;
|
import org.apache.commons.lang.NotImplementedException;
|
||||||
|
|
||||||
|
|
||||||
@ -133,16 +134,17 @@ public class BackendQueryFilterUtils
|
|||||||
case BETWEEN ->
|
case BETWEEN ->
|
||||||
{
|
{
|
||||||
QFilterCriteria criteria0 = new QFilterCriteria().withValues(criterion.getValues());
|
QFilterCriteria criteria0 = new QFilterCriteria().withValues(criterion.getValues());
|
||||||
QFilterCriteria criteria1 = new QFilterCriteria().withValues(new ArrayList<>(criterion.getValues()));
|
QFilterCriteria criteria1 = new QFilterCriteria().withValues(new MutableList<>(criterion.getValues()));
|
||||||
criteria1.getValues().remove(0);
|
criteria1.getValues().remove(0);
|
||||||
yield (testGreaterThan(criteria0, value) || testEquals(criteria0, value)) && (!testGreaterThan(criteria1, value) || testEquals(criteria1, value));
|
yield (testGreaterThan(criteria0, value) || testEquals(criteria0, value)) && (!testGreaterThan(criteria1, value) || testEquals(criteria1, value));
|
||||||
}
|
}
|
||||||
case NOT_BETWEEN ->
|
case NOT_BETWEEN ->
|
||||||
{
|
{
|
||||||
QFilterCriteria criteria0 = new QFilterCriteria().withValues(criterion.getValues());
|
QFilterCriteria criteria0 = new QFilterCriteria().withValues(criterion.getValues());
|
||||||
QFilterCriteria criteria1 = new QFilterCriteria().withValues(criterion.getValues());
|
QFilterCriteria criteria1 = new QFilterCriteria().withValues(new MutableList<>(criterion.getValues()));
|
||||||
criteria1.getValues().remove(0);
|
criteria1.getValues().remove(0);
|
||||||
yield !(testGreaterThan(criteria0, value) || testEquals(criteria0, value)) && (!testGreaterThan(criteria1, value) || testEquals(criteria1, value));
|
boolean between = (testGreaterThan(criteria0, value) || testEquals(criteria0, value)) && (!testGreaterThan(criteria1, value) || testEquals(criteria1, value));
|
||||||
|
yield !between;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return criterionMatches;
|
return criterionMatches;
|
||||||
@ -275,6 +277,26 @@ public class BackendQueryFilterUtils
|
|||||||
return (valueDate.isAfter(criterionDate));
|
return (valueDate.isAfter(criterionDate));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(a instanceof Number numberA && b instanceof String stringB)
|
||||||
|
{
|
||||||
|
BigDecimal bdA = ValueUtils.getValueAsBigDecimal(numberA);
|
||||||
|
BigDecimal bdB = ValueUtils.getValueAsBigDecimal(stringB);
|
||||||
|
return (bdA.doubleValue() < bdB.doubleValue());
|
||||||
|
}
|
||||||
|
else if(a instanceof String stringA && b instanceof Number numberB)
|
||||||
|
{
|
||||||
|
BigDecimal bdA = ValueUtils.getValueAsBigDecimal(stringA);
|
||||||
|
BigDecimal bdB = ValueUtils.getValueAsBigDecimal(numberB);
|
||||||
|
return (bdA.doubleValue() < bdB.doubleValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
// ignore...
|
||||||
|
}
|
||||||
|
|
||||||
throw (new NotImplementedException("Greater/Less Than comparisons are not (yet?) implemented for the supplied types [" + b.getClass().getSimpleName() + "][" + a.getClass().getSimpleName() + "]"));
|
throw (new NotImplementedException("Greater/Less Than comparisons are not (yet?) implemented for the supplied types [" + b.getClass().getSimpleName() + "][" + a.getClass().getSimpleName() + "]"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.modules.backend.implementations.utils;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for BackendQueryFilterUtils
|
||||||
|
*******************************************************************************/
|
||||||
|
class BackendQueryFilterUtilsTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testDoesCriterionMatch()
|
||||||
|
{
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
// < and > w/ mix of numbers and strings... //
|
||||||
|
//////////////////////////////////////////////
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.GREATER_THAN, 1), "f", "2"));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.GREATER_THAN, "1"), "f", 2));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.GREATER_THAN, 1), "f", "1"));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.GREATER_THAN, "1"), "f", 1));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.GREATER_THAN, 1), "f", "0"));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.GREATER_THAN, "1"), "f", 0));
|
||||||
|
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.GREATER_THAN_OR_EQUALS, 1), "f", "2"));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.GREATER_THAN_OR_EQUALS, "1"), "f", 2));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.GREATER_THAN_OR_EQUALS, 1), "f", "1"));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.GREATER_THAN_OR_EQUALS, "1"), "f", 1));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.GREATER_THAN_OR_EQUALS, 1), "f", "0"));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.GREATER_THAN_OR_EQUALS, "1"), "f", 0));
|
||||||
|
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.LESS_THAN, 2), "f", "1"));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.LESS_THAN, "2"), "f", 1));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.LESS_THAN, 1), "f", "1"));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.LESS_THAN, "1"), "f", 1));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.LESS_THAN, 0), "f", "1"));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.LESS_THAN, "0"), "f", 1));
|
||||||
|
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.LESS_THAN_OR_EQUALS, 2), "f", "1"));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.LESS_THAN_OR_EQUALS, "2"), "f", 1));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.LESS_THAN_OR_EQUALS, 1), "f", "1"));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.LESS_THAN_OR_EQUALS, "1"), "f", 1));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.LESS_THAN_OR_EQUALS, 0), "f", "1"));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.LESS_THAN_OR_EQUALS, "0"), "f", 1));
|
||||||
|
|
||||||
|
/////////////////////////////
|
||||||
|
// between and not-between //
|
||||||
|
/////////////////////////////
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.BETWEEN, List.of(1, 3)), "f", 0));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.BETWEEN, List.of(1, 3)), "f", 1));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.BETWEEN, List.of(1, 3)), "f", 2));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.BETWEEN, List.of(1, 3)), "f", 3));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.BETWEEN, List.of(1, 3)), "f", 4));
|
||||||
|
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.NOT_BETWEEN, List.of(1, 3)), "f", 0));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.NOT_BETWEEN, List.of(1, 3)), "f", 1));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.NOT_BETWEEN, List.of(1, 3)), "f", 2));
|
||||||
|
assertFalse(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.NOT_BETWEEN, List.of(1, 3)), "f", 3));
|
||||||
|
assertTrue(BackendQueryFilterUtils.doesCriteriaMatch(new QFilterCriteria("f", QCriteriaOperator.NOT_BETWEEN, List.of(1, 3)), "f", 4));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -24,6 +24,7 @@ package com.kingsrook.qqq.api.javalin;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -70,6 +71,7 @@ import com.kingsrook.qqq.backend.core.utils.ExceptionUtils;
|
|||||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.collections.ListBuilder;
|
||||||
import com.kingsrook.qqq.backend.javalin.QJavalinAccessLogger;
|
import com.kingsrook.qqq.backend.javalin.QJavalinAccessLogger;
|
||||||
import com.kingsrook.qqq.backend.javalin.QJavalinImplementation;
|
import com.kingsrook.qqq.backend.javalin.QJavalinImplementation;
|
||||||
import io.javalin.apibuilder.ApiBuilder;
|
import io.javalin.apibuilder.ApiBuilder;
|
||||||
@ -119,7 +121,7 @@ public class QJavalinApiHandler
|
|||||||
ApiBuilder.post("/", QJavalinApiHandler::doInsert);
|
ApiBuilder.post("/", QJavalinApiHandler::doInsert);
|
||||||
|
|
||||||
ApiBuilder.get("/query", QJavalinApiHandler::doQuery);
|
ApiBuilder.get("/query", QJavalinApiHandler::doQuery);
|
||||||
ApiBuilder.post("/query", QJavalinApiHandler::doQuery);
|
// ApiBuilder.post("/query", QJavalinApiHandler::doQuery);
|
||||||
|
|
||||||
ApiBuilder.get("/{primaryKey}", QJavalinApiHandler::doGet);
|
ApiBuilder.get("/{primaryKey}", QJavalinApiHandler::doGet);
|
||||||
ApiBuilder.patch("/{primaryKey}", QJavalinApiHandler::doUpdate);
|
ApiBuilder.patch("/{primaryKey}", QJavalinApiHandler::doUpdate);
|
||||||
@ -318,7 +320,7 @@ public class QJavalinApiHandler
|
|||||||
}
|
}
|
||||||
else if("or".equalsIgnoreCase(context.queryParam("booleanOperator")))
|
else if("or".equalsIgnoreCase(context.queryParam("booleanOperator")))
|
||||||
{
|
{
|
||||||
filter.setBooleanOperator(QQueryFilter.BooleanOperator.AND);
|
filter.setBooleanOperator(QQueryFilter.BooleanOperator.OR);
|
||||||
}
|
}
|
||||||
else if(StringUtils.hasContent(context.queryParam("booleanOperator")))
|
else if(StringUtils.hasContent(context.queryParam("booleanOperator")))
|
||||||
{
|
{
|
||||||
@ -401,9 +403,14 @@ public class QJavalinApiHandler
|
|||||||
{
|
{
|
||||||
if(StringUtils.hasContent(value))
|
if(StringUtils.hasContent(value))
|
||||||
{
|
{
|
||||||
QCriteriaOperator operator = getCriteriaOperator(value);
|
try
|
||||||
List<Serializable> criteriaValues = getCriteriaValues(field, value);
|
{
|
||||||
filter.addCriteria(new QFilterCriteria(name, operator, criteriaValues));
|
filter.addCriteria(parseQueryParamToCriteria(name, value));
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
badRequestMessages.add(e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -501,10 +508,42 @@ public class QJavalinApiHandler
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private static QCriteriaOperator getCriteriaOperator(String value)
|
private enum Operator
|
||||||
{
|
{
|
||||||
// todo - all other operators
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
return (QCriteriaOperator.EQUALS);
|
// order of these is important (e.g., because some are a sub-string of others!!) //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////
|
||||||
|
EQ("=", QCriteriaOperator.EQUALS, QCriteriaOperator.NOT_EQUALS, true, 1),
|
||||||
|
LTE("<=", QCriteriaOperator.LESS_THAN_OR_EQUALS, QCriteriaOperator.GREATER_THAN, false, 1),
|
||||||
|
GTE(">=", QCriteriaOperator.GREATER_THAN_OR_EQUALS, QCriteriaOperator.LESS_THAN, false, 1),
|
||||||
|
LT("<", QCriteriaOperator.LESS_THAN, QCriteriaOperator.GREATER_THAN_OR_EQUALS, false, 1),
|
||||||
|
GT(">", QCriteriaOperator.GREATER_THAN, QCriteriaOperator.LESS_THAN_OR_EQUALS, false, 1),
|
||||||
|
EMPTY("EMPTY", QCriteriaOperator.IS_BLANK, QCriteriaOperator.IS_NOT_BLANK, true, 0),
|
||||||
|
BETWEEN("BETWEEN ", QCriteriaOperator.BETWEEN, QCriteriaOperator.NOT_BETWEEN, true, 2),
|
||||||
|
IN("IN ", QCriteriaOperator.IN, QCriteriaOperator.NOT_IN, true, 2),
|
||||||
|
// todo MATCHES
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
private final String prefix;
|
||||||
|
private final QCriteriaOperator positiveOperator;
|
||||||
|
private final QCriteriaOperator negativeOperator;
|
||||||
|
private final boolean supportsNot;
|
||||||
|
private final int noOfValues; // 0 & 1 mean 0 & 1 ... 2 means 1 or more...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
Operator(String prefix, QCriteriaOperator positiveOperator, QCriteriaOperator negativeOperator, boolean supportsNot, int noOfValues)
|
||||||
|
{
|
||||||
|
this.prefix = prefix;
|
||||||
|
this.positiveOperator = positiveOperator;
|
||||||
|
this.negativeOperator = negativeOperator;
|
||||||
|
this.supportsNot = supportsNot;
|
||||||
|
this.noOfValues = noOfValues;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -512,10 +551,73 @@ public class QJavalinApiHandler
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private static List<Serializable> getCriteriaValues(QFieldMetaData field, String value)
|
private static QFilterCriteria parseQueryParamToCriteria(String name, String value) throws QBadRequestException
|
||||||
{
|
{
|
||||||
// todo - parse the thing, do stuff
|
///////////////////////////////////
|
||||||
return (List.of(value));
|
// process & discard a leading ! //
|
||||||
|
///////////////////////////////////
|
||||||
|
boolean isNot = false;
|
||||||
|
if(value.startsWith("!") && value.length() > 1)
|
||||||
|
{
|
||||||
|
isNot = true;
|
||||||
|
value = value.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////
|
||||||
|
// look for an operator //
|
||||||
|
//////////////////////////
|
||||||
|
Operator selectedOperator = null;
|
||||||
|
for(Operator op : Operator.values())
|
||||||
|
{
|
||||||
|
if(value.startsWith(op.prefix))
|
||||||
|
{
|
||||||
|
selectedOperator = op;
|
||||||
|
if(!selectedOperator.supportsNot && isNot)
|
||||||
|
{
|
||||||
|
throw (new QBadRequestException("Unsupported operator: !" + selectedOperator.prefix));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// if an operator was found, strip it away from the value for figuring out the values part //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if(selectedOperator != null)
|
||||||
|
{
|
||||||
|
value = value.substring(selectedOperator.prefix.length());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// else - assume the default operator, and use the full value //
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
selectedOperator = Operator.EQ;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////
|
||||||
|
// figure out the criteria values //
|
||||||
|
// todo - quotes? //
|
||||||
|
////////////////////////////////////
|
||||||
|
List<Serializable> criteriaValues;
|
||||||
|
if(selectedOperator.noOfValues == 1)
|
||||||
|
{
|
||||||
|
criteriaValues = ListBuilder.of(value);
|
||||||
|
}
|
||||||
|
else if(selectedOperator.noOfValues == 0)
|
||||||
|
{
|
||||||
|
if(StringUtils.hasContent(value))
|
||||||
|
{
|
||||||
|
throw (new QBadRequestException("Unexpected value after operator " + selectedOperator.prefix + " for field " + name));
|
||||||
|
}
|
||||||
|
criteriaValues = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
criteriaValues = Arrays.asList(value.split(","));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (new QFilterCriteria(name, isNot ? selectedOperator.negativeOperator : selectedOperator.positiveOperator, criteriaValues));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
package com.kingsrook.qqq.api.javalin;
|
package com.kingsrook.qqq.api.javalin;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import com.kingsrook.qqq.api.BaseTest;
|
import com.kingsrook.qqq.api.BaseTest;
|
||||||
import com.kingsrook.qqq.api.TestUtils;
|
import com.kingsrook.qqq.api.TestUtils;
|
||||||
@ -123,14 +124,14 @@ class QJavalinApiHandlerTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testGet200() throws QException
|
void testGet200() throws QException
|
||||||
{
|
{
|
||||||
insertTestRecord();
|
insertTestRecord(1, "Homer", "Simpson");
|
||||||
|
|
||||||
HttpResponse<String> response = Unirest.get(BASE_URL + "/api/" + VERSION + "/person/1").asString();
|
HttpResponse<String> response = Unirest.get(BASE_URL + "/api/" + VERSION + "/person/1").asString();
|
||||||
assertEquals(200, response.getStatus());
|
assertEquals(200, response.getStatus());
|
||||||
JSONObject jsonObject = new JSONObject(response.getBody());
|
JSONObject jsonObject = new JSONObject(response.getBody());
|
||||||
assertEquals(1, jsonObject.getInt("id"));
|
assertEquals(1, jsonObject.getInt("id"));
|
||||||
assertEquals("Darin", jsonObject.getString("firstName"));
|
assertEquals("Homer", jsonObject.getString("firstName"));
|
||||||
assertEquals("Kelkhoff", jsonObject.getString("lastName"));
|
assertEquals("Simpson", jsonObject.getString("lastName"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -138,11 +139,11 @@ class QJavalinApiHandlerTest extends BaseTest
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private static void insertTestRecord() throws QException
|
private static void insertTestRecord(Integer id, String firstName, String lastName) throws QException
|
||||||
{
|
{
|
||||||
InsertInput insertInput = new InsertInput();
|
InsertInput insertInput = new InsertInput();
|
||||||
insertInput.setTableName(TestUtils.TABLE_NAME_PERSON);
|
insertInput.setTableName(TestUtils.TABLE_NAME_PERSON);
|
||||||
insertInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("firstName", "Darin").withValue("lastName", "Kelkhoff")));
|
insertInput.setRecords(List.of(new QRecord().withValue("id", id).withValue("firstName", firstName).withValue("lastName", lastName)));
|
||||||
new InsertAction().execute(insertInput);
|
new InsertAction().execute(insertInput);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,7 +211,7 @@ class QJavalinApiHandlerTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testQuery200SomethingFound() throws QException
|
void testQuery200SomethingFound() throws QException
|
||||||
{
|
{
|
||||||
insertTestRecord();
|
insertTestRecord(1, "Homer", "Simpson");
|
||||||
|
|
||||||
HttpResponse<String> response = Unirest.get(BASE_URL + "/api/" + VERSION + "/person/query").asString();
|
HttpResponse<String> response = Unirest.get(BASE_URL + "/api/" + VERSION + "/person/query").asString();
|
||||||
assertEquals(200, response.getStatus());
|
assertEquals(200, response.getStatus());
|
||||||
@ -222,8 +223,147 @@ class QJavalinApiHandlerTest extends BaseTest
|
|||||||
JSONArray jsonArray = jsonObject.getJSONArray("records");
|
JSONArray jsonArray = jsonObject.getJSONArray("records");
|
||||||
jsonObject = jsonArray.getJSONObject(0);
|
jsonObject = jsonArray.getJSONObject(0);
|
||||||
assertEquals(1, jsonObject.getInt("id"));
|
assertEquals(1, jsonObject.getInt("id"));
|
||||||
assertEquals("Darin", jsonObject.getString("firstName"));
|
assertEquals("Homer", jsonObject.getString("firstName"));
|
||||||
assertEquals("Kelkhoff", jsonObject.getString("lastName"));
|
assertEquals("Simpson", jsonObject.getString("lastName"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testQuery200OrQuery() throws QException
|
||||||
|
{
|
||||||
|
insertSimpsons();
|
||||||
|
assertPersonQueryFindsFirstNames(List.of("Homer", "Marge"), "booleanOperator=OR&firstName=Homer&firstName=Marge&orderBy=firstName");
|
||||||
|
assertPersonQueryFindsFirstNames(List.of("Homer", "Marge", "Bart", "Lisa", "Maggie"), "booleanOperator=OR&firstName=!Homer&firstName=!Marge&orderBy=id");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testQuery200EqualsAndNot() throws QException
|
||||||
|
{
|
||||||
|
insertSimpsons();
|
||||||
|
assertPersonQueryFindsFirstNames(List.of("Homer"), "firstName=Homer"); // no operator implies =
|
||||||
|
assertPersonQueryFindsFirstNames(List.of("Homer"), "firstName==Homer"); // == is an explicit equals operator.
|
||||||
|
assertPersonQueryFindsFirstNames(List.of(), "firstName===Homer"); /// === is = "=Homer"
|
||||||
|
|
||||||
|
assertPersonQueryFindsFirstNames(List.of("Marge", "Bart", "Lisa", "Maggie"), "firstName=!Homer&orderBy=id"); // ! alone implies not-equals
|
||||||
|
assertPersonQueryFindsFirstNames(List.of("Marge", "Bart", "Lisa", "Maggie"), "firstName=!=Homer&orderBy=id"); // != is explicit not-equals
|
||||||
|
assertPersonQueryFindsFirstNames(List.of("Homer", "Marge", "Bart", "Lisa", "Maggie"), "firstName=!==Homer&orderBy=id"); // !== is not-equals "=Homer"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testQuery200In() throws QException
|
||||||
|
{
|
||||||
|
insertSimpsons();
|
||||||
|
assertPersonQueryFindsFirstNames(List.of("Bart", "Lisa", "Maggie"), "firstName=IN Bart,Lisa,Maggie&orderBy=firstName");
|
||||||
|
assertPersonQueryFindsFirstNames(List.of("Homer", "Marge"), "firstName=!IN Bart,Lisa,Maggie&orderBy=firstName");
|
||||||
|
assertPersonQueryFindsFirstNames(List.of("Maggie"), "firstName=IN Maggie");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testQuery200Between() throws QException
|
||||||
|
{
|
||||||
|
insertSimpsons();
|
||||||
|
assertPersonQueryFindsFirstNames(List.of("Homer", "Marge", "Bart"), "id=BETWEEN 1,3&orderBy=id");
|
||||||
|
assertPersonQueryFindsFirstNames(List.of("Homer", "Maggie"), "id=!BETWEEN 2,4&orderBy=id");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testQuery200Empty() throws QException
|
||||||
|
{
|
||||||
|
insertSimpsons();
|
||||||
|
assertPersonQueryFindsFirstNames(List.of("Homer", "Marge", "Bart", "Lisa", "Maggie"), "noOfShoes=EMPTY&orderBy=id");
|
||||||
|
assertPersonQueryFindsFirstNames(List.of(), "noOfShoes=!EMPTY");
|
||||||
|
assertPersonQueryFindsFirstNames(List.of("Homer", "Marge", "Bart", "Lisa", "Maggie"), "id=!EMPTY&orderBy=id");
|
||||||
|
assertPersonQueryFindsFirstNames(List.of(), "id=EMPTY");
|
||||||
|
|
||||||
|
assertError("Unexpected value after operator EMPTY for field id", BASE_URL + "/api/" + VERSION + "/person/query?id=EMPTY 3");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testQuery200LessThanGreaterThanEquals() throws QException
|
||||||
|
{
|
||||||
|
insertSimpsons();
|
||||||
|
String GT = "%3E";
|
||||||
|
assertPersonQueryFindsFirstNames(List.of("Lisa", "Maggie"), "id=" + GT + "3&orderBy=id");
|
||||||
|
assertPersonQueryFindsFirstNames(List.of("Bart", "Lisa", "Maggie"), "id=" + GT + "=3&orderBy=id");
|
||||||
|
|
||||||
|
String LT = "%3C";
|
||||||
|
assertPersonQueryFindsFirstNames(List.of("Homer", "Marge"), "id=" + LT + "3&orderBy=id");
|
||||||
|
assertPersonQueryFindsFirstNames(List.of("Homer", "Marge", "Bart"), "id=" + LT + "=3&orderBy=id");
|
||||||
|
|
||||||
|
assertError("Unsupported operator: !>", BASE_URL + "/api/" + VERSION + "/person/query?id=!" + GT + "3");
|
||||||
|
assertError("Unsupported operator: !>=", BASE_URL + "/api/" + VERSION + "/person/query?id=!" + GT + "=3");
|
||||||
|
assertError("Unsupported operator: !<", BASE_URL + "/api/" + VERSION + "/person/query?id=!" + LT + "3");
|
||||||
|
assertError("Unsupported operator: !<=", BASE_URL + "/api/" + VERSION + "/person/query?id=!" + LT + "=3");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static void insertSimpsons() throws QException
|
||||||
|
{
|
||||||
|
insertTestRecord(1, "Homer", "Simpson");
|
||||||
|
insertTestRecord(2, "Marge", "Simpson");
|
||||||
|
insertTestRecord(3, "Bart", "Simpson");
|
||||||
|
insertTestRecord(4, "Lisa", "Simpson");
|
||||||
|
insertTestRecord(5, "Maggie", "Simpson");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private void assertPersonQueryFindsFirstNames(List<String> expectedFirstNames, String queryString)
|
||||||
|
{
|
||||||
|
HttpResponse<String> response = Unirest.get(BASE_URL + "/api/" + VERSION + "/person/query?" + queryString).asString();
|
||||||
|
assertEquals(200, response.getStatus());
|
||||||
|
JSONObject jsonObject = new JSONObject(response.getBody());
|
||||||
|
assertEquals(expectedFirstNames.size(), jsonObject.getInt("count"));
|
||||||
|
|
||||||
|
if(expectedFirstNames.isEmpty() && !jsonObject.has("records"))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONArray jsonArray = jsonObject.getJSONArray("records");
|
||||||
|
List<String> actualFirstNames = new ArrayList<>();
|
||||||
|
for(int i = 0; i < jsonArray.length(); i++)
|
||||||
|
{
|
||||||
|
actualFirstNames.add(jsonArray.getJSONObject(i).getString("firstName"));
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEquals(expectedFirstNames, actualFirstNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -234,7 +374,7 @@ class QJavalinApiHandlerTest extends BaseTest
|
|||||||
@Test
|
@Test
|
||||||
void testQuery200ManyParams()
|
void testQuery200ManyParams()
|
||||||
{
|
{
|
||||||
HttpResponse<String> response = Unirest.get(BASE_URL + "/api/" + VERSION + "/person/query?pageSize=49&pageNo=2&includeCount=true&booleanOperator=AND&firstName=Darin&orderBy=firstName desc").asString();
|
HttpResponse<String> response = Unirest.get(BASE_URL + "/api/" + VERSION + "/person/query?pageSize=49&pageNo=2&includeCount=true&booleanOperator=AND&firstName=Homer&orderBy=firstName desc").asString();
|
||||||
assertEquals(200, response.getStatus());
|
assertEquals(200, response.getStatus());
|
||||||
JSONObject jsonObject = new JSONObject(response.getBody());
|
JSONObject jsonObject = new JSONObject(response.getBody());
|
||||||
assertEquals(0, jsonObject.getInt("count"));
|
assertEquals(0, jsonObject.getInt("count"));
|
||||||
|
Reference in New Issue
Block a user