From ea795ed70104271e5cabb164ced859a898d46f72 Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Thu, 23 Feb 2023 13:19:13 -0600 Subject: [PATCH] Missed things re: custom pvs --- .../widgets/NoCodeWidgetVelocityUtils.java | 11 +++ .../values/QCustomPossibleValueProvider.java | 14 ++-- .../possiblevalues/QPossibleValue.java | 1 + .../SearchPossibleValueSourceActionTest.java | 76 +++++++++++++++++++ .../qqq/backend/core/utils/TestUtils.java | 20 +++-- .../javalin/QJavalinProcessHandler.java | 56 ++++++++++++++ 6 files changed, 166 insertions(+), 12 deletions(-) diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/NoCodeWidgetVelocityUtils.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/NoCodeWidgetVelocityUtils.java index 1b01e702..8ce91f25 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/NoCodeWidgetVelocityUtils.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/dashboard/widgets/NoCodeWidgetVelocityUtils.java @@ -22,6 +22,7 @@ package com.kingsrook.qqq.backend.core.actions.dashboard.widgets; +import java.io.Serializable; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.Instant; @@ -270,6 +271,16 @@ public class NoCodeWidgetVelocityUtils + /******************************************************************************* + ** + *******************************************************************************/ + public String format(String displayFormat, Serializable value) + { + return (QValueFormatter.formatValue(displayFormat, value)); + } + + + /******************************************************************************* ** *******************************************************************************/ diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/values/QCustomPossibleValueProvider.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/values/QCustomPossibleValueProvider.java index 2d7cf7c7..09e786c5 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/values/QCustomPossibleValueProvider.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/actions/values/QCustomPossibleValueProvider.java @@ -36,18 +36,20 @@ import com.kingsrook.qqq.backend.core.utils.ValueUtils; /******************************************************************************* ** Interface to be implemented by user-defined code that serves as the backing ** for a CUSTOM type possibleValueSource + ** + ** Type parameter `T` is the id-type of the possible value. *******************************************************************************/ -public interface QCustomPossibleValueProvider +public interface QCustomPossibleValueProvider { /******************************************************************************* ** *******************************************************************************/ - QPossibleValue getPossibleValue(Serializable idValue); + QPossibleValue getPossibleValue(Serializable idValue); /******************************************************************************* ** *******************************************************************************/ - List> search(SearchPossibleValueSourceInput input) throws QException; + List> search(SearchPossibleValueSourceInput input) throws QException; /******************************************************************************* @@ -55,7 +57,7 @@ public interface QCustomPossibleValueProvider ** the type of the ids in the enum (e.g., strings from a frontend, integers ** in an enum). So, this method looks maps a list of input ids to the requested type. *******************************************************************************/ - default List convertInputIdsToIdType(Class type, List inputIdList) + default List convertInputIdsToIdType(Class type, List inputIdList) { List rs = new ArrayList<>(); if(CollectionUtils.nullSafeIsEmpty(inputIdList)) @@ -75,10 +77,8 @@ public interface QCustomPossibleValueProvider /******************************************************************************* ** *******************************************************************************/ - default boolean doesPossibleValueMatchSearchInput(Class idType, QPossibleValue possibleValue, SearchPossibleValueSourceInput input) + default boolean doesPossibleValueMatchSearchInput(List idsInType, QPossibleValue possibleValue, SearchPossibleValueSourceInput input) { - List idsInType = convertInputIdsToIdType(idType, input.getIdList()); - boolean match = false; if(input.getIdList() != null) { diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/possiblevalues/QPossibleValue.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/possiblevalues/QPossibleValue.java index 9bdae034..a36fb981 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/possiblevalues/QPossibleValue.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/model/metadata/possiblevalues/QPossibleValue.java @@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.possiblevalues; /******************************************************************************* ** An actual possible value - an id and label. ** + ** Type parameter `T` is the type of the id (often Integer, maybe String) *******************************************************************************/ public class QPossibleValue { diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/values/SearchPossibleValueSourceActionTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/values/SearchPossibleValueSourceActionTest.java index 93a38fc5..8a15a6d8 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/values/SearchPossibleValueSourceActionTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/actions/values/SearchPossibleValueSourceActionTest.java @@ -224,6 +224,82 @@ class SearchPossibleValueSourceActionTest extends BaseTest + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testSearchPvsAction_customSearchTermFound() throws QException + { + SearchPossibleValueSourceOutput output = getSearchPossibleValueSourceOutput("Custom[3]", TestUtils.POSSIBLE_VALUE_SOURCE_CUSTOM); + assertEquals(1, output.getResults().size()); + assertThat(output.getResults()).anyMatch(pv -> pv.getId().equals("3") && pv.getLabel().equals("Custom[3]")); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testSearchPvsAction_customSearchTermNotFound() throws QException + { + SearchPossibleValueSourceOutput output = getSearchPossibleValueSourceOutput("Foo", TestUtils.POSSIBLE_VALUE_SOURCE_CUSTOM); + assertEquals(0, output.getResults().size()); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testSearchPvsAction_customSearchTermManyFound() throws QException + { + SearchPossibleValueSourceOutput output = getSearchPossibleValueSourceOutput("Custom", TestUtils.POSSIBLE_VALUE_SOURCE_CUSTOM); + assertEquals(10, output.getResults().size()); + assertThat(output.getResults()).allMatch(pv -> pv.getLabel().startsWith("Custom[")); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testSearchPvsAction_customIdFound() throws QException + { + SearchPossibleValueSourceOutput output = getSearchPossibleValueSourceOutputById("4", TestUtils.POSSIBLE_VALUE_SOURCE_CUSTOM); + assertEquals(1, output.getResults().size()); + assertThat(output.getResults()).anyMatch(pv -> pv.getId().equals("4") && pv.getLabel().equals("Custom[4]")); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testSearchPvsAction_customIdFoundDifferentType() throws QException + { + SearchPossibleValueSourceOutput output = getSearchPossibleValueSourceOutputById(5, TestUtils.POSSIBLE_VALUE_SOURCE_CUSTOM); + assertEquals(1, output.getResults().size()); + assertThat(output.getResults()).anyMatch(pv -> pv.getId().equals("5") && pv.getLabel().equals("Custom[5]")); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testSearchPvsAction_customIdNotFound() throws QException + { + SearchPossibleValueSourceOutput output = getSearchPossibleValueSourceOutputById(-1, TestUtils.POSSIBLE_VALUE_SOURCE_CUSTOM); + assertEquals(0, output.getResults().size()); + } + + + /******************************************************************************* ** *******************************************************************************/ diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/TestUtils.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/TestUtils.java index c3b3b898..c2690a0d 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/TestUtils.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/utils/TestUtils.java @@ -1083,16 +1083,16 @@ public class TestUtils /******************************************************************************* ** *******************************************************************************/ - public static class CustomPossibleValueSource implements QCustomPossibleValueProvider + public static class CustomPossibleValueSource implements QCustomPossibleValueProvider { /******************************************************************************* ** *******************************************************************************/ @Override - public QPossibleValue getPossibleValue(Serializable idValue) + public QPossibleValue getPossibleValue(Serializable idValue) { - return (new QPossibleValue<>(idValue, "Custom[" + idValue + "]")); + return (new QPossibleValue<>(ValueUtils.getValueAsString(idValue), "Custom[" + idValue + "]")); } @@ -1101,9 +1101,19 @@ public class TestUtils ** *******************************************************************************/ @Override - public List> search(SearchPossibleValueSourceInput input) + public List> search(SearchPossibleValueSourceInput input) { - return (new ArrayList<>()); + List> rs = new ArrayList<>(); + for(int i = 0; i < 10; i++) + { + QPossibleValue possibleValue = getPossibleValue(i); + List idsInType = convertInputIdsToIdType(String.class, input.getIdList()); + if(doesPossibleValueMatchSearchInput(idsInType, possibleValue, input)) + { + rs.add(possibleValue); + } + } + return (rs); } } diff --git a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinProcessHandler.java b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinProcessHandler.java index 28361e20..b7af91ea 100644 --- a/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinProcessHandler.java +++ b/qqq-middleware-javalin/src/main/java/com/kingsrook/qqq/backend/javalin/QJavalinProcessHandler.java @@ -50,6 +50,7 @@ import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction; import com.kingsrook.qqq.backend.core.actions.reporting.GenerateReportAction; import com.kingsrook.qqq.backend.core.actions.tables.InsertAction; import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter; +import com.kingsrook.qqq.backend.core.actions.values.SearchPossibleValueSourceAction; import com.kingsrook.qqq.backend.core.exceptions.QNotFoundException; import com.kingsrook.qqq.backend.core.exceptions.QPermissionDeniedException; import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException; @@ -65,6 +66,8 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput; import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator; 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.values.SearchPossibleValueSourceInput; +import com.kingsrook.qqq.backend.core.model.actions.values.SearchPossibleValueSourceOutput; import com.kingsrook.qqq.backend.core.model.data.QRecord; import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; @@ -126,6 +129,8 @@ public class QJavalinProcessHandler get("/status/{jobUUID}", QJavalinProcessHandler::processStatus); get("/records", QJavalinProcessHandler::processRecords); }); + + get("/possibleValues/{fieldName}", QJavalinProcessHandler::possibleValues); }); }); get("/download/{file}", QJavalinProcessHandler::downloadFile); @@ -734,6 +739,57 @@ public class QJavalinProcessHandler + /******************************************************************************* + ** + *******************************************************************************/ + private static void possibleValues(Context context) + { + try + { + String processName = context.pathParam("processName"); + String fieldName = context.pathParam("fieldName"); + String searchTerm = context.queryParam("searchTerm"); + String ids = context.queryParam("ids"); + + QProcessMetaData process = QJavalinImplementation.qInstance.getProcess(processName); + if(process == null) + { + throw (new QNotFoundException("Could not find process named " + processName + " in this instance.")); + } + + Optional optField = process.getInputFields().stream().filter(f -> f.getName().equals(fieldName)).findFirst(); + QFieldMetaData field = optField.orElseThrow(() -> new QNotFoundException("Could not find field named " + fieldName + " in process " + processName + ".")); + + if(!StringUtils.hasContent(field.getPossibleValueSourceName())) + { + throw (new QNotFoundException("Field " + fieldName + " in process " + processName + " is not associated with a possible value source.")); + } + + SearchPossibleValueSourceInput input = new SearchPossibleValueSourceInput(); + QJavalinImplementation.setupSession(context, input); + input.setPossibleValueSourceName(field.getPossibleValueSourceName()); + input.setSearchTerm(searchTerm); + + if(StringUtils.hasContent(ids)) + { + List idList = new ArrayList<>(Arrays.asList(ids.split(","))); + input.setIdList(idList); + } + + SearchPossibleValueSourceOutput output = new SearchPossibleValueSourceAction().execute(input); + + Map result = new HashMap<>(); + result.put("options", output.getResults()); + context.result(JsonUtils.toJson(result)); + } + catch(Exception e) + { + QJavalinImplementation.handleException(context, e); + } + } + + + /******************************************************************************* ** *******************************************************************************/