From e936468f29e691f523d9aae7e0dd399fa11afccc Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Tue, 20 Feb 2024 16:53:24 -0600 Subject: [PATCH] Add validation check for widget names used within apps --- .../core/instances/QInstanceValidator.java | 44 +++++++++++ .../instances/QInstanceValidatorTest.java | 78 ++++++++++++++++++- 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java index 624c22e3..b1f00190 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidator.java @@ -37,6 +37,7 @@ import java.util.function.Supplier; import java.util.stream.Stream; import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandler; import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers; +import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.AbstractWidgetRenderer; import com.kingsrook.qqq.backend.core.actions.metadata.JoinGraph; import com.kingsrook.qqq.backend.core.actions.processes.BackendStep; import com.kingsrook.qqq.backend.core.actions.scripts.TestScriptActionInterface; @@ -52,6 +53,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.QInstance; import com.kingsrook.qqq.backend.core.model.metadata.QSupplementalInstanceMetaData; import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference; import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType; +import com.kingsrook.qqq.backend.core.model.metadata.dashboard.ParentWidgetMetaData; import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType; import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; @@ -158,6 +160,7 @@ public class QInstanceValidator validateProcesses(qInstance); validateReports(qInstance); validateApps(qInstance); + validateWidgets(qInstance); validatePossibleValueSources(qInstance); validateQueuesAndProviders(qInstance); validateJoins(qInstance); @@ -1546,12 +1549,53 @@ public class QInstanceValidator } } } + + ////////////////////// + // validate widgets // + ////////////////////// + for(String widgetName : CollectionUtils.nonNullList(app.getWidgets())) + { + assertCondition(qInstance.getWidget(widgetName) != null, "App " + appName + " widget " + widgetName + " is not a recognized widget."); + } }); } } + /******************************************************************************* + ** + *******************************************************************************/ + private void validateWidgets(QInstance qInstance) + { + if(CollectionUtils.nullSafeHasContents(qInstance.getWidgets())) + { + qInstance.getWidgets().forEach((widgetName, widget) -> + { + assertCondition(Objects.equals(widgetName, widget.getName()), "Inconsistent naming for widget: " + widgetName + "/" + widget.getName() + "."); + + if(assertCondition(widget.getCodeReference() != null, "Missing codeReference for widget: " + widgetName)) + { + validateSimpleCodeReference("Widget " + widgetName + " code reference: ", widget.getCodeReference(), AbstractWidgetRenderer.class); + } + + if(widget instanceof ParentWidgetMetaData parentWidgetMetaData) + { + if(assertCondition(CollectionUtils.nullSafeHasContents(parentWidgetMetaData.getChildWidgetNameList()), "Missing child widgets for parent widget: " + widget.getName())) + { + for(String childWidgetName : parentWidgetMetaData.getChildWidgetNameList()) + { + assertCondition(qInstance.getWidget(childWidgetName) != null, "Unrecognized child widget name [" + childWidgetName + "] in parent widget: " + widget.getName()); + } + } + } + } + ); + } + } + + + /******************************************************************************* ** *******************************************************************************/ diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidatorTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidatorTest.java index f66dfa16..7cba47f0 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidatorTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/instances/QInstanceValidatorTest.java @@ -31,6 +31,9 @@ import java.util.function.Function; import com.kingsrook.qqq.backend.core.BaseTest; import com.kingsrook.qqq.backend.core.actions.customizers.AbstractPostQueryCustomizer; import com.kingsrook.qqq.backend.core.actions.customizers.TableCustomizers; +import com.kingsrook.qqq.backend.core.actions.dashboard.PersonsByCreateDateBarChart; +import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.AbstractWidgetRenderer; +import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.ParentWidgetRenderer; import com.kingsrook.qqq.backend.core.actions.processes.BackendStep; import com.kingsrook.qqq.backend.core.context.QContext; import com.kingsrook.qqq.backend.core.exceptions.QException; @@ -46,6 +49,7 @@ 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.code.QCodeReference; import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType; +import com.kingsrook.qqq.backend.core.model.metadata.dashboard.ParentWidgetMetaData; import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType; import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; @@ -803,7 +807,7 @@ class QInstanceValidatorTest extends BaseTest ** *******************************************************************************/ @Test - void testChildNotInAnySections() + void testAppChildNotInAnySections() { QTableMetaData table = new QTableMetaData().withName("test") .withBackendName(TestUtils.DEFAULT_BACKEND_NAME) @@ -822,6 +826,19 @@ class QInstanceValidatorTest extends BaseTest + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testAppUnrecognizedWidgetName() + { + QAppMetaData app = new QAppMetaData().withName("test") + .withWidgets(List.of("no-such-widget")); + assertValidationFailureReasons((qInstance) -> qInstance.addApp(app), "not a recognized widget"); + } + + + /******************************************************************************* ** *******************************************************************************/ @@ -1813,6 +1830,65 @@ class QInstanceValidatorTest extends BaseTest + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testWidgetNaming() + { + String name = PersonsByCreateDateBarChart.class.getSimpleName(); + + assertValidationFailureReasons((qInstance) -> qInstance.getWidget(name).withName(null), + "Inconsistent naming for widget"); + + assertValidationFailureReasons((qInstance) -> qInstance.getWidget(name).withName(""), + "Inconsistent naming for widget"); + + assertValidationFailureReasons((qInstance) -> qInstance.getWidget(name).withName("wrongName"), + "Inconsistent naming for widget"); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testWidgetCodeReference() + { + String name = PersonsByCreateDateBarChart.class.getSimpleName(); + + assertValidationFailureReasons((qInstance) -> qInstance.getWidget(name).withCodeReference(null), + "Missing codeReference for widget"); + + assertValidationFailureReasons((qInstance) -> qInstance.getWidget(name).withCodeReference(new QCodeReference(ArrayList.class)), + "CodeReference is not of the expected type: class " + AbstractWidgetRenderer.class.getName()); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testParentWidgets() + { + assertValidationFailureReasons((qInstance) -> qInstance.addWidget(new ParentWidgetMetaData() + .withName("parentWidget") + .withCodeReference(new QCodeReference(ParentWidgetRenderer.class)) + ), + "Missing child widgets"); + + assertValidationFailureReasons((qInstance) -> qInstance.addWidget(new ParentWidgetMetaData() + .withChildWidgetNameList(List.of("noSuchWidget")) + .withName("parentWidget") + .withCodeReference(new QCodeReference(ParentWidgetRenderer.class)) + ), + "Unrecognized child widget name"); + } + + + /******************************************************************************* ** *******************************************************************************/