From b397c4da080bf61b20ccd03bc75d6a48ed3edfbc Mon Sep 17 00:00:00 2001 From: Darin Kelkhoff Date: Sat, 11 Jan 2025 20:13:51 -0600 Subject: [PATCH] CE-1955 Add plugins for QInstanceEnricher --- .../core/instances/QInstanceEnricher.java | 85 +++++++++++++++++++ .../QInstanceEnricherPluginInterface.java | 40 +++++++++ .../core/instances/QInstanceEnricherTest.java | 46 ++++++++++ 3 files changed, 171 insertions(+) create mode 100644 qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/validation/plugins/QInstanceEnricherPluginInterface.java diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricher.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricher.java index a0d926c0..fbe0ea54 100644 --- a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricher.java +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricher.java @@ -27,6 +27,7 @@ import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -34,6 +35,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader; @@ -41,6 +43,7 @@ import com.kingsrook.qqq.backend.core.actions.metadata.JoinGraph; import com.kingsrook.qqq.backend.core.actions.permissions.BulkTableActionProcessPermissionChecker; import com.kingsrook.qqq.backend.core.actions.values.QCustomPossibleValueProvider; import com.kingsrook.qqq.backend.core.exceptions.QException; +import com.kingsrook.qqq.backend.core.instances.validation.plugins.QInstanceEnricherPluginInterface; import com.kingsrook.qqq.backend.core.logging.QLogger; import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QInstance; @@ -52,6 +55,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.DynamicDefaultValueB import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType; +import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection; @@ -120,6 +124,8 @@ public class QInstanceEnricher ////////////////////////////////////////////////////////////////////////////////////////////////// private static final Map labelMappings = new LinkedHashMap<>(); + private static ListingHash, QInstanceEnricherPluginInterface> enricherPlugins = new ListingHash<>(); + /******************************************************************************* @@ -181,6 +187,7 @@ public class QInstanceEnricher } enrichJoins(); + enrichInstance(); ////////////////////////////////////////////////////////////////////////////// // if the instance DOES have 1 or more scheduler, but no schedulable types, // @@ -197,6 +204,16 @@ public class QInstanceEnricher + /*************************************************************************** + ** + ***************************************************************************/ + private void enrichInstance() + { + runPlugins(QInstance.class, qInstance, qInstance); + } + + + /******************************************************************************* ** *******************************************************************************/ @@ -261,6 +278,14 @@ public class QInstanceEnricher } } } + + /////////////////////////////////////////// + // run plugins on joins if there are any // + /////////////////////////////////////////// + for(QJoinMetaData join : qInstance.getJoins().values()) + { + runPlugins(QJoinMetaData.class, join, qInstance); + } } catch(Exception e) { @@ -276,6 +301,7 @@ public class QInstanceEnricher private void enrichWidget(QWidgetMetaDataInterface widgetMetaData) { enrichPermissionRules(widgetMetaData); + runPlugins(QWidgetMetaDataInterface.class, widgetMetaData, qInstance); } @@ -286,6 +312,7 @@ public class QInstanceEnricher private void enrichBackend(QBackendMetaData qBackendMetaData) { qBackendMetaData.enrich(); + runPlugins(QBackendMetaData.class, qBackendMetaData, qInstance); } @@ -326,6 +353,7 @@ public class QInstanceEnricher enrichPermissionRules(table); enrichAuditRules(table); + runPlugins(QTableMetaData.class, table, qInstance); } @@ -416,6 +444,7 @@ public class QInstanceEnricher } enrichPermissionRules(process); + runPlugins(QProcessMetaData.class, process, qInstance); } @@ -537,6 +566,8 @@ public class QInstanceEnricher field.withBehavior(DynamicDefaultValueBehavior.MODIFY_DATE); } } + + runPlugins(QFieldMetaData.class, field, qInstance); } @@ -608,6 +639,7 @@ public class QInstanceEnricher ensureAppSectionMembersAreAppChildren(app); enrichPermissionRules(app); + runPlugins(QAppMetaData.class, app, qInstance); } @@ -755,6 +787,7 @@ public class QInstanceEnricher } enrichPermissionRules(report); + runPlugins(QReportMetaData.class, report, qInstance); } @@ -1408,6 +1441,58 @@ public class QInstanceEnricher } } } + + runPlugins(QPossibleValueSource.class, possibleValueSource, qInstance); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public static void addEnricherPlugin(QInstanceEnricherPluginInterface plugin) + { + Optional enrichMethod = Arrays.stream(plugin.getClass().getDeclaredMethods()) + .filter(m -> m.getName().equals("enrich") + && m.getParameterCount() == 2 + && !m.getParameterTypes()[0].equals(Object.class) + && m.getParameterTypes()[1].equals(QInstance.class) + ).findFirst(); + + if(enrichMethod.isPresent()) + { + Class parameterType = enrichMethod.get().getParameterTypes()[0]; + enricherPlugins.add(parameterType, plugin); + } + else + { + LOG.warn("Could not find enrich method on enricher plugin [" + plugin.getClass().getName() + "] (to infer type being enriched) - this plugin will not be used."); + } + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + public static void removeAllEnricherPlugins() + { + enricherPlugins.clear(); + } + + + + /******************************************************************************* + ** + *******************************************************************************/ + private void runPlugins(Class c, T t, QInstance qInstance) + { + for(QInstanceEnricherPluginInterface plugin : CollectionUtils.nonNullList(enricherPlugins.get(c))) + { + @SuppressWarnings("unchecked") + QInstanceEnricherPluginInterface castedPlugin = (QInstanceEnricherPluginInterface) plugin; + castedPlugin.enrich(t, qInstance); + } } diff --git a/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/validation/plugins/QInstanceEnricherPluginInterface.java b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/validation/plugins/QInstanceEnricherPluginInterface.java new file mode 100644 index 00000000..9a900772 --- /dev/null +++ b/qqq-backend-core/src/main/java/com/kingsrook/qqq/backend/core/instances/validation/plugins/QInstanceEnricherPluginInterface.java @@ -0,0 +1,40 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2024. 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.instances.validation.plugins; + + +import com.kingsrook.qqq.backend.core.model.metadata.QInstance; + + +/******************************************************************************* + ** Interface for additional / optional enrichment to be done on q instance members. + ** Some may be provided by QQQ - others can be defined by applications. + *******************************************************************************/ +public interface QInstanceEnricherPluginInterface +{ + + /******************************************************************************* + ** + *******************************************************************************/ + void enrich(T object, QInstance qInstance); + +} diff --git a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricherTest.java b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricherTest.java index 20f87686..7b356218 100644 --- a/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricherTest.java +++ b/qqq-backend-core/src/test/java/com/kingsrook/qqq/backend/core/instances/QInstanceEnricherTest.java @@ -27,6 +27,7 @@ import java.util.Collections; import java.util.List; import java.util.Optional; import com.kingsrook.qqq.backend.core.BaseTest; +import com.kingsrook.qqq.backend.core.instances.validation.plugins.QInstanceEnricherPluginInterface; 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.fields.AdornmentType; @@ -47,6 +48,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection; import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData; import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier; import com.kingsrook.qqq.backend.core.utils.TestUtils; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import static com.kingsrook.qqq.backend.core.utils.TestUtils.APP_NAME_GREETINGS; import static com.kingsrook.qqq.backend.core.utils.TestUtils.APP_NAME_MISCELLANEOUS; @@ -66,6 +68,17 @@ import static org.junit.jupiter.api.Assertions.assertTrue; class QInstanceEnricherTest extends BaseTest { + /******************************************************************************* + ** + *******************************************************************************/ + @AfterEach + void afterEach() + { + QInstanceEnricher.removeAllEnricherPlugins(); + } + + + /******************************************************************************* ** Test that a table missing a label gets the default label applied (name w/ UC-first). ** @@ -572,4 +585,37 @@ class QInstanceEnricherTest extends BaseTest assertEquals("My Field", qInstance.getProcess("test").getFrontendStep("screen").getViewFields().get(0).getLabel()); } + + + /******************************************************************************* + ** + *******************************************************************************/ + @Test + void testFieldPlugIn() + { + QInstance qInstance = TestUtils.defineInstance(); + + QInstanceEnricher.addEnricherPlugin(new QInstanceEnricherPluginInterface() + { + /*************************************************************************** + * + ***************************************************************************/ + @Override + public void enrich(QFieldMetaData field, QInstance qInstance) + { + if(field != null) + { + field.setLabel(field.getLabel() + " Plugged"); + } + } + }); + + new QInstanceEnricher(qInstance).enrich(); + + qInstance.getTables().values().forEach(table -> table.getFields().values().forEach(field -> assertThat(field.getLabel()).endsWith("Plugged"))); + qInstance.getProcesses().values().forEach(process -> process.getInputFields().forEach(field -> assertThat(field.getLabel()).endsWith("Plugged"))); + qInstance.getProcesses().values().forEach(process -> process.getOutputFields().forEach(field -> assertThat(field.getLabel()).endsWith("Plugged"))); + + } + }