CE-1955 Add plugins for QInstanceEnricher

This commit is contained in:
2025-01-11 20:13:51 -06:00
parent e2c7748a4b
commit b397c4da08
3 changed files with 171 additions and 0 deletions

View File

@ -27,6 +27,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType; import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -34,6 +35,7 @@ import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader; 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.permissions.BulkTableActionProcessPermissionChecker;
import com.kingsrook.qqq.backend.core.actions.values.QCustomPossibleValueProvider; import com.kingsrook.qqq.backend.core.actions.values.QCustomPossibleValueProvider;
import com.kingsrook.qqq.backend.core.exceptions.QException; 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.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData; import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance; 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.FieldAdornment;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData; 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.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.QAppChildMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection; import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppSection;
@ -120,6 +124,8 @@ public class QInstanceEnricher
////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////
private static final Map<String, String> labelMappings = new LinkedHashMap<>(); private static final Map<String, String> labelMappings = new LinkedHashMap<>();
private static ListingHash<Class<?>, QInstanceEnricherPluginInterface<?>> enricherPlugins = new ListingHash<>();
/******************************************************************************* /*******************************************************************************
@ -181,6 +187,7 @@ public class QInstanceEnricher
} }
enrichJoins(); enrichJoins();
enrichInstance();
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// if the instance DOES have 1 or more scheduler, but no schedulable types, // // 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) catch(Exception e)
{ {
@ -276,6 +301,7 @@ public class QInstanceEnricher
private void enrichWidget(QWidgetMetaDataInterface widgetMetaData) private void enrichWidget(QWidgetMetaDataInterface widgetMetaData)
{ {
enrichPermissionRules(widgetMetaData); enrichPermissionRules(widgetMetaData);
runPlugins(QWidgetMetaDataInterface.class, widgetMetaData, qInstance);
} }
@ -286,6 +312,7 @@ public class QInstanceEnricher
private void enrichBackend(QBackendMetaData qBackendMetaData) private void enrichBackend(QBackendMetaData qBackendMetaData)
{ {
qBackendMetaData.enrich(); qBackendMetaData.enrich();
runPlugins(QBackendMetaData.class, qBackendMetaData, qInstance);
} }
@ -326,6 +353,7 @@ public class QInstanceEnricher
enrichPermissionRules(table); enrichPermissionRules(table);
enrichAuditRules(table); enrichAuditRules(table);
runPlugins(QTableMetaData.class, table, qInstance);
} }
@ -416,6 +444,7 @@ public class QInstanceEnricher
} }
enrichPermissionRules(process); enrichPermissionRules(process);
runPlugins(QProcessMetaData.class, process, qInstance);
} }
@ -537,6 +566,8 @@ public class QInstanceEnricher
field.withBehavior(DynamicDefaultValueBehavior.MODIFY_DATE); field.withBehavior(DynamicDefaultValueBehavior.MODIFY_DATE);
} }
} }
runPlugins(QFieldMetaData.class, field, qInstance);
} }
@ -608,6 +639,7 @@ public class QInstanceEnricher
ensureAppSectionMembersAreAppChildren(app); ensureAppSectionMembersAreAppChildren(app);
enrichPermissionRules(app); enrichPermissionRules(app);
runPlugins(QAppMetaData.class, app, qInstance);
} }
@ -755,6 +787,7 @@ public class QInstanceEnricher
} }
enrichPermissionRules(report); 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<Method> 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 <T> void runPlugins(Class<T> c, T t, QInstance qInstance)
{
for(QInstanceEnricherPluginInterface<?> plugin : CollectionUtils.nonNullList(enricherPlugins.get(c)))
{
@SuppressWarnings("unchecked")
QInstanceEnricherPluginInterface<T> castedPlugin = (QInstanceEnricherPluginInterface<T>) plugin;
castedPlugin.enrich(t, qInstance);
}
} }

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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<T>
{
/*******************************************************************************
**
*******************************************************************************/
void enrich(T object, QInstance qInstance);
}

View File

@ -27,6 +27,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import com.kingsrook.qqq.backend.core.BaseTest; 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.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference; import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType; 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.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier; import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
import com.kingsrook.qqq.backend.core.utils.TestUtils; import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test; 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_GREETINGS;
import static com.kingsrook.qqq.backend.core.utils.TestUtils.APP_NAME_MISCELLANEOUS; 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 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). ** 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()); assertEquals("My Field", qInstance.getProcess("test").getFrontendStep("screen").getViewFields().get(0).getLabel());
} }
/*******************************************************************************
**
*******************************************************************************/
@Test
void testFieldPlugIn()
{
QInstance qInstance = TestUtils.defineInstance();
QInstanceEnricher.addEnricherPlugin(new QInstanceEnricherPluginInterface<QFieldMetaData>()
{
/***************************************************************************
*
***************************************************************************/
@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")));
}
} }