mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-20 22:18:43 +00:00
Compare commits
4 Commits
snapshot-f
...
snapshot-i
Author | SHA1 | Date | |
---|---|---|---|
bb8d3156e6 | |||
ad0a3dcdc7 | |||
c9ccdf58cd | |||
b1f3613e00 |
2
pom.xml
2
pom.xml
@ -48,7 +48,7 @@
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<revision>0.25.0-SNAPSHOT</revision>
|
||||
<revision>0.24.0-SNAPSHOT</revision>
|
||||
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
|
@ -83,7 +83,7 @@ public class RunRecordScriptAutomationHandler extends RecordAutomationHandler
|
||||
}
|
||||
|
||||
QRecord scriptRevision = queryOutput.getRecords().get(0);
|
||||
LOG.debug("Running script against records", logPair("scriptRevisionId", scriptRevision.getValue("id")), logPair("scriptId", scriptRevision.getValue("scriptIdd")));
|
||||
LOG.info("Running script against records", logPair("scriptRevisionId", scriptRevision.getValue("id")), logPair("scriptId", scriptRevision.getValue("scriptIdd")));
|
||||
|
||||
RunAdHocRecordScriptInput input = new RunAdHocRecordScriptInput();
|
||||
input.setCodeReference(new AdHocScriptCodeReference().withScriptRevisionRecord(scriptRevision));
|
||||
|
@ -649,7 +649,7 @@ public class PollingAutomationPerTableRunner implements Runnable
|
||||
input.setRecordList(records);
|
||||
input.setAction(action);
|
||||
|
||||
RecordAutomationHandler recordAutomationHandler = QCodeLoader.getAdHoc(RecordAutomationHandler.class, action.getCodeReference());
|
||||
RecordAutomationHandler recordAutomationHandler = QCodeLoader.getRecordAutomationHandler(action);
|
||||
recordAutomationHandler.execute(input);
|
||||
}
|
||||
}
|
||||
|
@ -24,11 +24,17 @@ package com.kingsrook.qqq.backend.core.actions.customizers;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandler;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.QCustomPossibleValueProvider;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.InitializableViaCodeReference;
|
||||
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.possiblevalues.QPossibleValueSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TableAutomationAction;
|
||||
import com.kingsrook.qqq.backend.core.utils.lambdas.UnsafeFunction;
|
||||
import com.kingsrook.qqq.backend.core.utils.memoization.Memoization;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
@ -37,7 +43,9 @@ import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
/*******************************************************************************
|
||||
** Utility to load code for running QQQ customizers.
|
||||
**
|
||||
** That memoization causes 1,000,000 such calls to go from ~500ms to ~100ms.
|
||||
** TODO - redo all to go through method that memoizes class & constructor
|
||||
** lookup. That memoziation causes 1,000,000 such calls to go from ~500ms
|
||||
** to ~100ms.
|
||||
*******************************************************************************/
|
||||
public class QCodeLoader
|
||||
{
|
||||
@ -62,6 +70,84 @@ public class QCodeLoader
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T, R> Function<T, R> getFunction(QCodeReference codeReference)
|
||||
{
|
||||
if(codeReference == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
if(!codeReference.getCodeType().equals(QCodeType.JAVA))
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// todo - 1) support more languages, 2) wrap them w/ java Functions here, 3) profit! //
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
throw (new IllegalArgumentException("Only JAVA customizers are supported at this time."));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Class<?> customizerClass = Class.forName(codeReference.getName());
|
||||
return ((Function<T, R>) customizerClass.getConstructor().newInstance());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.error("Error initializing customizer", e, logPair("codeReference", codeReference));
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// return null here - under the assumption that during normal run-time operations, we'll never hit here //
|
||||
// as we'll want to validate all functions in the instance validator at startup time (and IT will throw //
|
||||
// if it finds an invalid code reference //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
return (null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends BackendStep> T getBackendStep(Class<T> expectedType, QCodeReference codeReference)
|
||||
{
|
||||
if(codeReference == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
if(!codeReference.getCodeType().equals(QCodeType.JAVA))
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// todo - 1) support more languages, 2) wrap them w/ java Functions here, 3) profit! //
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
throw (new IllegalArgumentException("Only JAVA BackendSteps are supported at this time."));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Class<?> customizerClass = Class.forName(codeReference.getName());
|
||||
return ((T) customizerClass.getConstructor().newInstance());
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.error("Error initializing customizer", e, logPair("codeReference", codeReference));
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// return null here - under the assumption that during normal run-time operations, we'll never hit here //
|
||||
// as we'll want to validate all functions in the instance validator at startup time (and IT will throw //
|
||||
// if it finds an invalid code reference //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
return (null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -91,17 +177,7 @@ public class QCodeLoader
|
||||
|
||||
if(constructor.isPresent())
|
||||
{
|
||||
T t = (T) constructor.get().newInstance();
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// if the object is initializable, then, well, initialize it! //
|
||||
////////////////////////////////////////////////////////////////
|
||||
if(t instanceof InitializableViaCodeReference initializableViaCodeReference)
|
||||
{
|
||||
initializableViaCodeReference.initialize(codeReference);
|
||||
}
|
||||
|
||||
return t;
|
||||
return ((T) constructor.get().newInstance());
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -111,7 +187,7 @@ public class QCodeLoader
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.error("Error initializing codeReference", e, logPair("codeReference", codeReference));
|
||||
LOG.error("Error initializing customizer", e, logPair("codeReference", codeReference));
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// return null here - under the assumption that during normal run-time operations, we'll never hit here //
|
||||
@ -122,4 +198,67 @@ public class QCodeLoader
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static RecordAutomationHandler getRecordAutomationHandler(TableAutomationAction action) throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
QCodeReference codeReference = action.getCodeReference();
|
||||
if(!codeReference.getCodeType().equals(QCodeType.JAVA))
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
// todo - 1) support more languages, 2) wrap them w/ java Functions here, 3) profit! //
|
||||
///////////////////////////////////////////////////////////////////////////////////////
|
||||
throw (new IllegalArgumentException("Only JAVA customizers are supported at this time."));
|
||||
}
|
||||
|
||||
Class<?> codeClass = Class.forName(codeReference.getName());
|
||||
Object codeObject = codeClass.getConstructor().newInstance();
|
||||
if(!(codeObject instanceof RecordAutomationHandler recordAutomationHandler))
|
||||
{
|
||||
throw (new QException("The supplied code [" + codeClass.getName() + "] is not an instance of RecordAutomationHandler"));
|
||||
}
|
||||
return (recordAutomationHandler);
|
||||
}
|
||||
catch(QException qe)
|
||||
{
|
||||
throw (qe);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new QException("Error getting record automation handler for action [" + action.getName() + "]", e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QCustomPossibleValueProvider getCustomPossibleValueProvider(QPossibleValueSource possibleValueSource) throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
Class<?> codeClass = Class.forName(possibleValueSource.getCustomCodeReference().getName());
|
||||
Object codeObject = codeClass.getConstructor().newInstance();
|
||||
if(!(codeObject instanceof QCustomPossibleValueProvider customPossibleValueProvider))
|
||||
{
|
||||
throw (new QException("The supplied code [" + codeClass.getName() + "] is not an instance of QCustomPossibleValueProvider"));
|
||||
}
|
||||
return (customPossibleValueProvider);
|
||||
}
|
||||
catch(QException qe)
|
||||
{
|
||||
throw (qe);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new QException("Error getting custom possible value provider for PVS [" + possibleValueSource.getName() + "]", e));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,7 +27,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
@ -161,18 +160,4 @@ public interface RecordCustomizerUtilityInterface
|
||||
return (oldRecordMap);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
static <T extends Serializable> T getValueFromRecordOrOldRecord(String fieldName, QRecord record, Serializable primaryKey, Optional<Map<Serializable, QRecord>> oldRecordMap)
|
||||
{
|
||||
T value = (T) record.getValue(fieldName);
|
||||
if(value == null && primaryKey != null && oldRecordMap.isPresent() && oldRecordMap.get().containsKey(primaryKey))
|
||||
{
|
||||
value = (T) oldRecordMap.get().get(primaryKey).getValue(fieldName);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -290,18 +290,7 @@ public abstract class AbstractHTMLWidgetRenderer extends AbstractWidgetRenderer
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "call one that doesn't take input param")
|
||||
public static String linkRecordEdit(AbstractActionInput input, String tableName, Serializable recordId) throws QException
|
||||
{
|
||||
return linkRecordEdit(tableName, recordId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static String linkRecordEdit(String tableName, Serializable recordId) throws QException
|
||||
{
|
||||
String tablePath = QContext.getQInstance().getTablePath(tableName);
|
||||
return (tablePath + "/" + recordId + "/edit");
|
||||
@ -328,17 +317,7 @@ public abstract class AbstractHTMLWidgetRenderer extends AbstractWidgetRenderer
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "call one that doesn't take input param")
|
||||
public static String linkProcessForFilter(AbstractActionInput input, String processName, QQueryFilter filter) throws QException
|
||||
{
|
||||
return linkProcessForFilter(processName, filter);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static String linkProcessForFilter(String processName, QQueryFilter filter) throws QException
|
||||
{
|
||||
QProcessMetaData process = QContext.getQInstance().getProcess(processName);
|
||||
if(process == null)
|
||||
@ -358,21 +337,10 @@ public abstract class AbstractHTMLWidgetRenderer extends AbstractWidgetRenderer
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "call one that doesn't take input param")
|
||||
public static String linkProcessForRecord(AbstractActionInput input, String processName, Serializable recordId) throws QException
|
||||
{
|
||||
return linkProcessForRecord(processName, recordId);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static String linkProcessForRecord(String processName, Serializable recordId) throws QException
|
||||
{
|
||||
QProcessMetaData process = QContext.getQInstance().getProcess(processName);
|
||||
String tableName = process.getTableName();
|
||||
|
@ -1,251 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. 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.actions.dashboard.widgets;
|
||||
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.CountAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
||||
import com.kingsrook.qqq.backend.core.instances.validation.plugins.QInstanceValidatorPluginInterface;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.FilterUseCase;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.ChildRecordListData;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.WidgetType;
|
||||
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.dashboard.AbstractWidgetMetaDataBuilder;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Generic widget to display a list of records.
|
||||
**
|
||||
** Note, closely related to (and copied from ChildRecordListRenderer.
|
||||
** opportunity to share more code with that in the future??
|
||||
*******************************************************************************/
|
||||
public class RecordListWidgetRenderer extends AbstractWidgetRenderer
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(RecordListWidgetRenderer.class);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static Builder widgetMetaDataBuilder(String widgetName)
|
||||
{
|
||||
return (new Builder(new QWidgetMetaData()
|
||||
.withName(widgetName)
|
||||
.withIsCard(true)
|
||||
.withCodeReference(new QCodeReference(RecordListWidgetRenderer.class))
|
||||
.withType(WidgetType.CHILD_RECORD_LIST.getType())
|
||||
.withValidatorPlugin(new RecordListWidgetValidator())
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static class Builder extends AbstractWidgetMetaDataBuilder
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder(QWidgetMetaData widgetMetaData)
|
||||
{
|
||||
super(widgetMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withLabel(String label)
|
||||
{
|
||||
widgetMetaData.setLabel(label);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withMaxRows(Integer maxRows)
|
||||
{
|
||||
widgetMetaData.withDefaultValue("maxRows", maxRows);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withTableName(String tableName)
|
||||
{
|
||||
widgetMetaData.withDefaultValue("tableName", tableName);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Builder withFilter(QQueryFilter filter)
|
||||
{
|
||||
widgetMetaData.withDefaultValue("filter", filter);
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public RenderWidgetOutput render(RenderWidgetInput input) throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
Integer maxRows = null;
|
||||
if(StringUtils.hasContent(input.getQueryParams().get("maxRows")))
|
||||
{
|
||||
maxRows = ValueUtils.getValueAsInteger(input.getQueryParams().get("maxRows"));
|
||||
}
|
||||
else if(input.getWidgetMetaData().getDefaultValues().containsKey("maxRows"))
|
||||
{
|
||||
maxRows = ValueUtils.getValueAsInteger(input.getWidgetMetaData().getDefaultValues().get("maxRows"));
|
||||
}
|
||||
|
||||
QQueryFilter filter = ((QQueryFilter) input.getWidgetMetaData().getDefaultValues().get("filter")).clone();
|
||||
filter.interpretValues(new HashMap<>(input.getQueryParams()), FilterUseCase.DEFAULT);
|
||||
filter.setLimit(maxRows);
|
||||
|
||||
String tableName = ValueUtils.getValueAsString(input.getWidgetMetaData().getDefaultValues().get("tableName"));
|
||||
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
||||
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(tableName);
|
||||
queryInput.setShouldTranslatePossibleValues(true);
|
||||
queryInput.setShouldGenerateDisplayValues(true);
|
||||
queryInput.setFilter(filter);
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
|
||||
QValueFormatter.setBlobValuesToDownloadUrls(table, queryOutput.getRecords());
|
||||
|
||||
int totalRows = queryOutput.getRecords().size();
|
||||
if(maxRows != null && (queryOutput.getRecords().size() == maxRows))
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// if the input said to only do some max, and the # of results we got is that max, //
|
||||
// then do a count query, for displaying 1-n of <count> //
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
CountInput countInput = new CountInput();
|
||||
countInput.setTableName(tableName);
|
||||
countInput.setFilter(filter);
|
||||
totalRows = new CountAction().execute(countInput).getCount();
|
||||
}
|
||||
|
||||
String tablePath = QContext.getQInstance().getTablePath(tableName);
|
||||
String viewAllLink = tablePath == null ? null : (tablePath + "?filter=" + URLEncoder.encode(JsonUtils.toJson(filter), Charset.defaultCharset()));
|
||||
|
||||
ChildRecordListData widgetData = new ChildRecordListData(input.getQueryParams().get("widgetLabel"), queryOutput, table, tablePath, viewAllLink, totalRows);
|
||||
|
||||
return (new RenderWidgetOutput(widgetData));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error rendering record list widget", e, logPair("widgetName", () -> input.getWidgetMetaData().getName()));
|
||||
throw (e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private static class RecordListWidgetValidator implements QInstanceValidatorPluginInterface<QWidgetMetaDataInterface>
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void validate(QWidgetMetaDataInterface widgetMetaData, QInstance qInstance, QInstanceValidator qInstanceValidator)
|
||||
{
|
||||
String prefix = "Widget " + widgetMetaData.getName() + ": ";
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// make sure table name is given and exists //
|
||||
//////////////////////////////////////////////
|
||||
QTableMetaData table = null;
|
||||
String tableName = ValueUtils.getValueAsString(CollectionUtils.nonNullMap(widgetMetaData.getDefaultValues()).get("tableName"));
|
||||
if(qInstanceValidator.assertCondition(StringUtils.hasContent(tableName), prefix + "defaultValue for tableName must be given"))
|
||||
{
|
||||
////////////////////////////
|
||||
// make sure table exists //
|
||||
////////////////////////////
|
||||
table = qInstance.getTable(tableName);
|
||||
qInstanceValidator.assertCondition(table != null, prefix + "No table named " + tableName + " exists in the instance");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// make sure filter is given and is valid (only check that if table is given too) //
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
QQueryFilter filter = ((QQueryFilter) widgetMetaData.getDefaultValues().get("filter"));
|
||||
if(qInstanceValidator.assertCondition(filter != null, prefix + "defaultValue for filter must be given") && table != null)
|
||||
{
|
||||
qInstanceValidator.validateQueryFilter(qInstance, prefix, table, filter, null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -33,7 +33,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
/*******************************************************************************
|
||||
** a default implementation of MetaDataFilterInterface, that allows all the things
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "migrated to metaDataCustomizer")
|
||||
public class AllowAllMetaDataFilter implements MetaDataFilterInterface
|
||||
{
|
||||
|
||||
|
@ -1,92 +0,0 @@
|
||||
/*
|
||||
* 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.actions.metadata;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** a default implementation of MetaDataFilterInterface, that is all noop.
|
||||
*******************************************************************************/
|
||||
public class DefaultNoopMetaDataActionCustomizer implements MetaDataActionCustomizerInterface
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public boolean allowTable(MetaDataInput input, QTableMetaData table)
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public boolean allowProcess(MetaDataInput input, QProcessMetaData process)
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public boolean allowReport(MetaDataInput input, QReportMetaData report)
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public boolean allowApp(MetaDataInput input, QAppMetaData app)
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public boolean allowWidget(MetaDataInput input, QWidgetMetaDataInterface widget)
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
}
|
@ -23,12 +23,10 @@ package com.kingsrook.qqq.backend.core.actions.metadata;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionCheckResult;
|
||||
@ -36,7 +34,6 @@ import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
|
||||
@ -68,7 +65,7 @@ public class MetaDataAction
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(MetaDataAction.class);
|
||||
|
||||
private static Memoization<QInstance, MetaDataActionCustomizerInterface> metaDataActionCustomizerMemoization = new Memoization<>();
|
||||
private static Memoization<QInstance, MetaDataFilterInterface> metaDataFilterMemoization = new Memoization<>();
|
||||
|
||||
|
||||
|
||||
@ -82,7 +79,7 @@ public class MetaDataAction
|
||||
MetaDataOutput metaDataOutput = new MetaDataOutput();
|
||||
Map<String, AppTreeNode> treeNodes = new LinkedHashMap<>();
|
||||
|
||||
MetaDataActionCustomizerInterface customizer = getMetaDataActionCustomizer();
|
||||
MetaDataFilterInterface filter = getMetaDataFilter();
|
||||
|
||||
/////////////////////////////////////
|
||||
// map tables to frontend metadata //
|
||||
@ -93,7 +90,7 @@ public class MetaDataAction
|
||||
String tableName = entry.getKey();
|
||||
QTableMetaData table = entry.getValue();
|
||||
|
||||
if(!customizer.allowTable(metaDataInput, table))
|
||||
if(!filter.allowTable(metaDataInput, table))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -122,7 +119,7 @@ public class MetaDataAction
|
||||
String processName = entry.getKey();
|
||||
QProcessMetaData process = entry.getValue();
|
||||
|
||||
if(!customizer.allowProcess(metaDataInput, process))
|
||||
if(!filter.allowProcess(metaDataInput, process))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -147,7 +144,7 @@ public class MetaDataAction
|
||||
String reportName = entry.getKey();
|
||||
QReportMetaData report = entry.getValue();
|
||||
|
||||
if(!customizer.allowReport(metaDataInput, report))
|
||||
if(!filter.allowReport(metaDataInput, report))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -172,7 +169,7 @@ public class MetaDataAction
|
||||
String widgetName = entry.getKey();
|
||||
QWidgetMetaDataInterface widget = entry.getValue();
|
||||
|
||||
if(!customizer.allowWidget(metaDataInput, widget))
|
||||
if(!filter.allowWidget(metaDataInput, widget))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -209,7 +206,7 @@ public class MetaDataAction
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!customizer.allowApp(metaDataInput, app))
|
||||
if(!filter.allowApp(metaDataInput, app))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -295,22 +292,11 @@ public class MetaDataAction
|
||||
metaDataOutput.setBranding(QContext.getQInstance().getBranding());
|
||||
}
|
||||
|
||||
metaDataOutput.setEnvironmentValues(Objects.requireNonNullElse(QContext.getQInstance().getEnvironmentValues(), Collections.emptyMap()));
|
||||
metaDataOutput.setEnvironmentValues(QContext.getQInstance().getEnvironmentValues());
|
||||
|
||||
metaDataOutput.setHelpContents(Objects.requireNonNullElse(QContext.getQInstance().getHelpContent(), Collections.emptyMap()));
|
||||
metaDataOutput.setHelpContents(QContext.getQInstance().getHelpContent());
|
||||
|
||||
try
|
||||
{
|
||||
customizer.postProcess(metaDataOutput);
|
||||
}
|
||||
catch(QUserFacingException e)
|
||||
{
|
||||
LOG.debug("User-facing exception thrown in meta-data customizer post-processing", e);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Unexpected error thrown in meta-data customizer post-processing", e);
|
||||
}
|
||||
// todo post-customization - can do whatever w/ the result if you want?
|
||||
|
||||
return metaDataOutput;
|
||||
}
|
||||
@ -320,36 +306,26 @@ public class MetaDataAction
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private MetaDataActionCustomizerInterface getMetaDataActionCustomizer()
|
||||
private MetaDataFilterInterface getMetaDataFilter()
|
||||
{
|
||||
return metaDataActionCustomizerMemoization.getResult(QContext.getQInstance(), i ->
|
||||
return metaDataFilterMemoization.getResult(QContext.getQInstance(), i ->
|
||||
{
|
||||
MetaDataActionCustomizerInterface actionCustomizer = null;
|
||||
QCodeReference metaDataActionCustomizerReference = QContext.getQInstance().getMetaDataActionCustomizer();
|
||||
if(metaDataActionCustomizerReference != null)
|
||||
MetaDataFilterInterface filter = null;
|
||||
QCodeReference metaDataFilterReference = QContext.getQInstance().getMetaDataFilter();
|
||||
if(metaDataFilterReference != null)
|
||||
{
|
||||
actionCustomizer = QCodeLoader.getAdHoc(MetaDataActionCustomizerInterface.class, metaDataActionCustomizerReference);
|
||||
LOG.debug("Using new meta-data actionCustomizer of type: " + actionCustomizer.getClass().getSimpleName());
|
||||
filter = QCodeLoader.getAdHoc(MetaDataFilterInterface.class, metaDataFilterReference);
|
||||
LOG.debug("Using new meta-data filter of type: " + filter.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
if(actionCustomizer == null)
|
||||
if(filter == null)
|
||||
{
|
||||
QCodeReference metaDataFilterReference = QContext.getQInstance().getMetaDataFilter();
|
||||
if(metaDataFilterReference != null)
|
||||
{
|
||||
actionCustomizer = QCodeLoader.getAdHoc(MetaDataActionCustomizerInterface.class, metaDataFilterReference);
|
||||
LOG.debug("Using new meta-data actionCustomizer (via metaDataFilter reference) of type: " + actionCustomizer.getClass().getSimpleName());
|
||||
}
|
||||
filter = new AllowAllMetaDataFilter();
|
||||
LOG.debug("Using new default (allow-all) meta-data filter");
|
||||
}
|
||||
|
||||
if(actionCustomizer == null)
|
||||
{
|
||||
actionCustomizer = new DefaultNoopMetaDataActionCustomizer();
|
||||
LOG.debug("Using new default (allow-all) meta-data actionCustomizer");
|
||||
}
|
||||
|
||||
return (actionCustomizer);
|
||||
}).orElseThrow(() -> new QRuntimeException("Error getting MetaDataActionCustomizer"));
|
||||
return (filter);
|
||||
}).orElseThrow(() -> new QRuntimeException("Error getting metaDataFilter"));
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,78 +0,0 @@
|
||||
/*
|
||||
* 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.actions.metadata;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Interface for customizations that can be injected by an application into
|
||||
** the MetaDataAction - e.g., loading applicable meta-data for a user into a
|
||||
** frontend.
|
||||
*******************************************************************************/
|
||||
public interface MetaDataActionCustomizerInterface
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
boolean allowTable(MetaDataInput input, QTableMetaData table);
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
boolean allowProcess(MetaDataInput input, QProcessMetaData process);
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
boolean allowReport(MetaDataInput input, QReportMetaData report);
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
boolean allowApp(MetaDataInput input, QAppMetaData app);
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
boolean allowWidget(MetaDataInput input, QWidgetMetaDataInterface widget);
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
default void postProcess(MetaDataOutput metaDataOutput) throws QException
|
||||
{
|
||||
/////////////////////
|
||||
// noop by default //
|
||||
/////////////////////
|
||||
}
|
||||
|
||||
}
|
@ -22,11 +22,43 @@
|
||||
package com.kingsrook.qqq.backend.core.actions.metadata;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "migrated to metaDataCustomizer")
|
||||
public interface MetaDataFilterInterface extends MetaDataActionCustomizerInterface
|
||||
public interface MetaDataFilterInterface
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
boolean allowTable(MetaDataInput input, QTableMetaData table);
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
boolean allowProcess(MetaDataInput input, QProcessMetaData process);
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
boolean allowReport(MetaDataInput input, QReportMetaData report);
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
boolean allowApp(MetaDataInput input, QAppMetaData app);
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
boolean allowWidget(MetaDataInput input, QWidgetMetaDataInterface widget);
|
||||
|
||||
}
|
||||
|
@ -36,8 +36,6 @@ import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||
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.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||
@ -48,7 +46,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaD
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFunctionInputMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
|
||||
|
||||
@ -191,40 +188,21 @@ public class RunBackendStepAction
|
||||
{
|
||||
if(CollectionUtils.nullSafeIsEmpty(runBackendStepInput.getRecords()))
|
||||
{
|
||||
QTableMetaData table = QContext.getQInstance().getTable(inputMetaData.getRecordListMetaData().getTableName());
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(table.getName());
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(inputMetaData.getRecordListMetaData().getTableName());
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// look for record ids in the input data values //
|
||||
//////////////////////////////////////////////////
|
||||
String recordIds = (String) runBackendStepInput.getValue("recordIds");
|
||||
if(recordIds == null)
|
||||
// todo - handle this being async (e.g., http)
|
||||
// seems like it just needs to throw, breaking this flow, and to send a response to the frontend, directing it to prompt the user for the needed data
|
||||
// then this step can re-run, hopefully with the needed data.
|
||||
|
||||
QProcessCallback callback = runBackendStepInput.getCallback();
|
||||
if(callback == null)
|
||||
{
|
||||
recordIds = (String) runBackendStepInput.getValue("recordId");
|
||||
throw (new QUserFacingException("Missing input records.",
|
||||
new QException("Function is missing input records, but no callback was present to request fields from a user")));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// if records were found, add as criteria to query input //
|
||||
///////////////////////////////////////////////////////////
|
||||
if(recordIds != null)
|
||||
{
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria(table.getPrimaryKeyField(), QCriteriaOperator.IN, recordIds.split(","))));
|
||||
}
|
||||
else
|
||||
{
|
||||
// todo - handle this being async (e.g., http)
|
||||
// seems like it just needs to throw, breaking this flow, and to send a response to the frontend, directing it to prompt the user for the needed data
|
||||
// then this step can re-run, hopefully with the needed data.
|
||||
QProcessCallback callback = runBackendStepInput.getCallback();
|
||||
if(callback == null)
|
||||
{
|
||||
throw (new QUserFacingException("Missing input records.",
|
||||
new QException("Function is missing input records, but no callback was present to request fields from a user")));
|
||||
}
|
||||
|
||||
queryInput.setFilter(callback.getQueryFilter());
|
||||
}
|
||||
queryInput.setFilter(callback.getQueryFilter());
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if process has a max-no of records, set a limit on the process of that number plus 1 //
|
||||
@ -232,7 +210,7 @@ public class RunBackendStepAction
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(process.getMaxInputRecords() != null)
|
||||
{
|
||||
if(queryInput.getFilter() == null)
|
||||
if(callback.getQueryFilter() == null)
|
||||
{
|
||||
queryInput.setFilter(new QQueryFilter());
|
||||
}
|
||||
|
@ -108,7 +108,7 @@ public class CsvExportStreamer implements ExportStreamerInterface
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new QReportingException("Error starting CSV report", e));
|
||||
throw (new QReportingException("Error starting CSV report"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,14 +54,6 @@ public interface ExportStreamerInterface
|
||||
// noop in base class
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
default void setExportStyleCustomizer(ExportStyleCustomizerInterface exportStyleCustomizer)
|
||||
{
|
||||
// noop in base class
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
** Called once per sheet, before any rows are available. Meant to write a
|
||||
** header, for example.
|
||||
|
@ -1,35 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.actions.reporting;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** interface for classes that can be used to customize visual style aspects of
|
||||
** exports/reports.
|
||||
**
|
||||
** Anticipates very different sub-interfaces based on the file type being generated,
|
||||
** and the capabilities of each. e.g., excel (bolds, fonts, cell merging) vs
|
||||
** json (different structure of objects).
|
||||
*******************************************************************************/
|
||||
public interface ExportStyleCustomizerInterface
|
||||
{
|
||||
}
|
@ -163,17 +163,6 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
||||
reportStreamer = reportFormat.newReportStreamer();
|
||||
}
|
||||
|
||||
if(reportInput.getExportStyleCustomizer() != null)
|
||||
{
|
||||
ExportStyleCustomizerInterface styleCustomizer = QCodeLoader.getAdHoc(ExportStyleCustomizerInterface.class, reportInput.getExportStyleCustomizer());
|
||||
reportStreamer.setExportStyleCustomizer(styleCustomizer);
|
||||
}
|
||||
else if(report.getExportStyleCustomizer() != null)
|
||||
{
|
||||
ExportStyleCustomizerInterface styleCustomizer = QCodeLoader.getAdHoc(ExportStyleCustomizerInterface.class, report.getExportStyleCustomizer());
|
||||
reportStreamer.setExportStyleCustomizer(styleCustomizer);
|
||||
}
|
||||
|
||||
reportStreamer.preRun(reportInput.getReportDestination(), views);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -222,8 +211,7 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(dataSourceTableView.getViewCustomizer() != null)
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
Function<QReportView, QReportView> viewCustomizerFunction = QCodeLoader.getAdHoc(Function.class, dataSourceTableView.getViewCustomizer());
|
||||
Function<QReportView, QReportView> viewCustomizerFunction = QCodeLoader.getFunction(dataSourceTableView.getViewCustomizer());
|
||||
if(viewCustomizerFunction instanceof ReportViewCustomizer reportViewCustomizer)
|
||||
{
|
||||
reportViewCustomizer.setReportInput(reportInput);
|
||||
@ -672,7 +660,7 @@ public class GenerateReportAction extends AbstractQActionFunction<ReportInput, R
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if any fields are 'showPossibleValueLabel', then move display values for them into the record's values map //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
for(QReportField column : CollectionUtils.nonNullList(tableView.getColumns()))
|
||||
for(QReportField column : tableView.getColumns())
|
||||
{
|
||||
if(column.getShowPossibleValueLabel())
|
||||
{
|
||||
|
@ -46,7 +46,6 @@ import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.ExportStreamerInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.ExportStyleCustomizerInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.ReportUtils;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QReportingException;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
|
||||
@ -78,7 +77,6 @@ import org.apache.poi.xssf.usermodel.XSSFPivotTable;
|
||||
import org.apache.poi.xssf.usermodel.XSSFRow;
|
||||
import org.apache.poi.xssf.usermodel.XSSFSheet;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -114,10 +112,9 @@ public class ExcelPoiBasedStreamingExportStreamer implements ExportStreamerInter
|
||||
public static final String EXCEL_DATE_FORMAT = "yyyy-MM-dd";
|
||||
public static final String EXCEL_DATE_TIME_FORMAT = "yyyy-MM-dd H:mm:ss";
|
||||
|
||||
private ExcelPoiBasedStreamingStyleCustomizerInterface styleCustomizerInterface;
|
||||
|
||||
private PoiExcelStylerInterface poiExcelStylerInterface = getStylerInterface();
|
||||
private Map<String, String> excelCellFormats;
|
||||
private Map<String, XSSFCellStyle> styles = new HashMap<>();
|
||||
private Map<String, XSSFCellStyle> styles = new HashMap<>();
|
||||
|
||||
private int rowNo = 0;
|
||||
private int sheetIndex = 1;
|
||||
@ -405,7 +402,6 @@ public class ExcelPoiBasedStreamingExportStreamer implements ExportStreamerInter
|
||||
dateTimeStyle.setDataFormat(createHelper.createDataFormat().getFormat(EXCEL_DATE_TIME_FORMAT));
|
||||
styles.put("datetime", dateTimeStyle);
|
||||
|
||||
PoiExcelStylerInterface poiExcelStylerInterface = getStylerInterface();
|
||||
styles.put("title", poiExcelStylerInterface.createStyleForTitle(workbook, createHelper));
|
||||
styles.put("header", poiExcelStylerInterface.createStyleForHeader(workbook, createHelper));
|
||||
styles.put("footer", poiExcelStylerInterface.createStyleForFooter(workbook, createHelper));
|
||||
@ -417,11 +413,6 @@ public class ExcelPoiBasedStreamingExportStreamer implements ExportStreamerInter
|
||||
XSSFCellStyle footerDateTimeStyle = poiExcelStylerInterface.createStyleForFooter(workbook, createHelper);
|
||||
footerDateTimeStyle.setDataFormat(createHelper.createDataFormat().getFormat(EXCEL_DATE_TIME_FORMAT));
|
||||
styles.put("footer-datetime", footerDateTimeStyle);
|
||||
|
||||
if(styleCustomizerInterface != null)
|
||||
{
|
||||
styleCustomizerInterface.customizeStyles(styles, workbook, createHelper);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -467,7 +458,7 @@ public class ExcelPoiBasedStreamingExportStreamer implements ExportStreamerInter
|
||||
}
|
||||
else
|
||||
{
|
||||
sheetWriter.beginSheet(view, styleCustomizerInterface);
|
||||
sheetWriter.beginSheet();
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// put the title and header rows in the sheet //
|
||||
@ -569,16 +560,6 @@ public class ExcelPoiBasedStreamingExportStreamer implements ExportStreamerInter
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static void setStyleForField(QRecord record, String fieldName, String styleName)
|
||||
{
|
||||
record.setDisplayValue(fieldName + ":excelStyle", styleName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -586,12 +567,12 @@ public class ExcelPoiBasedStreamingExportStreamer implements ExportStreamerInter
|
||||
{
|
||||
sheetWriter.insertRow(rowNo++);
|
||||
|
||||
int baseStyleIndex = -1;
|
||||
int styleIndex = -1;
|
||||
int dateStyleIndex = styles.get("date").getIndex();
|
||||
int dateTimeStyleIndex = styles.get("datetime").getIndex();
|
||||
if(isFooter)
|
||||
{
|
||||
baseStyleIndex = styles.get("footer").getIndex();
|
||||
styleIndex = styles.get("footer").getIndex();
|
||||
dateStyleIndex = styles.get("footer-date").getIndex();
|
||||
dateTimeStyleIndex = styles.get("footer-datetime").getIndex();
|
||||
}
|
||||
@ -601,13 +582,6 @@ public class ExcelPoiBasedStreamingExportStreamer implements ExportStreamerInter
|
||||
{
|
||||
Serializable value = qRecord.getValue(field.getName());
|
||||
|
||||
String overrideStyleName = qRecord.getDisplayValue(field.getName() + ":excelStyle");
|
||||
int styleIndex = baseStyleIndex;
|
||||
if(overrideStyleName != null)
|
||||
{
|
||||
styleIndex = styles.get(overrideStyleName).getIndex();
|
||||
}
|
||||
|
||||
if(value != null)
|
||||
{
|
||||
if(value instanceof String s)
|
||||
@ -732,7 +706,7 @@ public class ExcelPoiBasedStreamingExportStreamer implements ExportStreamerInter
|
||||
{
|
||||
if(!ReportType.PIVOT.equals(currentView.getType()))
|
||||
{
|
||||
sheetWriter.endSheet(currentView, styleCustomizerInterface);
|
||||
sheetWriter.endSheet();
|
||||
}
|
||||
|
||||
activeSheetWriter.flush();
|
||||
@ -841,29 +815,7 @@ public class ExcelPoiBasedStreamingExportStreamer implements ExportStreamerInter
|
||||
*******************************************************************************/
|
||||
protected PoiExcelStylerInterface getStylerInterface()
|
||||
{
|
||||
if(styleCustomizerInterface != null)
|
||||
{
|
||||
return styleCustomizerInterface.getExcelStyler();
|
||||
}
|
||||
|
||||
return (new PlainPoiExcelStyler());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void setExportStyleCustomizer(ExportStyleCustomizerInterface exportStyleCustomizer)
|
||||
{
|
||||
if(exportStyleCustomizer instanceof ExcelPoiBasedStreamingStyleCustomizerInterface poiExcelStylerInterface)
|
||||
{
|
||||
this.styleCustomizerInterface = poiExcelStylerInterface;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.debug("Supplied export style customizer is not an instance of ExcelPoiStyleCustomizerInterface, so will not be used for an excel export", logPair("exportStyleCustomizerClass", exportStyleCustomizer.getClass().getSimpleName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,81 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.actions.reporting.excel.poi;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.ExportStyleCustomizerInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportView;
|
||||
import org.apache.poi.ss.usermodel.CreationHelper;
|
||||
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** style customization points for Excel files generated via our streaming POI.
|
||||
*******************************************************************************/
|
||||
public interface ExcelPoiBasedStreamingStyleCustomizerInterface extends ExportStyleCustomizerInterface
|
||||
{
|
||||
/***************************************************************************
|
||||
** slightly legacy way we did excel styles - but get an instance of object
|
||||
** that defaults "default" styles (header, footer, etc).
|
||||
***************************************************************************/
|
||||
default PoiExcelStylerInterface getExcelStyler()
|
||||
{
|
||||
return (new PlainPoiExcelStyler());
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** either change "default" styles put in the styles map, or create new ones
|
||||
** which can then be applied to row/field values (cells) via:
|
||||
** ExcelPoiBasedStreamingExportStreamer.setStyleForField(row, fieldName, styleName);
|
||||
***************************************************************************/
|
||||
default void customizeStyles(Map<String, XSSFCellStyle> styles, XSSFWorkbook workbook, CreationHelper createHelper)
|
||||
{
|
||||
//////////////////
|
||||
// noop default //
|
||||
//////////////////
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** for a given view (sheet), return a list of custom column widths.
|
||||
** any nulls in the list are ignored (so default width is used).
|
||||
***************************************************************************/
|
||||
default List<Integer> getColumnWidthsForView(QReportView view)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** for a given view (sheet), return a list of any ranges which should be
|
||||
** merged, as in "A1:C1" (first three cells in first row).
|
||||
***************************************************************************/
|
||||
default List<String> getMergedRangesForView(QReportView view)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
}
|
@ -25,10 +25,7 @@ package com.kingsrook.qqq.backend.core.actions.reporting.excel.poi;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportView;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import org.apache.poi.ss.util.CellReference;
|
||||
|
||||
|
||||
@ -56,33 +53,13 @@ public class StreamedSheetWriter
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void beginSheet(QReportView view, ExcelPoiBasedStreamingStyleCustomizerInterface styleCustomizerInterface) throws IOException
|
||||
public void beginSheet() throws IOException
|
||||
{
|
||||
writer.write("""
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">""");
|
||||
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
|
||||
<sheetData>""");
|
||||
|
||||
if(styleCustomizerInterface != null && view != null)
|
||||
{
|
||||
List<Integer> columnWidths = styleCustomizerInterface.getColumnWidthsForView(view);
|
||||
if(CollectionUtils.nullSafeHasContents(columnWidths))
|
||||
{
|
||||
writer.write("<cols>");
|
||||
for(int i = 0; i < columnWidths.size(); i++)
|
||||
{
|
||||
Integer width = columnWidths.get(i);
|
||||
if(width != null)
|
||||
{
|
||||
writer.write("""
|
||||
<col min="%d" max="%d" width="%d" customWidth="1"/>
|
||||
""".formatted(i + 1, i + 1, width));
|
||||
}
|
||||
}
|
||||
writer.write("</cols>");
|
||||
}
|
||||
}
|
||||
|
||||
writer.write("<sheetData>");
|
||||
}
|
||||
|
||||
|
||||
@ -90,25 +67,11 @@ public class StreamedSheetWriter
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void endSheet(QReportView view, ExcelPoiBasedStreamingStyleCustomizerInterface styleCustomizerInterface) throws IOException
|
||||
public void endSheet() throws IOException
|
||||
{
|
||||
writer.write("</sheetData>");
|
||||
|
||||
if(styleCustomizerInterface != null && view != null)
|
||||
{
|
||||
List<String> mergedRanges = styleCustomizerInterface.getMergedRangesForView(view);
|
||||
if(CollectionUtils.nullSafeHasContents(mergedRanges))
|
||||
{
|
||||
writer.write(String.format("<mergeCells count=\"%d\">", mergedRanges.size()));
|
||||
for(String range : mergedRanges)
|
||||
{
|
||||
writer.write(String.format("<mergeCell ref=\"%s\"/>", range));
|
||||
}
|
||||
writer.write("</mergeCells>");
|
||||
}
|
||||
}
|
||||
|
||||
writer.write("</worksheet>");
|
||||
writer.write("""
|
||||
</sheetData>
|
||||
</worksheet>""");
|
||||
}
|
||||
|
||||
|
||||
@ -188,7 +151,7 @@ public class StreamedSheetWriter
|
||||
{
|
||||
rs.append(""");
|
||||
}
|
||||
else if(c < 32 && c != '\t' && c != '\n')
|
||||
else if (c < 32 && c != '\t' && c != '\n')
|
||||
{
|
||||
rs.append(' ');
|
||||
}
|
||||
|
@ -53,8 +53,7 @@ public class QJavaExecutor implements QCodeExecutor
|
||||
Serializable output;
|
||||
try
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
Function<Map<String, Object>, Serializable> function = QCodeLoader.getAdHoc(Function.class, codeReference);
|
||||
Function<Map<String, Object>, Serializable> function = QCodeLoader.getFunction(codeReference);
|
||||
output = function.apply(context);
|
||||
}
|
||||
catch(Exception e)
|
||||
|
@ -32,7 +32,6 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.querystats.QueryStat;
|
||||
@ -83,22 +82,6 @@ public class CountAction
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** shorthand way to call for the most common use-case, when you just want the
|
||||
** count to be returned, and you just want to pass in a table name and filter.
|
||||
*******************************************************************************/
|
||||
public static Integer execute(String tableName, QQueryFilter filter) throws QException
|
||||
{
|
||||
CountAction countAction = new CountAction();
|
||||
CountInput countInput = new CountInput();
|
||||
countInput.setTableName(tableName);
|
||||
countInput.setFilter(filter);
|
||||
CountOutput countOutput = countAction.execute(countInput);
|
||||
return (countOutput.getCount());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -238,11 +238,6 @@ public class GetAction
|
||||
*******************************************************************************/
|
||||
public static QRecord execute(String tableName, Serializable primaryKey) throws QException
|
||||
{
|
||||
if(primaryKey instanceof QQueryFilter)
|
||||
{
|
||||
LOG.warn("Unexpected use of QQueryFilter instead of primary key in GetAction call");
|
||||
}
|
||||
|
||||
GetAction getAction = new GetAction();
|
||||
GetInput getInput = new GetInput(tableName).withPrimaryKey(primaryKey);
|
||||
return getAction.executeForRecord(getInput);
|
||||
|
@ -114,7 +114,7 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
|
||||
|
||||
if(!StringUtils.hasContent(insertInput.getTableName()))
|
||||
{
|
||||
throw (new QException("Table name was not specified in insert input"));
|
||||
throw (new QException("Table name was not specified in update input"));
|
||||
}
|
||||
|
||||
QTableMetaData table = insertInput.getTable();
|
||||
|
@ -24,7 +24,6 @@ package com.kingsrook.qqq.backend.core.actions.values;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.values.SearchPossibleValueSourceInput;
|
||||
@ -75,31 +74,6 @@ public interface QCustomPossibleValueProvider<T extends Serializable>
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** meant to be protected (but interface...) - for a custom PVS implementation
|
||||
** to complete its search (e.g., after it generates the list of PVS objects,
|
||||
** let this method do the filtering).
|
||||
***************************************************************************/
|
||||
default List<QPossibleValue<T>> completeCustomPVSSearch(SearchPossibleValueSourceInput input, List<QPossibleValue<T>> possibleValues)
|
||||
{
|
||||
SearchPossibleValueSourceAction.PreparedSearchPossibleValueSourceInput preparedInput = SearchPossibleValueSourceAction.prepareSearchPossibleValueSourceInput(input);
|
||||
|
||||
List<QPossibleValue<T>> rs = new ArrayList<>();
|
||||
|
||||
for(QPossibleValue<T> possibleValue : possibleValues)
|
||||
{
|
||||
if(possibleValue != null && SearchPossibleValueSourceAction.doesPossibleValueMatchSearchInput(possibleValue, preparedInput))
|
||||
{
|
||||
rs.add(possibleValue);
|
||||
}
|
||||
}
|
||||
|
||||
rs.sort(Comparator.nullsLast(Comparator.comparing((QPossibleValue<T> pv) -> pv.getLabel())));
|
||||
|
||||
return (rs);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -341,7 +341,7 @@ public class QPossibleValueTranslator
|
||||
|
||||
try
|
||||
{
|
||||
QCustomPossibleValueProvider<?> customPossibleValueProvider = QCodeLoader.getAdHoc(QCustomPossibleValueProvider.class, possibleValueSource.getCustomCodeReference());
|
||||
QCustomPossibleValueProvider customPossibleValueProvider = QCodeLoader.getCustomPossibleValueProvider(possibleValueSource);
|
||||
return (formatPossibleValue(possibleValueSource, customPossibleValueProvider.getPossibleValue(value)));
|
||||
}
|
||||
catch(Exception e)
|
||||
|
@ -23,6 +23,8 @@ package com.kingsrook.qqq.backend.core.actions.values;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
@ -32,6 +34,7 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
@ -45,7 +48,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
@ -486,8 +488,6 @@ public class QValueFormatter
|
||||
String fileNameFormat = ValueUtils.getValueAsString(adornmentValues.get(AdornmentType.FileDownloadValues.FILE_NAME_FORMAT));
|
||||
String defaultExtension = ValueUtils.getValueAsString(adornmentValues.get(AdornmentType.FileDownloadValues.DEFAULT_EXTENSION));
|
||||
|
||||
Boolean downloadUrlDynamic = ValueUtils.getValueAsBoolean(adornmentValues.get(AdornmentType.FileDownloadValues.DOWNLOAD_URL_DYNAMIC));
|
||||
|
||||
for(QRecord record : records)
|
||||
{
|
||||
if(!doesFieldHaveValue(field, record))
|
||||
@ -495,11 +495,6 @@ public class QValueFormatter
|
||||
continue;
|
||||
}
|
||||
|
||||
if(BooleanUtils.isTrue(downloadUrlDynamic))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Serializable primaryKey = record.getValue(table.getPrimaryKeyField());
|
||||
String fileName = null;
|
||||
|
||||
@ -549,7 +544,10 @@ public class QValueFormatter
|
||||
|| adornmentValues.containsKey(AdornmentType.FileDownloadValues.SUPPLEMENTAL_CODE_REFERENCE)
|
||||
|| adornmentValues.containsKey(AdornmentType.FileDownloadValues.SUPPLEMENTAL_PROCESS_NAME))
|
||||
{
|
||||
record.setValue(field.getName(), AdornmentType.FileDownloadValues.makeFieldDownloadUrl(table.getName(), primaryKey, field.getName(), fileName));
|
||||
record.setValue(field.getName(), "/data/" + table.getName() + "/"
|
||||
+ URLEncoder.encode(ValueUtils.getValueAsString(primaryKey), StandardCharsets.UTF_8) + "/"
|
||||
+ field.getName() + "/"
|
||||
+ URLEncoder.encode(Objects.requireNonNullElse(fileName, ""), StandardCharsets.UTF_8));
|
||||
}
|
||||
record.setDisplayValue(field.getName(), fileName);
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@ -52,6 +51,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleVal
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
@ -108,54 +108,60 @@ public class SearchPossibleValueSourceAction
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** record to store "computed" values as part of a possible-value search -
|
||||
** e.g., ids type-convered, and lower-cased labels.
|
||||
***************************************************************************/
|
||||
public record PreparedSearchPossibleValueSourceInput(Collection<?> inputIdsAsCorrectType, Collection<String> lowerCaseLabels, String searchTerm) {}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static PreparedSearchPossibleValueSourceInput prepareSearchPossibleValueSourceInput(SearchPossibleValueSourceInput input)
|
||||
{
|
||||
QPossibleValueSource possibleValueSource = QContext.getQInstance().getPossibleValueSource(input.getPossibleValueSourceName());
|
||||
List<?> inputIdsAsCorrectType = convertInputIdsToPossibleValueSourceIdType(possibleValueSource, input.getIdList());
|
||||
|
||||
Set<String> lowerCaseLabels = null;
|
||||
if(input.getLabelList() != null)
|
||||
{
|
||||
lowerCaseLabels = input.getLabelList().stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(l -> l.toLowerCase())
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
return (new PreparedSearchPossibleValueSourceInput(inputIdsAsCorrectType, lowerCaseLabels, input.getSearchTerm()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private SearchPossibleValueSourceOutput searchPossibleValueEnum(SearchPossibleValueSourceInput input, QPossibleValueSource possibleValueSource)
|
||||
{
|
||||
PreparedSearchPossibleValueSourceInput preparedSearchPossibleValueSourceInput = prepareSearchPossibleValueSourceInput(input);
|
||||
|
||||
SearchPossibleValueSourceOutput output = new SearchPossibleValueSourceOutput();
|
||||
List<Serializable> matchingIds = new ArrayList<>();
|
||||
|
||||
List<?> inputIdsAsCorrectType = convertInputIdsToEnumIdType(possibleValueSource, input.getIdList());
|
||||
Set<String> labels = null;
|
||||
|
||||
for(QPossibleValue<?> possibleValue : possibleValueSource.getEnumValues())
|
||||
{
|
||||
boolean match = doesPossibleValueMatchSearchInput(possibleValue, preparedSearchPossibleValueSourceInput);
|
||||
boolean match = false;
|
||||
|
||||
if(input.getIdList() != null)
|
||||
{
|
||||
if(inputIdsAsCorrectType.contains(possibleValue.getId()))
|
||||
{
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
else if(input.getLabelList() != null)
|
||||
{
|
||||
if(labels == null)
|
||||
{
|
||||
labels = input.getLabelList().stream().filter(Objects::nonNull).map(l -> l.toLowerCase()).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
if(labels.contains(possibleValue.getLabel().toLowerCase()))
|
||||
{
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(StringUtils.hasContent(input.getSearchTerm()))
|
||||
{
|
||||
match = (Objects.equals(ValueUtils.getValueAsString(possibleValue.getId()).toLowerCase(), input.getSearchTerm().toLowerCase())
|
||||
|| possibleValue.getLabel().toLowerCase().startsWith(input.getSearchTerm().toLowerCase()));
|
||||
}
|
||||
else
|
||||
{
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(match)
|
||||
{
|
||||
matchingIds.add(possibleValue.getId());
|
||||
matchingIds.add((Serializable) possibleValue.getId());
|
||||
}
|
||||
|
||||
// todo - skip & limit?
|
||||
// todo - default filter
|
||||
}
|
||||
|
||||
List<QPossibleValue<?>> qPossibleValues = possibleValueTranslator.buildTranslatedPossibleValueList(possibleValueSource, matchingIds);
|
||||
@ -166,84 +172,42 @@ public class SearchPossibleValueSourceAction
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static boolean doesPossibleValueMatchSearchInput(QPossibleValue<?> possibleValue, PreparedSearchPossibleValueSourceInput input)
|
||||
{
|
||||
boolean match = false;
|
||||
|
||||
if(input.inputIdsAsCorrectType() != null)
|
||||
{
|
||||
if(input.inputIdsAsCorrectType().contains(possibleValue.getId()))
|
||||
{
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
else if(input.lowerCaseLabels() != null)
|
||||
{
|
||||
if(input.lowerCaseLabels().contains(possibleValue.getLabel().toLowerCase()))
|
||||
{
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(StringUtils.hasContent(input.searchTerm()))
|
||||
{
|
||||
match = (Objects.equals(ValueUtils.getValueAsString(possibleValue.getId()).toLowerCase(), input.searchTerm().toLowerCase())
|
||||
|| possibleValue.getLabel().toLowerCase().startsWith(input.searchTerm().toLowerCase()));
|
||||
}
|
||||
else
|
||||
{
|
||||
match = true;
|
||||
}
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** The input list of ids might come through as a type that isn't the same as
|
||||
** the type of the ids in the enum (e.g., strings from a frontend, integers
|
||||
** in an enum). So, this method type-converts them.
|
||||
** in an enum). So, this method looks at the first id in the enum, and then
|
||||
** maps all the inputIds to be of the same type.
|
||||
*******************************************************************************/
|
||||
private static List<Object> convertInputIdsToPossibleValueSourceIdType(QPossibleValueSource possibleValueSource, List<Serializable> inputIdList)
|
||||
private List<Object> convertInputIdsToEnumIdType(QPossibleValueSource possibleValueSource, List<Serializable> inputIdList)
|
||||
{
|
||||
List<Object> rs = new ArrayList<>();
|
||||
|
||||
if(inputIdList == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
else if(inputIdList.isEmpty())
|
||||
if(CollectionUtils.nullSafeIsEmpty(inputIdList))
|
||||
{
|
||||
return (rs);
|
||||
}
|
||||
|
||||
QFieldType type = possibleValueSource.getIdType();
|
||||
Object anIdFromTheEnum = possibleValueSource.getEnumValues().get(0).getId();
|
||||
|
||||
for(Serializable inputId : inputIdList)
|
||||
{
|
||||
Object properlyTypedId = null;
|
||||
try
|
||||
{
|
||||
if(type.equals(QFieldType.INTEGER))
|
||||
if(anIdFromTheEnum instanceof Integer)
|
||||
{
|
||||
properlyTypedId = ValueUtils.getValueAsInteger(inputId);
|
||||
}
|
||||
else if(type.isStringLike())
|
||||
else if(anIdFromTheEnum instanceof String)
|
||||
{
|
||||
properlyTypedId = ValueUtils.getValueAsString(inputId);
|
||||
}
|
||||
else if(type.equals(QFieldType.BOOLEAN))
|
||||
else if(anIdFromTheEnum instanceof Boolean)
|
||||
{
|
||||
properlyTypedId = ValueUtils.getValueAsBoolean(inputId);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.warn("Unexpected type [" + type + "] for ids in enum: " + possibleValueSource.getName());
|
||||
LOG.warn("Unexpected type [" + anIdFromTheEnum.getClass().getSimpleName() + "] for ids in enum: " + possibleValueSource.getName());
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
@ -251,7 +215,7 @@ public class SearchPossibleValueSourceAction
|
||||
LOG.debug("Error converting possible value id to expected id type", e, logPair("value", inputId));
|
||||
}
|
||||
|
||||
if(properlyTypedId != null)
|
||||
if (properlyTypedId != null)
|
||||
{
|
||||
rs.add(properlyTypedId);
|
||||
}
|
||||
@ -424,7 +388,7 @@ public class SearchPossibleValueSourceAction
|
||||
{
|
||||
try
|
||||
{
|
||||
QCustomPossibleValueProvider customPossibleValueProvider = QCodeLoader.getAdHoc(QCustomPossibleValueProvider.class, possibleValueSource.getCustomCodeReference());
|
||||
QCustomPossibleValueProvider customPossibleValueProvider = QCodeLoader.getCustomPossibleValueProvider(possibleValueSource);
|
||||
List<QPossibleValue<?>> possibleValues = customPossibleValueProvider.search(input);
|
||||
|
||||
SearchPossibleValueSourceOutput output = new SearchPossibleValueSourceOutput();
|
||||
@ -433,9 +397,9 @@ public class SearchPossibleValueSourceAction
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
String message = "Error searching custom possible value source [" + input.getPossibleValueSourceName() + "]";
|
||||
String message = "Error sending searching custom possible value source [" + input.getPossibleValueSourceName() + "]";
|
||||
LOG.warn(message, e);
|
||||
throw (new QException(message, e));
|
||||
throw (new QException(message));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -95,7 +95,6 @@ import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.Bulk
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcess;
|
||||
import com.kingsrook.qqq.backend.core.scheduler.QScheduleManager;
|
||||
import com.kingsrook.qqq.backend.core.utils.ClassPathUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
@ -1425,7 +1424,7 @@ public class QInstanceEnricher
|
||||
{
|
||||
try
|
||||
{
|
||||
QCustomPossibleValueProvider<?> customPossibleValueProvider = QCodeLoader.getAdHoc(QCustomPossibleValueProvider.class, possibleValueSource.getCustomCodeReference());
|
||||
QCustomPossibleValueProvider<?> customPossibleValueProvider = QCodeLoader.getCustomPossibleValueProvider(possibleValueSource);
|
||||
|
||||
Method getPossibleValueMethod = customPossibleValueProvider.getClass().getDeclaredMethod("getPossibleValue", Serializable.class);
|
||||
Type returnType = getPossibleValueMethod.getGenericReturnType();
|
||||
@ -1483,31 +1482,6 @@ public class QInstanceEnricher
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** scan the classpath for classes in the specified package name which
|
||||
** implement the QInstanceEnricherPluginInterface - any found get added
|
||||
***************************************************************************/
|
||||
public static void discoverAndAddPluginsInPackage(String packageName) throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
for(Class<?> aClass : ClassPathUtils.getClassesInPackage(packageName))
|
||||
{
|
||||
if(QInstanceEnricherPluginInterface.class.isAssignableFrom(aClass))
|
||||
{
|
||||
QInstanceEnricherPluginInterface<?> plugin = (QInstanceEnricherPluginInterface<?>) aClass.getConstructor().newInstance();
|
||||
addEnricherPlugin(plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new QException("Error discovering and adding enricher plugins in package [" + packageName + "]", e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -45,7 +45,6 @@ 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.metadata.MetaDataActionCustomizerInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.metadata.MetaDataFilterInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.customizers.ReportCustomRecordSourceInterface;
|
||||
@ -143,8 +142,6 @@ public class QInstanceValidator
|
||||
|
||||
private static ListingHash<Class<?>, QInstanceValidatorPluginInterface<?>> validatorPlugins = new ListingHash<>();
|
||||
|
||||
private JoinGraph joinGraph = null;
|
||||
|
||||
private List<String> errors = new ArrayList<>();
|
||||
|
||||
|
||||
@ -172,7 +169,8 @@ public class QInstanceValidator
|
||||
// the enricher will build a join graph (if there are any joins). we'd like to only do that //
|
||||
// once, during the enrichment/validation work, so, capture it, and store it back in the instance. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
long start = System.currentTimeMillis();
|
||||
JoinGraph joinGraph = null;
|
||||
long start = System.currentTimeMillis();
|
||||
try
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -181,7 +179,7 @@ public class QInstanceValidator
|
||||
// TODO - possible point of customization (use a different enricher, or none, or pass it options).
|
||||
QInstanceEnricher qInstanceEnricher = new QInstanceEnricher(qInstance);
|
||||
qInstanceEnricher.enrich();
|
||||
this.joinGraph = qInstanceEnricher.getJoinGraph();
|
||||
joinGraph = qInstanceEnricher.getJoinGraph();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
@ -246,11 +244,6 @@ public class QInstanceValidator
|
||||
{
|
||||
validateSimpleCodeReference("Instance metaDataFilter ", qInstance.getMetaDataFilter(), MetaDataFilterInterface.class);
|
||||
}
|
||||
|
||||
if(qInstance.getMetaDataActionCustomizer() != null)
|
||||
{
|
||||
validateSimpleCodeReference("Instance metaDataActionCustomizer ", qInstance.getMetaDataActionCustomizer(), MetaDataActionCustomizerInterface.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1912,7 +1905,7 @@ public class QInstanceValidator
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void validateQueryFilter(QInstance qInstance, String context, QTableMetaData table, QQueryFilter queryFilter, List<QueryJoin> queryJoins)
|
||||
private void validateQueryFilter(QInstance qInstance, String context, QTableMetaData table, QQueryFilter queryFilter, List<QueryJoin> queryJoins)
|
||||
{
|
||||
for(QFilterCriteria criterion : CollectionUtils.nonNullList(queryFilter.getCriteria()))
|
||||
{
|
||||
@ -1956,8 +1949,7 @@ public class QInstanceValidator
|
||||
{
|
||||
if(fieldName.contains("."))
|
||||
{
|
||||
String fieldNameAfterDot = fieldName.substring(fieldName.lastIndexOf(".") + 1);
|
||||
String tableNameBeforeDot = fieldName.substring(0, fieldName.lastIndexOf("."));
|
||||
String fieldNameAfterDot = fieldName.substring(fieldName.lastIndexOf(".") + 1);
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(queryJoins))
|
||||
{
|
||||
@ -1981,32 +1973,11 @@ public class QInstanceValidator
|
||||
}
|
||||
else
|
||||
{
|
||||
if(this.joinGraph != null)
|
||||
{
|
||||
Set<JoinGraph.JoinConnectionList> joinConnections = joinGraph.getJoinConnections(table.getName());
|
||||
for(JoinGraph.JoinConnectionList joinConnectionList : joinConnections)
|
||||
{
|
||||
JoinGraph.JoinConnection joinConnection = joinConnectionList.list().get(joinConnectionList.list().size() - 1);
|
||||
if(tableNameBeforeDot.equals(joinConnection.joinTable()))
|
||||
{
|
||||
QTableMetaData joinTable = qInstance.getTable(tableNameBeforeDot);
|
||||
if(joinTable.getFields().containsKey(fieldNameAfterDot))
|
||||
{
|
||||
/////////////////////////
|
||||
// mmm, looks valid... //
|
||||
/////////////////////////
|
||||
return (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// todo - not sure how vulnerable we are to ongoing issues here... //
|
||||
// idea: let a filter (or any object?) be opted out of validation, some version of //
|
||||
// a static map of objects we can check at the top of various validate methods... //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
errors.add("Failed to find field named: " + fieldName);
|
||||
errors.add("QInstanceValidator does not yet support finding a field that looks like a join field, but isn't associated with a query.");
|
||||
return (true);
|
||||
// todo! for(QJoinMetaData join : CollectionUtils.nonNullMap(qInstance.getJoins()).values())
|
||||
// {
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,6 @@ import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.processes.tracing.ProcessTracerInterface;
|
||||
@ -618,14 +617,4 @@ public class RunBackendStepInput extends AbstractActionInput
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public QProcessMetaData getProcess()
|
||||
{
|
||||
return (QContext.getQInstance().getProcess(getProcessName()));
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,6 @@ import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.ExportStreamerInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
|
||||
|
||||
@ -45,7 +44,6 @@ public class ReportInput extends AbstractTableActionInput
|
||||
private ReportDestination reportDestination;
|
||||
|
||||
private Supplier<? extends ExportStreamerInterface> overrideExportStreamerSupplier;
|
||||
private QCodeReference exportStyleCustomizer;
|
||||
|
||||
|
||||
|
||||
@ -210,35 +208,4 @@ public class ReportInput extends AbstractTableActionInput
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for exportStyleCustomizer
|
||||
*******************************************************************************/
|
||||
public QCodeReference getExportStyleCustomizer()
|
||||
{
|
||||
return (this.exportStyleCustomizer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for exportStyleCustomizer
|
||||
*******************************************************************************/
|
||||
public void setExportStyleCustomizer(QCodeReference exportStyleCustomizer)
|
||||
{
|
||||
this.exportStyleCustomizer = exportStyleCustomizer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for exportStyleCustomizer
|
||||
*******************************************************************************/
|
||||
public ReportInput withExportStyleCustomizer(QCodeReference exportStyleCustomizer)
|
||||
{
|
||||
this.exportStyleCustomizer = exportStyleCustomizer;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -583,31 +583,4 @@ public abstract class QRecordEntity
|
||||
return (null);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static String getTableName(Class<? extends QRecordEntity> entityClass) throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
Field tableNameField = entityClass.getDeclaredField("TABLE_NAME");
|
||||
String tableNameValue = (String) tableNameField.get(null);
|
||||
return (tableNameValue);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new QException("Could not get TABLE_NAME from entity class: " + entityClass.getSimpleName(), e));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** named without the 'get' to avoid conflict w/ entity fields named that...
|
||||
***************************************************************************/
|
||||
public String tableName() throws QException
|
||||
{
|
||||
return (getTableName(this.getClass()));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -29,40 +29,5 @@ package com.kingsrook.qqq.backend.core.model.metadata;
|
||||
*******************************************************************************/
|
||||
public abstract class MetaDataProducer<T extends MetaDataProducerOutput> implements MetaDataProducerInterface<T>
|
||||
{
|
||||
private Class<?> sourceClass;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public Class<?> getSourceClass()
|
||||
{
|
||||
return sourceClass;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void setSourceClass(Class<?> sourceClass)
|
||||
{
|
||||
this.sourceClass = sourceClass;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public MetaDataProducer<T> withSourceClass(Class<?> sourceClass)
|
||||
{
|
||||
this.sourceClass = sourceClass;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -106,10 +106,14 @@ public class MetaDataProducerHelper
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
|
||||
/*******************************************************************************
|
||||
** Recursively find all classes in the given package, that implement MetaDataProducerInterface
|
||||
** run them, and add their output to the given qInstance.
|
||||
**
|
||||
***************************************************************************/
|
||||
public static List<MetaDataProducerInterface<?>> findProducers(String packageName) throws QException
|
||||
** Note - they'll be sorted by the sortOrder they provide.
|
||||
*******************************************************************************/
|
||||
public static void processAllMetaDataProducersInPackage(QInstance instance, String packageName) throws QException
|
||||
{
|
||||
List<Class<?>> classesInPackage;
|
||||
try
|
||||
@ -192,20 +196,6 @@ public class MetaDataProducerHelper
|
||||
}
|
||||
}));
|
||||
|
||||
return (producers);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Recursively find all classes in the given package, that implement MetaDataProducerInterface
|
||||
** run them, and add their output to the given qInstance.
|
||||
**
|
||||
** Note - they'll be sorted by the sortOrder they provide.
|
||||
*******************************************************************************/
|
||||
public static void processAllMetaDataProducersInPackage(QInstance instance, String packageName) throws QException
|
||||
{
|
||||
List<MetaDataProducerInterface<?>> producers = findProducers(packageName);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// execute each one (if enabled), adding their meta data to the instance //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@ -239,19 +229,17 @@ public class MetaDataProducerHelper
|
||||
**
|
||||
***************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T extends Serializable & PossibleValueEnum<T>> MetaDataProducerInterface<?> processMetaDataProducingPossibleValueEnum(Class<?> sourceClass)
|
||||
private static <T extends Serializable & PossibleValueEnum<T>> MetaDataProducerInterface<?> processMetaDataProducingPossibleValueEnum(Class<?> aClass)
|
||||
{
|
||||
String warningPrefix = "Found a class annotated as @" + QMetaDataProducingPossibleValueEnum.class.getSimpleName();
|
||||
if(!PossibleValueEnum.class.isAssignableFrom(sourceClass))
|
||||
if(!PossibleValueEnum.class.isAssignableFrom(aClass))
|
||||
{
|
||||
LOG.warn(warningPrefix + ", but which is not a " + PossibleValueEnum.class.getSimpleName() + ", so it will not be used.", logPair("class", sourceClass.getSimpleName()));
|
||||
LOG.warn(warningPrefix + ", but which is not a " + PossibleValueEnum.class.getSimpleName() + ", so it will not be used.", logPair("class", aClass.getSimpleName()));
|
||||
return null;
|
||||
}
|
||||
|
||||
PossibleValueEnum<?>[] values = (PossibleValueEnum<?>[]) sourceClass.getEnumConstants();
|
||||
PossibleValueSourceOfEnumGenericMetaDataProducer<T> producer = new PossibleValueSourceOfEnumGenericMetaDataProducer<>(sourceClass.getSimpleName(), (PossibleValueEnum<T>[]) values);
|
||||
producer.setSourceClass(sourceClass);
|
||||
return producer;
|
||||
PossibleValueEnum<?>[] values = (PossibleValueEnum<?>[]) aClass.getEnumConstants();
|
||||
return (new PossibleValueSourceOfEnumGenericMetaDataProducer<T>(aClass.getSimpleName(), (PossibleValueEnum<T>[]) values));
|
||||
}
|
||||
|
||||
|
||||
@ -259,32 +247,32 @@ public class MetaDataProducerHelper
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private static List<MetaDataProducerInterface<?>> processMetaDataProducingEntity(Class<?> sourceClass) throws Exception
|
||||
private static List<MetaDataProducerInterface<?>> processMetaDataProducingEntity(Class<?> aClass) throws Exception
|
||||
{
|
||||
List<MetaDataProducerInterface<?>> rs = new ArrayList<>();
|
||||
|
||||
QMetaDataProducingEntity qMetaDataProducingEntity = sourceClass.getAnnotation(QMetaDataProducingEntity.class);
|
||||
QMetaDataProducingEntity qMetaDataProducingEntity = aClass.getAnnotation(QMetaDataProducingEntity.class);
|
||||
String warningPrefix = "Found a class annotated as @" + QMetaDataProducingEntity.class.getSimpleName();
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// make sures class is QRecordEntity and cast it as such //
|
||||
///////////////////////////////////////////////////////////
|
||||
if(!QRecordEntity.class.isAssignableFrom(sourceClass))
|
||||
if(!QRecordEntity.class.isAssignableFrom(aClass))
|
||||
{
|
||||
LOG.warn(warningPrefix + ", but which is not a " + QRecordEntity.class.getSimpleName() + ", so it will not be used.", logPair("class", sourceClass.getSimpleName()));
|
||||
LOG.warn(warningPrefix + ", but which is not a " + QRecordEntity.class.getSimpleName() + ", so it will not be used.", logPair("class", aClass.getSimpleName()));
|
||||
return (rs);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked") // safe per the check above.
|
||||
Class<? extends QRecordEntity> recordEntityClass = (Class<? extends QRecordEntity>) sourceClass;
|
||||
Class<? extends QRecordEntity> recordEntityClass = (Class<? extends QRecordEntity>) aClass;
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// get TABLE_NAME static field from the class //
|
||||
////////////////////////////////////////////////
|
||||
Field tableNameField = recordEntityClass.getDeclaredField("TABLE_NAME");
|
||||
Field tableNameField = aClass.getDeclaredField("TABLE_NAME");
|
||||
if(!tableNameField.getType().equals(String.class))
|
||||
{
|
||||
LOG.warn(warningPrefix + ", but whose TABLE_NAME field is not a String, so it will not be used.", logPair("class", recordEntityClass.getSimpleName()));
|
||||
LOG.warn(warningPrefix + ", but whose TABLE_NAME field is not a String, so it will not be used.", logPair("class", aClass.getSimpleName()));
|
||||
return (rs);
|
||||
}
|
||||
|
||||
@ -305,7 +293,6 @@ public class MetaDataProducerHelper
|
||||
}
|
||||
|
||||
RecordEntityToTableGenericMetaDataProducer producer = new RecordEntityToTableGenericMetaDataProducer(tableNameValue, recordEntityClass, tableMetaDataProductionCustomizer);
|
||||
producer.setSourceClass(recordEntityClass);
|
||||
|
||||
if(tableMetaDataCustomizer != null)
|
||||
{
|
||||
@ -325,9 +312,7 @@ public class MetaDataProducerHelper
|
||||
////////////////////////////////////////
|
||||
if(qMetaDataProducingEntity.producePossibleValueSource())
|
||||
{
|
||||
PossibleValueSourceOfTableGenericMetaDataProducer producer = new PossibleValueSourceOfTableGenericMetaDataProducer(tableNameValue);
|
||||
producer.setSourceClass(recordEntityClass);
|
||||
rs.add(producer);
|
||||
rs.add(new PossibleValueSourceOfTableGenericMetaDataProducer(tableNameValue));
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
@ -338,11 +323,11 @@ public class MetaDataProducerHelper
|
||||
Class<? extends QRecordEntity> childEntityClass = childTable.childTableEntityClass();
|
||||
if(childTable.childJoin().enabled())
|
||||
{
|
||||
CollectionUtils.addIfNotNull(rs, processChildJoin(recordEntityClass, childTable));
|
||||
CollectionUtils.addIfNotNull(rs, processChildJoin(aClass, childTable));
|
||||
|
||||
if(childTable.childRecordListWidget().enabled())
|
||||
{
|
||||
CollectionUtils.addIfNotNull(rs, processChildRecordListWidget(recordEntityClass, childTable));
|
||||
CollectionUtils.addIfNotNull(rs, processChildRecordListWidget(aClass, childTable));
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -352,7 +337,7 @@ public class MetaDataProducerHelper
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// if not doing the join, can't do the child-widget, so warn about that //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
LOG.warn(warningPrefix + " requested to produce a ChildRecordListWidget, but not produce a Join - which is not allowed (must do join to do widget). ", logPair("class", recordEntityClass.getSimpleName()), logPair("childEntityClass", childEntityClass.getSimpleName()));
|
||||
LOG.warn(warningPrefix + " requested to produce a ChildRecordListWidget, but not produce a Join - which is not allowed (must do join to do widget). ", logPair("class", aClass.getSimpleName()), logPair("childEntityClass", childEntityClass.getSimpleName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -365,16 +350,14 @@ public class MetaDataProducerHelper
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private static MetaDataProducerInterface<?> processChildRecordListWidget(Class<? extends QRecordEntity> sourceClass, ChildTable childTable) throws Exception
|
||||
private static MetaDataProducerInterface<?> processChildRecordListWidget(Class<?> aClass, ChildTable childTable) throws Exception
|
||||
{
|
||||
Class<? extends QRecordEntity> childEntityClass = childTable.childTableEntityClass();
|
||||
String parentTableName = getTableNameStaticFieldValue(sourceClass);
|
||||
String parentTableName = getTableNameStaticFieldValue(aClass);
|
||||
String childTableName = getTableNameStaticFieldValue(childEntityClass);
|
||||
|
||||
ChildRecordListWidget childRecordListWidget = childTable.childRecordListWidget();
|
||||
ChildRecordListWidgetFromRecordEntityGenericMetaDataProducer producer = new ChildRecordListWidgetFromRecordEntityGenericMetaDataProducer(childTableName, parentTableName, childRecordListWidget);
|
||||
producer.setSourceClass(sourceClass);
|
||||
return producer;
|
||||
return (new ChildRecordListWidgetFromRecordEntityGenericMetaDataProducer(childTableName, parentTableName, childRecordListWidget));
|
||||
}
|
||||
|
||||
|
||||
@ -404,22 +387,20 @@ public class MetaDataProducerHelper
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private static MetaDataProducerInterface<?> processChildJoin(Class<? extends QRecordEntity> entityClass, ChildTable childTable) throws Exception
|
||||
private static MetaDataProducerInterface<?> processChildJoin(Class<?> aClass, ChildTable childTable) throws Exception
|
||||
{
|
||||
Class<? extends QRecordEntity> childEntityClass = childTable.childTableEntityClass();
|
||||
|
||||
String parentTableName = getTableNameStaticFieldValue(entityClass);
|
||||
String parentTableName = getTableNameStaticFieldValue(aClass);
|
||||
String childTableName = getTableNameStaticFieldValue(childEntityClass);
|
||||
String possibleValueFieldName = findPossibleValueField(childEntityClass, parentTableName);
|
||||
if(!StringUtils.hasContent(possibleValueFieldName))
|
||||
{
|
||||
LOG.warn("Could not find field in [" + childEntityClass.getSimpleName() + "] with possibleValueSource referencing table [" + entityClass.getSimpleName() + "]");
|
||||
LOG.warn("Could not find field in [" + childEntityClass.getSimpleName() + "] with possibleValueSource referencing table [" + aClass.getSimpleName() + "]");
|
||||
return (null);
|
||||
}
|
||||
|
||||
ChildJoinFromRecordEntityGenericMetaDataProducer producer = new ChildJoinFromRecordEntityGenericMetaDataProducer(childTableName, parentTableName, possibleValueFieldName, childTable.childJoin().orderBy());
|
||||
producer.setSourceClass(entityClass);
|
||||
return producer;
|
||||
return (new ChildJoinFromRecordEntityGenericMetaDataProducer(childTableName, parentTableName, possibleValueFieldName));
|
||||
}
|
||||
|
||||
|
||||
@ -427,20 +408,18 @@ public class MetaDataProducerHelper
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private static MetaDataProducerInterface<?> processMetaDataProducer(Class<?> sourceCClass) throws Exception
|
||||
private static MetaDataProducerInterface<?> processMetaDataProducer(Class<?> aClass) throws Exception
|
||||
{
|
||||
for(Constructor<?> constructor : sourceCClass.getConstructors())
|
||||
for(Constructor<?> constructor : aClass.getConstructors())
|
||||
{
|
||||
if(constructor.getParameterCount() == 0)
|
||||
{
|
||||
Object o = constructor.newInstance();
|
||||
MetaDataProducerInterface<?> producer = (MetaDataProducerInterface<?>) o;
|
||||
producer.setSourceClass(sourceCClass);
|
||||
return producer;
|
||||
return (MetaDataProducerInterface<?>) o;
|
||||
}
|
||||
}
|
||||
|
||||
LOG.warn("Found a class which implements MetaDataProducerInterface, but it does not have a no-arg constructor, so it cannot be used.", logPair("class", sourceCClass.getSimpleName()));
|
||||
LOG.warn("Found a class which implements MetaDataProducerInterface, but it does not have a no-arg constructor, so it cannot be used.", logPair("class", aClass.getSimpleName()));
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -73,23 +73,4 @@ public interface MetaDataProducerInterface<T extends MetaDataProducerOutput>
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
***************************************************************************/
|
||||
default void setSourceClass(Class<?> sourceClass)
|
||||
{
|
||||
//////////
|
||||
// noop //
|
||||
//////////
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
default Class<?> getSourceClass()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ package com.kingsrook.qqq.backend.core.model.metadata;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.qbits.SourceQBitAware;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
|
||||
|
||||
@ -32,12 +31,10 @@ import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
** Output object for a MetaDataProducer, which contains multiple meta-data
|
||||
** objects.
|
||||
*******************************************************************************/
|
||||
public class MetaDataProducerMultiOutput implements MetaDataProducerOutput, SourceQBitAware
|
||||
public class MetaDataProducerMultiOutput implements MetaDataProducerOutput
|
||||
{
|
||||
private List<MetaDataProducerOutput> contents;
|
||||
|
||||
private String sourceQBitName;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -101,48 +98,4 @@ public class MetaDataProducerMultiOutput implements MetaDataProducerOutput, Sour
|
||||
return (rs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public String getSourceQBitName()
|
||||
{
|
||||
return (this.sourceQBitName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void setSourceQBitName(String sourceQBitName)
|
||||
{
|
||||
this.sourceQBitName = sourceQBitName;
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// propagate the name down to the children //
|
||||
/////////////////////////////////////////////
|
||||
for(MetaDataProducerOutput content : contents)
|
||||
{
|
||||
if(content instanceof SourceQBitAware aware)
|
||||
{
|
||||
aware.setSourceQBitName(sourceQBitName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public MetaDataProducerMultiOutput withSourceQBitName(String sourceQBitName)
|
||||
{
|
||||
setSourceQBitName(sourceQBitName);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRule
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.qbits.QBitMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.queues.QQueueMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.queues.QQueueProviderMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||
@ -90,7 +89,6 @@ public class QInstance
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Important to use LinkedHashmap here, to preserve the order in which entries are added. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////
|
||||
private Map<String, QBitMetaData> qBits = new LinkedHashMap<>();
|
||||
private Map<String, QTableMetaData> tables = new LinkedHashMap<>();
|
||||
private Map<String, QJoinMetaData> joins = new LinkedHashMap<>();
|
||||
private Map<String, QPossibleValueSource> possibleValueSources = new LinkedHashMap<>();
|
||||
@ -116,11 +114,8 @@ public class QInstance
|
||||
private QPermissionRules defaultPermissionRules = QPermissionRules.defaultInstance();
|
||||
private QAuditRules defaultAuditRules = QAuditRules.defaultInstanceLevelNone();
|
||||
|
||||
@Deprecated(since = "migrated to metaDataCustomizer")
|
||||
private QCodeReference metaDataFilter = null;
|
||||
|
||||
private QCodeReference metaDataActionCustomizer = null;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// todo - lock down the object (no more changes allowed) after it's been validated? //
|
||||
// if doing so, may need to copy all of the collections into read-only versions... //
|
||||
@ -1494,11 +1489,9 @@ public class QInstance
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for metaDataFilter
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "migrated to metaDataCustomizer")
|
||||
public QCodeReference getMetaDataFilter()
|
||||
{
|
||||
return (this.metaDataFilter);
|
||||
@ -1509,7 +1502,6 @@ public class QInstance
|
||||
/*******************************************************************************
|
||||
** Setter for metaDataFilter
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "migrated to metaDataCustomizer")
|
||||
public void setMetaDataFilter(QCodeReference metaDataFilter)
|
||||
{
|
||||
this.metaDataFilter = metaDataFilter;
|
||||
@ -1520,7 +1512,6 @@ public class QInstance
|
||||
/*******************************************************************************
|
||||
** Fluent setter for metaDataFilter
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "migrated to metaDataCustomizer")
|
||||
public QInstance withMetaDataFilter(QCodeReference metaDataFilter)
|
||||
{
|
||||
this.metaDataFilter = metaDataFilter;
|
||||
@ -1528,99 +1519,4 @@ public class QInstance
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addQBit(QBitMetaData qBitMetaData)
|
||||
{
|
||||
List<String> missingParts = new ArrayList<>();
|
||||
if(!StringUtils.hasContent(qBitMetaData.getGroupId()))
|
||||
{
|
||||
missingParts.add("groupId");
|
||||
}
|
||||
if(!StringUtils.hasContent(qBitMetaData.getArtifactId()))
|
||||
{
|
||||
missingParts.add("artifactId");
|
||||
}
|
||||
if(!StringUtils.hasContent(qBitMetaData.getVersion()))
|
||||
{
|
||||
missingParts.add("version");
|
||||
|
||||
}
|
||||
if(!missingParts.isEmpty())
|
||||
{
|
||||
throw (new IllegalArgumentException("Attempted to add a qBit without a " + StringUtils.joinWithCommasAndAnd(missingParts)));
|
||||
}
|
||||
|
||||
String name = qBitMetaData.getName();
|
||||
if(this.qBits.containsKey(name))
|
||||
{
|
||||
throw (new IllegalArgumentException("Attempted to add a second qBit with name (formed from 'groupId:artifactId:version[:namespace]'): " + name));
|
||||
}
|
||||
this.qBits.put(name, qBitMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for qBits
|
||||
*******************************************************************************/
|
||||
public Map<String, QBitMetaData> getQBits()
|
||||
{
|
||||
return (this.qBits);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for qBits
|
||||
*******************************************************************************/
|
||||
public void setQBits(Map<String, QBitMetaData> qBits)
|
||||
{
|
||||
this.qBits = qBits;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for qBits
|
||||
*******************************************************************************/
|
||||
public QInstance withQBits(Map<String, QBitMetaData> qBits)
|
||||
{
|
||||
this.qBits = qBits;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for metaDataActionCustomizer
|
||||
*******************************************************************************/
|
||||
public QCodeReference getMetaDataActionCustomizer()
|
||||
{
|
||||
return (this.metaDataActionCustomizer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for metaDataActionCustomizer
|
||||
*******************************************************************************/
|
||||
public void setMetaDataActionCustomizer(QCodeReference metaDataActionCustomizer)
|
||||
{
|
||||
this.metaDataActionCustomizer = metaDataActionCustomizer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for metaDataActionCustomizer
|
||||
*******************************************************************************/
|
||||
public QInstance withMetaDataActionCustomizer(QCodeReference metaDataActionCustomizer)
|
||||
{
|
||||
this.metaDataActionCustomizer = metaDataActionCustomizer;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,269 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.model.metadata.branding;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** element of BrandingMetaData - content to send to a frontend for showing a
|
||||
** user across the whole UI - e.g., what environment you're in, or a message
|
||||
** about your account - site announcements, etc.
|
||||
*******************************************************************************/
|
||||
public class Banner implements Serializable, Cloneable
|
||||
{
|
||||
private Severity severity;
|
||||
private String textColor;
|
||||
private String backgroundColor;
|
||||
private String messageText;
|
||||
private String messageHTML;
|
||||
|
||||
private Map<String, Serializable> additionalStyles;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public enum Severity
|
||||
{
|
||||
INFO, WARNING, ERROR, SUCCESS
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public Banner clone()
|
||||
{
|
||||
try
|
||||
{
|
||||
Banner clone = (Banner) super.clone();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// copy mutable state here, so the clone can't change the internals of the original //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
if(additionalStyles != null)
|
||||
{
|
||||
clone.setAdditionalStyles(new LinkedHashMap<>(additionalStyles));
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
catch(CloneNotSupportedException e)
|
||||
{
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for textColor
|
||||
*******************************************************************************/
|
||||
public String getTextColor()
|
||||
{
|
||||
return (this.textColor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for textColor
|
||||
*******************************************************************************/
|
||||
public void setTextColor(String textColor)
|
||||
{
|
||||
this.textColor = textColor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for textColor
|
||||
*******************************************************************************/
|
||||
public Banner withTextColor(String textColor)
|
||||
{
|
||||
this.textColor = textColor;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for backgroundColor
|
||||
*******************************************************************************/
|
||||
public String getBackgroundColor()
|
||||
{
|
||||
return (this.backgroundColor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for backgroundColor
|
||||
*******************************************************************************/
|
||||
public void setBackgroundColor(String backgroundColor)
|
||||
{
|
||||
this.backgroundColor = backgroundColor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for backgroundColor
|
||||
*******************************************************************************/
|
||||
public Banner withBackgroundColor(String backgroundColor)
|
||||
{
|
||||
this.backgroundColor = backgroundColor;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for additionalStyles
|
||||
*******************************************************************************/
|
||||
public Map<String, Serializable> getAdditionalStyles()
|
||||
{
|
||||
return (this.additionalStyles);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for additionalStyles
|
||||
*******************************************************************************/
|
||||
public void setAdditionalStyles(Map<String, Serializable> additionalStyles)
|
||||
{
|
||||
this.additionalStyles = additionalStyles;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for additionalStyles
|
||||
*******************************************************************************/
|
||||
public Banner withAdditionalStyles(Map<String, Serializable> additionalStyles)
|
||||
{
|
||||
this.additionalStyles = additionalStyles;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for messageText
|
||||
*******************************************************************************/
|
||||
public String getMessageText()
|
||||
{
|
||||
return (this.messageText);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for messageText
|
||||
*******************************************************************************/
|
||||
public void setMessageText(String messageText)
|
||||
{
|
||||
this.messageText = messageText;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for messageText
|
||||
*******************************************************************************/
|
||||
public Banner withMessageText(String messageText)
|
||||
{
|
||||
this.messageText = messageText;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for messageHTML
|
||||
*******************************************************************************/
|
||||
public String getMessageHTML()
|
||||
{
|
||||
return (this.messageHTML);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for messageHTML
|
||||
*******************************************************************************/
|
||||
public void setMessageHTML(String messageHTML)
|
||||
{
|
||||
this.messageHTML = messageHTML;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for messageHTML
|
||||
*******************************************************************************/
|
||||
public Banner withMessageHTML(String messageHTML)
|
||||
{
|
||||
this.messageHTML = messageHTML;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for severity
|
||||
*******************************************************************************/
|
||||
public Severity getSeverity()
|
||||
{
|
||||
return (this.severity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for severity
|
||||
*******************************************************************************/
|
||||
public void setSeverity(Severity severity)
|
||||
{
|
||||
this.severity = severity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for severity
|
||||
*******************************************************************************/
|
||||
public Banner withSeverity(Severity severity)
|
||||
{
|
||||
this.severity = severity;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.model.metadata.branding;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** interface to define keys for where banners should be displayed.
|
||||
** expect frontends to implement this interface with enums of known possible values
|
||||
*******************************************************************************/
|
||||
public interface BannerSlot
|
||||
{
|
||||
}
|
@ -22,9 +22,6 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.branding;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.TopLevelMetaDataInterface;
|
||||
|
||||
@ -33,7 +30,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.TopLevelMetaDataInterface;
|
||||
** Meta-Data to define branding in a QQQ instance.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class QBrandingMetaData implements TopLevelMetaDataInterface, Cloneable, Serializable
|
||||
public class QBrandingMetaData implements TopLevelMetaDataInterface
|
||||
{
|
||||
private String companyName;
|
||||
private String companyUrl;
|
||||
@ -42,45 +39,9 @@ public class QBrandingMetaData implements TopLevelMetaDataInterface, Cloneable,
|
||||
private String icon;
|
||||
private String accentColor;
|
||||
|
||||
@Deprecated(since = "migrate to use banners map instead")
|
||||
private String environmentBannerText;
|
||||
|
||||
@Deprecated(since = "migrate to use banners map instead")
|
||||
private String environmentBannerColor;
|
||||
|
||||
private Map<BannerSlot, Banner> banners;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public QBrandingMetaData clone()
|
||||
{
|
||||
try
|
||||
{
|
||||
QBrandingMetaData clone = (QBrandingMetaData) super.clone();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// copy mutable state here, so the clone can't change the internals of the original //
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
if(banners != null)
|
||||
{
|
||||
clone.banners = new LinkedHashMap<>();
|
||||
for(Map.Entry<BannerSlot, Banner> entry : this.banners.entrySet())
|
||||
{
|
||||
clone.banners.put(entry.getKey(), entry.getValue().clone());
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
catch(CloneNotSupportedException e)
|
||||
{
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -306,7 +267,6 @@ public class QBrandingMetaData implements TopLevelMetaDataInterface, Cloneable,
|
||||
/*******************************************************************************
|
||||
** Getter for environmentBannerText
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "migrate to use banners map instead")
|
||||
public String getEnvironmentBannerText()
|
||||
{
|
||||
return (this.environmentBannerText);
|
||||
@ -317,7 +277,6 @@ public class QBrandingMetaData implements TopLevelMetaDataInterface, Cloneable,
|
||||
/*******************************************************************************
|
||||
** Setter for environmentBannerText
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "migrate to use banners map instead")
|
||||
public void setEnvironmentBannerText(String environmentBannerText)
|
||||
{
|
||||
this.environmentBannerText = environmentBannerText;
|
||||
@ -328,7 +287,6 @@ public class QBrandingMetaData implements TopLevelMetaDataInterface, Cloneable,
|
||||
/*******************************************************************************
|
||||
** Fluent setter for environmentBannerText
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "migrate to use banners map instead")
|
||||
public QBrandingMetaData withEnvironmentBannerText(String environmentBannerText)
|
||||
{
|
||||
this.environmentBannerText = environmentBannerText;
|
||||
@ -340,7 +298,6 @@ public class QBrandingMetaData implements TopLevelMetaDataInterface, Cloneable,
|
||||
/*******************************************************************************
|
||||
** Getter for environmentBannerColor
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "migrate to use banners map instead")
|
||||
public String getEnvironmentBannerColor()
|
||||
{
|
||||
return (this.environmentBannerColor);
|
||||
@ -351,7 +308,6 @@ public class QBrandingMetaData implements TopLevelMetaDataInterface, Cloneable,
|
||||
/*******************************************************************************
|
||||
** Setter for environmentBannerColor
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "migrate to use banners map instead")
|
||||
public void setEnvironmentBannerColor(String environmentBannerColor)
|
||||
{
|
||||
this.environmentBannerColor = environmentBannerColor;
|
||||
@ -362,7 +318,6 @@ public class QBrandingMetaData implements TopLevelMetaDataInterface, Cloneable,
|
||||
/*******************************************************************************
|
||||
** Fluent setter for environmentBannerColor
|
||||
*******************************************************************************/
|
||||
@Deprecated(since = "migrate to use banners map instead")
|
||||
public QBrandingMetaData withEnvironmentBannerColor(String environmentBannerColor)
|
||||
{
|
||||
this.environmentBannerColor = environmentBannerColor;
|
||||
@ -379,52 +334,4 @@ public class QBrandingMetaData implements TopLevelMetaDataInterface, Cloneable,
|
||||
{
|
||||
qInstance.setBranding(this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for banners
|
||||
*******************************************************************************/
|
||||
public Map<BannerSlot, Banner> getBanners()
|
||||
{
|
||||
return (this.banners);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for banners
|
||||
*******************************************************************************/
|
||||
public void setBanners(Map<BannerSlot, Banner> banners)
|
||||
{
|
||||
this.banners = banners;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for banners
|
||||
*******************************************************************************/
|
||||
public QBrandingMetaData withBanners(Map<BannerSlot, Banner> banners)
|
||||
{
|
||||
this.banners = banners;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public QBrandingMetaData withBanner(BannerSlot slot, Banner banner)
|
||||
{
|
||||
if(this.banners == null)
|
||||
{
|
||||
this.banners = new LinkedHashMap<>();
|
||||
}
|
||||
this.banners.put(slot, banner);
|
||||
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.model.metadata.code;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** an object which is intended to be constructed via a CodeReference, and,
|
||||
** moreso, after it is created, then the initialize method here gets called,
|
||||
** passing the codeRefernce in - e.g., to do additional initalization of the
|
||||
** object, e.g., properties in a QCodeReferenceWithProperties
|
||||
*******************************************************************************/
|
||||
public interface InitializableViaCodeReference
|
||||
{
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
void initialize(QCodeReference codeReference);
|
||||
|
||||
}
|
@ -29,7 +29,7 @@ import java.io.Serializable;
|
||||
** Pointer to code to be ran by the qqq framework, e.g., for custom behavior -
|
||||
** maybe process steps, maybe customization to a table, etc.
|
||||
*******************************************************************************/
|
||||
public class QCodeReference implements Serializable, Cloneable
|
||||
public class QCodeReference implements Serializable
|
||||
{
|
||||
private String name;
|
||||
private QCodeType codeType;
|
||||
@ -58,25 +58,6 @@ public class QCodeReference implements Serializable, Cloneable
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public QCodeReference clone()
|
||||
{
|
||||
try
|
||||
{
|
||||
QCodeReference clone = (QCodeReference) super.clone();
|
||||
return clone;
|
||||
}
|
||||
catch(CloneNotSupportedException e)
|
||||
{
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -198,4 +179,5 @@ public class QCodeReference implements Serializable, Cloneable
|
||||
this.inlineCode = inlineCode;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,59 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.model.metadata.code;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** a code reference that also has a map of properties. This object (with the
|
||||
** properties) will be passed in to the referenced object, if it implements
|
||||
** InitializableViaCodeReference.
|
||||
*******************************************************************************/
|
||||
public class QCodeReferenceWithProperties extends QCodeReference
|
||||
{
|
||||
private final Map<String, Serializable> properties;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public QCodeReferenceWithProperties(Class<?> javaClass, Map<String, Serializable> properties)
|
||||
{
|
||||
super(javaClass);
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for properties
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, Serializable> getProperties()
|
||||
{
|
||||
return properties;
|
||||
}
|
||||
}
|
@ -62,8 +62,7 @@ public class WidgetAdHocValue extends AbstractWidgetValueSource
|
||||
context.putAll(inputValues);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Function<Object, Object> function = QCodeLoader.getAdHoc(Function.class, codeReference);
|
||||
Function<Object, Object> function = QCodeLoader.getFunction(codeReference);
|
||||
Object result = function.apply(context);
|
||||
return (result);
|
||||
}
|
||||
|
@ -23,12 +23,8 @@ package com.kingsrook.qqq.backend.core.model.metadata.fields;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Objects;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PossibleValueEnum;
|
||||
import com.kingsrook.qqq.backend.core.utils.Pair;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||
|
||||
|
||||
@ -46,21 +42,20 @@ public enum AdornmentType
|
||||
REVEAL,
|
||||
FILE_DOWNLOAD,
|
||||
FILE_UPLOAD,
|
||||
TOOLTIP,
|
||||
ERROR;
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// keep these values in sync with AdornmentType.ts in qqq-frontend-core //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface LinkValues
|
||||
{
|
||||
String TARGET = "target";
|
||||
String TO_RECORD_FROM_TABLE = "toRecordFromTable";
|
||||
String TO_RECORD_FROM_TABLE_DYNAMIC = "toRecordFromTableDynamic";
|
||||
String TARGET = "target";
|
||||
String TO_RECORD_FROM_TABLE = "toRecordFromTable";
|
||||
}
|
||||
|
||||
|
||||
@ -77,8 +72,6 @@ public enum AdornmentType
|
||||
String SUPPLEMENTAL_PROCESS_NAME = "supplementalProcessName";
|
||||
String SUPPLEMENTAL_CODE_REFERENCE = "supplementalCodeReference";
|
||||
|
||||
String DOWNLOAD_URL_DYNAMIC = "downloadUrlDynamic";
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// use these two together, as in: //
|
||||
// FILE_NAME_FORMAT = "Order %s Packing Slip.pdf" //
|
||||
@ -86,17 +79,6 @@ public enum AdornmentType
|
||||
////////////////////////////////////////////////////
|
||||
String FILE_NAME_FORMAT = "fileNameFormat";
|
||||
String FILE_NAME_FORMAT_FIELDS = "fileNameFormatFields";
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
static String makeFieldDownloadUrl(String tableName, Serializable primaryKey, String fieldName, String fileName)
|
||||
{
|
||||
return ("/data/" + tableName + "/"
|
||||
+ URLEncoder.encode(Objects.requireNonNullElse(ValueUtils.getValueAsString(primaryKey), ""), StandardCharsets.UTF_8).replace("+", "%20") + "/"
|
||||
+ fieldName + "/"
|
||||
+ URLEncoder.encode(Objects.requireNonNullElse(fileName, ""), StandardCharsets.UTF_8).replace("+", "%20"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -247,15 +229,4 @@ public enum AdornmentType
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface TooltipValues
|
||||
{
|
||||
String STATIC_TEXT = "staticText";
|
||||
String TOOLTIP_DYNAMIC = "tooltipDynamic";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -238,7 +238,7 @@ public class QFieldMetaData implements Cloneable
|
||||
|
||||
if(StringUtils.hasContent(fieldAnnotation.defaultValue()))
|
||||
{
|
||||
withDefaultValue(ValueUtils.getValueAsFieldType(this.type, fieldAnnotation.defaultValue()));
|
||||
ValueUtils.getValueAsFieldType(this.type, fieldAnnotation.defaultValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,6 @@ public class QFrontendFieldMetaData implements Serializable
|
||||
private String possibleValueSourceName;
|
||||
private String displayFormat;
|
||||
private Serializable defaultValue;
|
||||
private Integer maxLength;
|
||||
|
||||
private List<FieldAdornment> adornments;
|
||||
private List<QHelpContent> helpContents;
|
||||
@ -86,7 +85,6 @@ public class QFrontendFieldMetaData implements Serializable
|
||||
this.defaultValue = fieldMetaData.getDefaultValue();
|
||||
this.helpContents = fieldMetaData.getHelpContents();
|
||||
this.inlinePossibleValueSource = fieldMetaData.getInlinePossibleValueSource();
|
||||
this.maxLength = fieldMetaData.getMaxLength();
|
||||
|
||||
for(FieldBehavior<?> behavior : CollectionUtils.nonNullCollection(fieldMetaData.getBehaviors()))
|
||||
{
|
||||
|
@ -48,8 +48,6 @@ public class QFrontendProcessMetaData
|
||||
private String label;
|
||||
private String tableName;
|
||||
private boolean isHidden;
|
||||
private Integer minInputRecords;
|
||||
private Integer maxInputRecords;
|
||||
|
||||
private QIcon icon;
|
||||
|
||||
@ -74,8 +72,6 @@ public class QFrontendProcessMetaData
|
||||
this.tableName = processMetaData.getTableName();
|
||||
this.isHidden = processMetaData.getIsHidden();
|
||||
this.stepFlow = processMetaData.getStepFlow().toString();
|
||||
this.minInputRecords = processMetaData.getMinInputRecords();
|
||||
this.maxInputRecords = processMetaData.getMaxInputRecords();
|
||||
|
||||
if(includeSteps)
|
||||
{
|
||||
@ -217,27 +213,4 @@ public class QFrontendProcessMetaData
|
||||
{
|
||||
return icon;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for minInputRecords
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getMinInputRecords()
|
||||
{
|
||||
return minInputRecords;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for maxInputRecords
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getMaxInputRecords()
|
||||
{
|
||||
return maxInputRecords;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.layout;
|
||||
** Future may allow something like a "namespace", and/or multiple icons for
|
||||
** use in different frontends, etc.
|
||||
*******************************************************************************/
|
||||
public class QIcon implements Cloneable
|
||||
public class QIcon
|
||||
{
|
||||
private String name;
|
||||
private String path;
|
||||
@ -58,25 +58,6 @@ public class QIcon implements Cloneable
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public QIcon clone()
|
||||
{
|
||||
try
|
||||
{
|
||||
QIcon clone = (QIcon) super.clone();
|
||||
return clone;
|
||||
}
|
||||
catch(CloneNotSupportedException e)
|
||||
{
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
**
|
||||
@ -173,4 +154,6 @@ public class QIcon implements Cloneable
|
||||
this.color = color;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -216,16 +216,11 @@ public class SendSESAction
|
||||
{
|
||||
LOG.warn("More than one FROM value was found, will send using the first one found [" + partyList.get(0).getAddress() + "].");
|
||||
}
|
||||
Party fromParty = partyList.get(0);
|
||||
if(fromParty.getAddress() == null)
|
||||
{
|
||||
throw (new QException("Cannot send SES message because a FROM address was not provided."));
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// return the from address //
|
||||
/////////////////////////////
|
||||
return (getFullEmailAddress(fromParty));
|
||||
return (partyList.get(0).getAddress());
|
||||
}
|
||||
|
||||
|
||||
@ -272,15 +267,15 @@ public class SendSESAction
|
||||
{
|
||||
if(EmailPartyRole.CC.equals(party.getRole()))
|
||||
{
|
||||
ccList.add(getFullEmailAddress(party));
|
||||
ccList.add(party.getAddress());
|
||||
}
|
||||
else if(EmailPartyRole.BCC.equals(party.getRole()))
|
||||
{
|
||||
bccList.add(getFullEmailAddress(party));
|
||||
bccList.add(party.getAddress());
|
||||
}
|
||||
else if(party.getRole() == null || PartyRole.Default.DEFAULT.equals(party.getRole()) || EmailPartyRole.TO.equals(party.getRole()))
|
||||
{
|
||||
toList.add(getFullEmailAddress(party));
|
||||
toList.add(party.getAddress());
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -337,22 +332,4 @@ public class SendSESAction
|
||||
|
||||
return amazonSES;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String getFullEmailAddress(Party party)
|
||||
{
|
||||
if(party.getLabel() != null)
|
||||
{
|
||||
return (party.getLabel() + " <" + party.getAddress() + ">");
|
||||
}
|
||||
|
||||
/////////////////////////////
|
||||
// return the from address //
|
||||
/////////////////////////////
|
||||
return (party.getAddress());
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.qbits.SourceQBitAware;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.scheduleing.QScheduleMetaData;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.basepull.BasepullConfiguration;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
@ -47,14 +46,11 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
** Meta-Data to define a process in a QQQ instance.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissionRules, TopLevelMetaDataInterface, SourceQBitAware
|
||||
public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissionRules, TopLevelMetaDataInterface
|
||||
{
|
||||
private String name;
|
||||
private String label;
|
||||
private String tableName;
|
||||
|
||||
private String sourceQBitName;
|
||||
|
||||
private String name;
|
||||
private String label;
|
||||
private String tableName;
|
||||
private boolean isHidden = false;
|
||||
private BasepullConfiguration basepullConfiguration;
|
||||
private QPermissionRules permissionRules;
|
||||
@ -874,7 +870,6 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for processTracerCodeReference
|
||||
*******************************************************************************/
|
||||
@ -905,37 +900,4 @@ public class QProcessMetaData implements QAppChildMetaData, MetaDataWithPermissi
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sourceQBitName
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getSourceQBitName()
|
||||
{
|
||||
return (this.sourceQBitName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sourceQBitName
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void setSourceQBitName(String sourceQBitName)
|
||||
{
|
||||
this.sourceQBitName = sourceQBitName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sourceQBitName
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public QProcessMetaData withSourceQBitName(String sourceQBitName)
|
||||
{
|
||||
this.sourceQBitName = sourceQBitName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -24,13 +24,11 @@ package com.kingsrook.qqq.backend.core.model.metadata.producers;
|
||||
|
||||
import java.util.Objects;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.producers.annotations.ChildJoin;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
|
||||
|
||||
@ -41,11 +39,12 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
**
|
||||
** e.g., Orders & LineItems - on the Order entity
|
||||
** <code>
|
||||
@QMetaDataProducingEntity( childTables = { @ChildTable(
|
||||
childTableEntityClass = LineItem.class,
|
||||
childJoin = @ChildJoin(enabled = true),
|
||||
childRecordListWidget = @ChildRecordListWidget(enabled = true, label = "Order Lines"))
|
||||
}
|
||||
@QMetaDataProducingEntity(
|
||||
childTables = { @ChildTable(
|
||||
childTableEntityClass = LineItem.class,
|
||||
childJoin = @ChildJoin(enabled = true),
|
||||
childRecordListWidget = @ChildRecordListWidget(enabled = true, label = "Order Lines"))
|
||||
}
|
||||
)
|
||||
public class Order extends QRecordEntity
|
||||
** </code>
|
||||
@ -63,16 +62,12 @@ public class ChildJoinFromRecordEntityGenericMetaDataProducer implements MetaDat
|
||||
private String parentTableName; // e.g., order
|
||||
private String foreignKeyFieldName; // e.g., orderId
|
||||
|
||||
private ChildJoin.OrderBy[] orderBys;
|
||||
|
||||
private Class<?> sourceClass;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public ChildJoinFromRecordEntityGenericMetaDataProducer(String childTableName, String parentTableName, String foreignKeyFieldName, ChildJoin.OrderBy[] orderBys)
|
||||
public ChildJoinFromRecordEntityGenericMetaDataProducer(String childTableName, String parentTableName, String foreignKeyFieldName)
|
||||
{
|
||||
Objects.requireNonNull(childTableName, "childTableName cannot be null");
|
||||
Objects.requireNonNull(parentTableName, "parentTableName cannot be null");
|
||||
@ -81,7 +76,6 @@ public class ChildJoinFromRecordEntityGenericMetaDataProducer implements MetaDat
|
||||
this.childTableName = childTableName;
|
||||
this.parentTableName = parentTableName;
|
||||
this.foreignKeyFieldName = foreignKeyFieldName;
|
||||
this.orderBys = orderBys;
|
||||
}
|
||||
|
||||
|
||||
@ -92,75 +86,20 @@ public class ChildJoinFromRecordEntityGenericMetaDataProducer implements MetaDat
|
||||
@Override
|
||||
public QJoinMetaData produce(QInstance qInstance) throws QException
|
||||
{
|
||||
QTableMetaData parentTable = qInstance.getTable(parentTableName);
|
||||
if(parentTable == null)
|
||||
QTableMetaData possibleValueTable = qInstance.getTable(parentTableName);
|
||||
if(possibleValueTable == null)
|
||||
{
|
||||
throw (new QException("Could not find tableMetaData " + parentTableName));
|
||||
}
|
||||
|
||||
QTableMetaData childTable = qInstance.getTable(childTableName);
|
||||
if(childTable == null)
|
||||
{
|
||||
throw (new QException("Could not find tableMetaData " + childTable));
|
||||
}
|
||||
|
||||
QJoinMetaData join = new QJoinMetaData()
|
||||
.withLeftTable(parentTableName)
|
||||
.withRightTable(childTableName)
|
||||
.withInferredName()
|
||||
.withType(JoinType.ONE_TO_MANY)
|
||||
.withJoinOn(new JoinOn(parentTable.getPrimaryKeyField(), foreignKeyFieldName));
|
||||
|
||||
if(orderBys != null && orderBys.length > 0)
|
||||
{
|
||||
for(ChildJoin.OrderBy orderBy : orderBys)
|
||||
{
|
||||
join.withOrderBy(new QFilterOrderBy(orderBy.fieldName(), orderBy.isAscending()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//////////////////////////////////////////////////////////
|
||||
// by default, sort by the id of the child table... mmm //
|
||||
//////////////////////////////////////////////////////////
|
||||
join.withOrderBy(new QFilterOrderBy(childTable.getPrimaryKeyField()));
|
||||
}
|
||||
.withJoinOn(new JoinOn(possibleValueTable.getPrimaryKeyField(), foreignKeyFieldName));
|
||||
|
||||
return (join);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Class<?> getSourceClass()
|
||||
{
|
||||
return sourceClass;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setSourceClass(Class<?> sourceClass)
|
||||
{
|
||||
this.sourceClass = sourceClass;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ChildJoinFromRecordEntityGenericMetaDataProducer withSourceClass(Class<?> sourceClass)
|
||||
{
|
||||
this.sourceClass = sourceClass;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -57,8 +57,6 @@ public class ChildRecordListWidgetFromRecordEntityGenericMetaDataProducer implem
|
||||
|
||||
private ChildRecordListWidget childRecordListWidget;
|
||||
|
||||
private Class<?> sourceClass;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
@ -113,36 +111,4 @@ public class ChildRecordListWidgetFromRecordEntityGenericMetaDataProducer implem
|
||||
return (widget);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Class<?> getSourceClass()
|
||||
{
|
||||
return sourceClass;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setSourceClass(Class<?> sourceClass)
|
||||
{
|
||||
this.sourceClass = sourceClass;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ChildRecordListWidgetFromRecordEntityGenericMetaDataProducer withSourceClass(Class<?> sourceClass)
|
||||
{
|
||||
this.sourceClass = sourceClass;
|
||||
return (this);
|
||||
}
|
||||
}
|
||||
|
@ -40,10 +40,6 @@ public class PossibleValueSourceOfEnumGenericMetaDataProducer<T extends Serializ
|
||||
private final String name;
|
||||
private final PossibleValueEnum<T>[] values;
|
||||
|
||||
private Class<?> sourceClass;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -66,37 +62,4 @@ public class PossibleValueSourceOfEnumGenericMetaDataProducer<T extends Serializ
|
||||
{
|
||||
return (QPossibleValueSource.newForEnum(name, values));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Class<?> getSourceClass()
|
||||
{
|
||||
return sourceClass;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setSourceClass(Class<?> sourceClass)
|
||||
{
|
||||
this.sourceClass = sourceClass;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public PossibleValueSourceOfEnumGenericMetaDataProducer<T> withSourceClass(Class<?> sourceClass)
|
||||
{
|
||||
this.sourceClass = sourceClass;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,7 +37,6 @@ public class PossibleValueSourceOfTableGenericMetaDataProducer implements MetaDa
|
||||
{
|
||||
private final String tableName;
|
||||
|
||||
private Class<?> sourceClass;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -59,38 +58,4 @@ public class PossibleValueSourceOfTableGenericMetaDataProducer implements MetaDa
|
||||
{
|
||||
return (QPossibleValueSource.newForTable(tableName));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Class<?> getSourceClass()
|
||||
{
|
||||
return sourceClass;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setSourceClass(Class<?> sourceClass)
|
||||
{
|
||||
this.sourceClass = sourceClass;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public PossibleValueSourceOfTableGenericMetaDataProducer withSourceClass(Class<?> sourceClass)
|
||||
{
|
||||
this.sourceClass = sourceClass;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -48,7 +48,6 @@ public class RecordEntityToTableGenericMetaDataProducer implements MetaDataProdu
|
||||
|
||||
private static MetaDataCustomizerInterface<QTableMetaData> defaultMetaDataCustomizer = null;
|
||||
|
||||
private Class<?> sourceClass;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -155,37 +154,4 @@ public class RecordEntityToTableGenericMetaDataProducer implements MetaDataProdu
|
||||
RecordEntityToTableGenericMetaDataProducer.defaultMetaDataCustomizer = defaultMetaDataCustomizer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Class<?> getSourceClass()
|
||||
{
|
||||
return sourceClass;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setSourceClass(Class<?> sourceClass)
|
||||
{
|
||||
this.sourceClass = sourceClass;
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public RecordEntityToTableGenericMetaDataProducer withSourceClass(Class<?> sourceClass)
|
||||
{
|
||||
this.sourceClass = sourceClass;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,16 +35,4 @@ import java.lang.annotation.RetentionPolicy;
|
||||
public @interface ChildJoin
|
||||
{
|
||||
boolean enabled();
|
||||
|
||||
OrderBy[] orderBy() default { };
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@interface OrderBy
|
||||
{
|
||||
String fieldName();
|
||||
|
||||
boolean isAscending() default true;
|
||||
}
|
||||
}
|
||||
|
@ -1,122 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.model.metadata.qbits;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** Common (maybe)? qbit config pattern, where the qbit may be able to provide
|
||||
** a particular table, or, the application may supply it itself.
|
||||
**
|
||||
** If the qbit provides it, then we need to be told (by the application)
|
||||
** what backendName to use for the table.
|
||||
**
|
||||
** Else if the application supplies it, it needs to tell the qBit what the
|
||||
** tableName is.
|
||||
***************************************************************************/
|
||||
public class ProvidedOrSuppliedTableConfig
|
||||
{
|
||||
private boolean doProvideTable;
|
||||
private String backendName;
|
||||
private String tableName;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public ProvidedOrSuppliedTableConfig(boolean doProvideTable, String backendName, String tableName)
|
||||
{
|
||||
this.doProvideTable = doProvideTable;
|
||||
this.backendName = backendName;
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static ProvidedOrSuppliedTableConfig provideTableUsingBackendNamed(String backendName)
|
||||
{
|
||||
return (new ProvidedOrSuppliedTableConfig(true, backendName, null));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static ProvidedOrSuppliedTableConfig useSuppliedTaleNamed(String tableName)
|
||||
{
|
||||
return (new ProvidedOrSuppliedTableConfig(false, null, tableName));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public String getEffectiveTableName(String tableNameIfProviding)
|
||||
{
|
||||
if (getDoProvideTable())
|
||||
{
|
||||
return tableNameIfProviding;
|
||||
}
|
||||
else
|
||||
{
|
||||
return getTableName();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getTableName()
|
||||
{
|
||||
return tableName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for doProvideTable
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean getDoProvideTable()
|
||||
{
|
||||
return doProvideTable;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for backendName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getBackendName()
|
||||
{
|
||||
return backendName;
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.model.metadata.qbits;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerOutput;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** extension of MetaDataProducerInterface, designed for producing meta data
|
||||
** within a (java-defined, at this time) QBit.
|
||||
**
|
||||
** Specifically exists to accept the QBitConfig as a type parameter and a value,
|
||||
** easily accessed in the producer's methods as getQBitConfig()
|
||||
*******************************************************************************/
|
||||
public abstract class QBitComponentMetaDataProducer<T extends MetaDataProducerOutput, C extends QBitConfig> implements MetaDataProducerInterface<T>
|
||||
{
|
||||
private C qBitConfig = null;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for qBitConfig
|
||||
*******************************************************************************/
|
||||
public C getQBitConfig()
|
||||
{
|
||||
return (this.qBitConfig);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for qBitConfig
|
||||
*******************************************************************************/
|
||||
public void setQBitConfig(C qBitConfig)
|
||||
{
|
||||
this.qBitConfig = qBitConfig;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for qBitConfig
|
||||
*******************************************************************************/
|
||||
public QBitComponentMetaDataProducer<T, C> withQBitConfig(C qBitConfig)
|
||||
{
|
||||
this.qBitConfig = qBitConfig;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.model.metadata.qbits;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.producers.MetaDataCustomizerInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Interface for configuration settings used both in the production of meta-data
|
||||
** for a QBit, but also at runtime, e.g., to be aware of exactly how the qbit
|
||||
** has been incorporated into an application.
|
||||
**
|
||||
** For example:
|
||||
** - should the QBit define certain tables, or will they be supplied by the application?
|
||||
** - what other meta-data names should the qbit reference (backends, schedulers)
|
||||
** - what meta-data-customizer(s) should be used?
|
||||
**
|
||||
** When implementing a QBit, you'll implement this interface - adding whatever
|
||||
** (if any) properties you need, and if you have any rules, then overriding
|
||||
** the validate method (ideally the one that takes the List-of-String errors)
|
||||
**
|
||||
** When using a QBit, you'll create an instance of the QBit's config object,
|
||||
** and pass it through to the QBit producer.
|
||||
*******************************************************************************/
|
||||
public interface QBitConfig extends Serializable
|
||||
{
|
||||
QLogger LOG = QLogger.getLogger(QBitConfig.class);
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
default void validate(QInstance qInstance) throws QBitConfigValidationException
|
||||
{
|
||||
List<String> errors = new ArrayList<>();
|
||||
|
||||
try
|
||||
{
|
||||
validate(qInstance, errors);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error validating QBitConfig: " + this.getClass().getName(), e);
|
||||
}
|
||||
|
||||
if(!errors.isEmpty())
|
||||
{
|
||||
throw (new QBitConfigValidationException(this, errors));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
default void validate(QInstance qInstance, List<String> errors)
|
||||
{
|
||||
/////////////////////////////////////
|
||||
// nothing to validate by default! //
|
||||
/////////////////////////////////////
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
default boolean assertCondition(boolean condition, String message, List<String> errors)
|
||||
{
|
||||
if(!condition)
|
||||
{
|
||||
errors.add(message);
|
||||
}
|
||||
return (condition);
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
default MetaDataCustomizerInterface<QTableMetaData> getTableMetaDataCustomizer()
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.model.metadata.qbits;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** thrown by QBitConfig.validate() if there's an issue.
|
||||
*******************************************************************************/
|
||||
public class QBitConfigValidationException extends QException
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public QBitConfigValidationException(QBitConfig qBitConfig, List<String> errors)
|
||||
{
|
||||
super("Validation failed for QBitConfig: " + qBitConfig.getClass().getName() + ":\n" + StringUtils.join("\n", errors));
|
||||
}
|
||||
|
||||
}
|
@ -1,237 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.model.metadata.qbits;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.TopLevelMetaDataInterface;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Meta-data to define an active QBit in a QQQ Instance.
|
||||
**
|
||||
** The unique "name" for the QBit is composed of its groupId and artifactId
|
||||
** (maven style). There is also a version - but it is not part of the unique
|
||||
** name. But - there is also a namespace attribute, which IS part of the
|
||||
** unique name. This will (eventually?) allow us to have multiple instances
|
||||
** of the same qbit in a qInstance at the same time (e.g., 2 versions of some
|
||||
** table, which should be namespace-prefixed);
|
||||
**
|
||||
** QBitMetaData also retains the QBitConfig that was used to produce the QBit.
|
||||
**
|
||||
** Some meta-data objects are aware of the fact that they may have come from a
|
||||
** QBit - see SourceQBitAware interface. These objects can get their source
|
||||
** QBitMetaData (this object) and its config,via that interface.
|
||||
*******************************************************************************/
|
||||
public class QBitMetaData implements TopLevelMetaDataInterface
|
||||
{
|
||||
private String groupId;
|
||||
private String artifactId;
|
||||
private String version;
|
||||
private String namespace;
|
||||
|
||||
private QBitConfig config;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
String name = groupId + ":" + artifactId;
|
||||
if(StringUtils.hasContent(namespace))
|
||||
{
|
||||
name += ":" + namespace;
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void addSelfToInstance(QInstance qInstance)
|
||||
{
|
||||
qInstance.addQBit(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for config
|
||||
*******************************************************************************/
|
||||
public QBitConfig getConfig()
|
||||
{
|
||||
return (this.config);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for config
|
||||
*******************************************************************************/
|
||||
public void setConfig(QBitConfig config)
|
||||
{
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for config
|
||||
*******************************************************************************/
|
||||
public QBitMetaData withConfig(QBitConfig config)
|
||||
{
|
||||
this.config = config;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for groupId
|
||||
*******************************************************************************/
|
||||
public String getGroupId()
|
||||
{
|
||||
return (this.groupId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for groupId
|
||||
*******************************************************************************/
|
||||
public void setGroupId(String groupId)
|
||||
{
|
||||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for groupId
|
||||
*******************************************************************************/
|
||||
public QBitMetaData withGroupId(String groupId)
|
||||
{
|
||||
this.groupId = groupId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for artifactId
|
||||
*******************************************************************************/
|
||||
public String getArtifactId()
|
||||
{
|
||||
return (this.artifactId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for artifactId
|
||||
*******************************************************************************/
|
||||
public void setArtifactId(String artifactId)
|
||||
{
|
||||
this.artifactId = artifactId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for artifactId
|
||||
*******************************************************************************/
|
||||
public QBitMetaData withArtifactId(String artifactId)
|
||||
{
|
||||
this.artifactId = artifactId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for version
|
||||
*******************************************************************************/
|
||||
public String getVersion()
|
||||
{
|
||||
return (this.version);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for version
|
||||
*******************************************************************************/
|
||||
public void setVersion(String version)
|
||||
{
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for version
|
||||
*******************************************************************************/
|
||||
public QBitMetaData withVersion(String version)
|
||||
{
|
||||
this.version = version;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for namespace
|
||||
*******************************************************************************/
|
||||
public String getNamespace()
|
||||
{
|
||||
return (this.namespace);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for namespace
|
||||
*******************************************************************************/
|
||||
public void setNamespace(String namespace)
|
||||
{
|
||||
this.namespace = namespace;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for namespace
|
||||
*******************************************************************************/
|
||||
public QBitMetaData withNamespace(String namespace)
|
||||
{
|
||||
this.namespace = namespace;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
@ -1,117 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.model.metadata.qbits;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.MetaDataProducerOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** interface for how a QBit's meta-data gets produced and added to a QInstance.
|
||||
**
|
||||
** When implementing a QBit, you'll implement this interface:
|
||||
** - adding a QBitConfig subclass as a property
|
||||
** - overriding the produce(qInstance, namespace) method - where you'll:
|
||||
** -- create and add your QBitMetaData
|
||||
** -- call MetaDataProducerHelper.findProducers
|
||||
** -- hand off to finishProducing() in this interface
|
||||
**
|
||||
** When using a QBit, you'll create an instance of the QBit's config object,
|
||||
** pass it in to the producer, then call produce, ala:
|
||||
**
|
||||
** new SomeQBitProducer()
|
||||
** .withQBitConfig(someQBitConfig)
|
||||
** .produce(qInstance);
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface QBitProducer
|
||||
{
|
||||
QLogger LOG = QLogger.getLogger(QBitProducer.class);
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
default void produce(QInstance qInstance) throws QException
|
||||
{
|
||||
produce(qInstance, null);
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
void produce(QInstance qInstance, String namespace) throws QException;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
*
|
||||
***************************************************************************/
|
||||
default <C extends QBitConfig> void finishProducing(QInstance qInstance, QBitMetaData qBitMetaData, C qBitConfig, List<MetaDataProducerInterface<?>> producers) throws QException
|
||||
{
|
||||
qBitConfig.validate(qInstance);
|
||||
|
||||
///////////////////////////////
|
||||
// todo - move to base class //
|
||||
///////////////////////////////
|
||||
for(MetaDataProducerInterface<?> producer : producers)
|
||||
{
|
||||
if(producer instanceof QBitComponentMetaDataProducer<?, ?>)
|
||||
{
|
||||
QBitComponentMetaDataProducer<?, C> qBitComponentMetaDataProducer = (QBitComponentMetaDataProducer<?, C>) producer;
|
||||
qBitComponentMetaDataProducer.setQBitConfig(qBitConfig);
|
||||
}
|
||||
|
||||
if(!producer.isEnabled())
|
||||
{
|
||||
LOG.debug("Not using producer which is not enabled", logPair("producer", producer.getClass().getSimpleName()));
|
||||
continue;
|
||||
}
|
||||
|
||||
MetaDataProducerOutput output = producer.produce(qInstance);
|
||||
|
||||
/////////////////////////////////////////
|
||||
// apply table customizer, if provided //
|
||||
/////////////////////////////////////////
|
||||
if(qBitConfig.getTableMetaDataCustomizer() != null && output instanceof QTableMetaData table)
|
||||
{
|
||||
output = qBitConfig.getTableMetaDataCustomizer().customizeMetaData(qInstance, table);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// set source qbit, if output is aware of such //
|
||||
/////////////////////////////////////////////////
|
||||
if(output instanceof SourceQBitAware sourceQBitAware)
|
||||
{
|
||||
sourceQBitAware.setSourceQBitName(qBitMetaData.getName());
|
||||
}
|
||||
|
||||
output.addSelfToInstance(qInstance);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.model.metadata.qbits;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** interface for meta data objects that may have come from a qbit, and where we
|
||||
** might want to get data about that qbit (e.g., config or meta-data).
|
||||
*******************************************************************************/
|
||||
public interface SourceQBitAware
|
||||
{
|
||||
/*******************************************************************************
|
||||
** Getter for sourceQBitName
|
||||
*******************************************************************************/
|
||||
String getSourceQBitName();
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sourceQBitName
|
||||
*******************************************************************************/
|
||||
void setSourceQBitName(String sourceQBitName);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sourceQBitName
|
||||
*******************************************************************************/
|
||||
Object withSourceQBitName(String sourceQBitName);
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
default QBitMetaData getSourceQBit()
|
||||
{
|
||||
String qbitName = getSourceQBitName();
|
||||
return (QContext.getQInstance().getQBits().get(qbitName));
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
default QBitConfig getSourceQBitConfig()
|
||||
{
|
||||
QBitMetaData sourceQBit = getSourceQBit();
|
||||
if(sourceQBit == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return sourceQBit.getConfig();
|
||||
}
|
||||
}
|
||||
}
|
@ -40,7 +40,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
** (optionally along with queryJoins and queryInputCustomizer) is used.
|
||||
** - else a staticDataSupplier is used.
|
||||
*******************************************************************************/
|
||||
public class QReportDataSource implements Cloneable
|
||||
public class QReportDataSource
|
||||
{
|
||||
private String name;
|
||||
|
||||
@ -55,39 +55,6 @@ public class QReportDataSource implements Cloneable
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public QReportDataSource clone()
|
||||
{
|
||||
try
|
||||
{
|
||||
QReportDataSource clone = (QReportDataSource) super.clone();
|
||||
if(queryFilter != null)
|
||||
{
|
||||
clone.queryFilter = queryFilter.clone();
|
||||
}
|
||||
|
||||
if(queryJoins != null)
|
||||
{
|
||||
clone.queryJoins = new ArrayList<>();
|
||||
for(QueryJoin join : queryJoins)
|
||||
{
|
||||
queryJoins.add(join.clone());
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
catch(CloneNotSupportedException e)
|
||||
{
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
**
|
||||
@ -307,7 +274,6 @@ public class QReportDataSource implements Cloneable
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for customRecordSource
|
||||
*******************************************************************************/
|
||||
@ -337,4 +303,5 @@ public class QReportDataSource implements Cloneable
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.TopLevelMetaDataInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
@ -38,7 +37,7 @@ import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
/*******************************************************************************
|
||||
** Meta-data definition of a report generated by QQQ
|
||||
*******************************************************************************/
|
||||
public class QReportMetaData implements QAppChildMetaData, MetaDataWithPermissionRules, TopLevelMetaDataInterface, Cloneable
|
||||
public class QReportMetaData implements QAppChildMetaData, MetaDataWithPermissionRules, TopLevelMetaDataInterface
|
||||
{
|
||||
private String name;
|
||||
private String label;
|
||||
@ -53,72 +52,6 @@ public class QReportMetaData implements QAppChildMetaData, MetaDataWithPermissio
|
||||
|
||||
private QIcon icon;
|
||||
|
||||
private QCodeReference exportStyleCustomizer;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public QReportMetaData clone()
|
||||
{
|
||||
try
|
||||
{
|
||||
QReportMetaData clone = (QReportMetaData) super.clone();
|
||||
|
||||
//////////////////////////////
|
||||
// Deep copy mutable fields //
|
||||
//////////////////////////////
|
||||
if(this.inputFields != null)
|
||||
{
|
||||
clone.inputFields = new ArrayList<>();
|
||||
for(QFieldMetaData inputField : this.inputFields)
|
||||
{
|
||||
clone.inputFields.add(inputField.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if(this.dataSources != null)
|
||||
{
|
||||
clone.dataSources = new ArrayList<>();
|
||||
for(QReportDataSource dataSource : this.dataSources)
|
||||
{
|
||||
clone.dataSources.add(dataSource.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if(this.views != null)
|
||||
{
|
||||
clone.views = new ArrayList<>();
|
||||
for(QReportView view : this.views)
|
||||
{
|
||||
clone.views.add(view.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if(this.permissionRules != null)
|
||||
{
|
||||
clone.permissionRules = this.permissionRules.clone();
|
||||
}
|
||||
|
||||
if(this.icon != null)
|
||||
{
|
||||
clone.icon = this.icon.clone();
|
||||
}
|
||||
|
||||
if(this.exportStyleCustomizer != null)
|
||||
{
|
||||
clone.exportStyleCustomizer = this.exportStyleCustomizer.clone();
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
catch(CloneNotSupportedException e)
|
||||
{
|
||||
throw new AssertionError("Cloning not supported", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -464,35 +397,4 @@ public class QReportMetaData implements QAppChildMetaData, MetaDataWithPermissio
|
||||
qInstance.addReport(this);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for exportStyleCustomizer
|
||||
*******************************************************************************/
|
||||
public QCodeReference getExportStyleCustomizer()
|
||||
{
|
||||
return (this.exportStyleCustomizer);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for exportStyleCustomizer
|
||||
*******************************************************************************/
|
||||
public void setExportStyleCustomizer(QCodeReference exportStyleCustomizer)
|
||||
{
|
||||
this.exportStyleCustomizer = exportStyleCustomizer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for exportStyleCustomizer
|
||||
*******************************************************************************/
|
||||
public QReportMetaData withExportStyleCustomizer(QCodeReference exportStyleCustomizer)
|
||||
{
|
||||
this.exportStyleCustomizer = exportStyleCustomizer;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -50,7 +50,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.qbits.SourceQBitAware;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.sharing.ShareableTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.QTableAutomationDetails;
|
||||
@ -63,7 +62,7 @@ import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
** Meta-Data to define a table in a QQQ instance.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class QTableMetaData implements QAppChildMetaData, Serializable, MetaDataWithPermissionRules, TopLevelMetaDataInterface, SourceQBitAware
|
||||
public class QTableMetaData implements QAppChildMetaData, Serializable, MetaDataWithPermissionRules, TopLevelMetaDataInterface
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(QTableMetaData.class);
|
||||
|
||||
@ -74,8 +73,6 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
||||
private String primaryKeyField;
|
||||
private boolean isHidden = false;
|
||||
|
||||
private String sourceQBitName;
|
||||
|
||||
private Map<String, QFieldMetaData> fields;
|
||||
private List<UniqueKey> uniqueKeys;
|
||||
private List<Association> associations;
|
||||
@ -1059,7 +1056,7 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
||||
{
|
||||
for(Capability disabledCapability : disabledCapabilities)
|
||||
{
|
||||
withoutCapability(disabledCapability);
|
||||
withCapability(disabledCapability);
|
||||
}
|
||||
return (this);
|
||||
}
|
||||
@ -1557,38 +1554,4 @@ public class QTableMetaData implements QAppChildMetaData, Serializable, MetaData
|
||||
QInstanceHelpContentManager.removeHelpContentByRoleSetFromList(roles, listForSlot);
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sourceQBitName
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getSourceQBitName()
|
||||
{
|
||||
return (this.sourceQBitName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for sourceQBitName
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void setSourceQBitName(String sourceQBitName)
|
||||
{
|
||||
this.sourceQBitName = sourceQBitName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for sourceQBitName
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public QTableMetaData withSourceQBitName(String sourceQBitName)
|
||||
{
|
||||
this.sourceQBitName = sourceQBitName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,89 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.model.metadata.tables;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionCheckResult;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.QCustomPossibleValueProvider;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.values.SearchPossibleValueSourceInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** possible-value source provider for the `Tables` PVS - a list of all tables
|
||||
** in an application/qInstance.
|
||||
*******************************************************************************/
|
||||
public class TablesCustomPossibleValueProvider implements QCustomPossibleValueProvider<String>
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public QPossibleValue<String> getPossibleValue(Serializable idValue)
|
||||
{
|
||||
QTableMetaData table = QContext.getQInstance().getTable(ValueUtils.getValueAsString(idValue));
|
||||
if(table != null && !table.getIsHidden())
|
||||
{
|
||||
PermissionCheckResult permissionCheckResult = PermissionsHelper.getPermissionCheckResult(new QueryInput(table.getName()), table);
|
||||
if(PermissionCheckResult.ALLOW.equals(permissionCheckResult))
|
||||
{
|
||||
return (new QPossibleValue<>(table.getName(), table.getLabel()));
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public List<QPossibleValue<String>> search(SearchPossibleValueSourceInput input) throws QException
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// build all of the possible values (note, will be filtered by user's permissions) //
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
List<QPossibleValue<String>> allPossibleValues = new ArrayList<>();
|
||||
for(QTableMetaData table : QContext.getQInstance().getTables().values())
|
||||
{
|
||||
QPossibleValue<String> possibleValue = getPossibleValue(table.getName());
|
||||
if(possibleValue != null)
|
||||
{
|
||||
allPossibleValues.add(possibleValue);
|
||||
}
|
||||
}
|
||||
|
||||
return completeCustomPVSSearch(input, allPossibleValues);
|
||||
}
|
||||
|
||||
}
|
@ -22,11 +22,17 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.tables;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
|
||||
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.possiblevalues.PVSValueFormatAndFields;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import org.apache.commons.lang.BooleanUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -45,10 +51,22 @@ public class TablesPossibleValueSourceMetaDataProvider
|
||||
{
|
||||
QPossibleValueSource possibleValueSource = new QPossibleValueSource()
|
||||
.withName(NAME)
|
||||
.withType(QPossibleValueSourceType.CUSTOM)
|
||||
.withCustomCodeReference(new QCodeReference(TablesCustomPossibleValueProvider.class))
|
||||
.withType(QPossibleValueSourceType.ENUM)
|
||||
.withValueFormatAndFields(PVSValueFormatAndFields.LABEL_ONLY);
|
||||
|
||||
List<QPossibleValue<?>> enumValues = new ArrayList<>();
|
||||
for(QTableMetaData table : qInstance.getTables().values())
|
||||
{
|
||||
if(BooleanUtils.isNotTrue(table.getIsHidden()))
|
||||
{
|
||||
String label = StringUtils.hasContent(table.getLabel()) ? table.getLabel() : QInstanceEnricher.nameToLabel(table.getName());
|
||||
enumValues.add(new QPossibleValue<>(table.getName(), label));
|
||||
}
|
||||
}
|
||||
|
||||
enumValues.sort(Comparator.comparing(QPossibleValue::getLabel));
|
||||
|
||||
possibleValueSource.withEnumValues(enumValues);
|
||||
return (possibleValueSource);
|
||||
}
|
||||
|
||||
|
@ -56,16 +56,7 @@ import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
/*******************************************************************************
|
||||
** Note - exists under 2 names, for the RenderSavedReport process, and for the
|
||||
** ScheduledReport table (and can be used in your custom code too:
|
||||
*
|
||||
** by default, in qqq backend core, we'll assume this widget is being used on the
|
||||
** view screen for a ScheduledReport, with field names that we know from that table.
|
||||
** But, allow it to be used on a different table (optionally with different field names),
|
||||
** coming from the input map.
|
||||
**
|
||||
** e.g., that one may set in widget metaData as:
|
||||
** .withDefaultValue("tableName", "myTable")
|
||||
** .withDefaultValue("fieldNameId", "identifier"), etc.
|
||||
** ScheduledReport table
|
||||
*******************************************************************************/
|
||||
public class ReportValuesDynamicFormWidgetRenderer extends AbstractWidgetRenderer
|
||||
{
|
||||
@ -97,16 +88,11 @@ public class ReportValuesDynamicFormWidgetRenderer extends AbstractWidgetRendere
|
||||
}
|
||||
else if(input.getQueryParams().containsKey("id"))
|
||||
{
|
||||
String tableName = input.getQueryParams().getOrDefault("tableName", ScheduledReport.TABLE_NAME);
|
||||
String fieldNameId = input.getQueryParams().getOrDefault("fieldNameId", "id");
|
||||
String fieldNameSavedReportId = input.getQueryParams().getOrDefault("fieldNameSavedReportId", "savedReportId");
|
||||
String fieldNameInputValues = input.getQueryParams().getOrDefault("fieldNameInputValues", "inputValues");
|
||||
|
||||
QRecord hostRecord = new GetAction().executeForRecord(new GetInput(tableName).withPrimaryKey(ValueUtils.getValueAsInteger(input.getQueryParams().get(fieldNameId))));
|
||||
QRecord record = new GetAction().executeForRecord(new GetInput(SavedReport.TABLE_NAME).withPrimaryKey(ValueUtils.getValueAsInteger(hostRecord.getValueInteger(fieldNameSavedReportId))));
|
||||
QRecord scheduledReportRecord = new GetAction().executeForRecord(new GetInput(ScheduledReport.TABLE_NAME).withPrimaryKey(ValueUtils.getValueAsInteger(input.getQueryParams().get("id"))));
|
||||
QRecord record = new GetAction().executeForRecord(new GetInput(SavedReport.TABLE_NAME).withPrimaryKey(ValueUtils.getValueAsInteger(scheduledReportRecord.getValueInteger("savedReportId"))));
|
||||
savedReport = new SavedReport(record);
|
||||
|
||||
String inputValues = hostRecord.getValueString(fieldNameInputValues);
|
||||
String inputValues = scheduledReportRecord.getValueString("inputValues");
|
||||
if(StringUtils.hasContent(inputValues))
|
||||
{
|
||||
JSONObject jsonObject = JsonUtils.toJSONObject(inputValues);
|
||||
@ -211,8 +197,8 @@ public class ReportValuesDynamicFormWidgetRenderer extends AbstractWidgetRendere
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error rendering report values dynamic form widget", e, logPair("queryParams", String.valueOf(input.getQueryParams())));
|
||||
throw (new QException("Error rendering report values dynamic form widget", e));
|
||||
LOG.warn("Error rendering scheduled report values dynamic form widget", e, logPair("queryParams", String.valueOf(input.getQueryParams())));
|
||||
throw (new QException("Error rendering scheduled report values dynamic form widget", e));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,6 +50,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.sharing.ShareScopePossibleValueMetaDataProducer;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.sharing.ShareableAudienceType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.sharing.ShareableTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||
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;
|
||||
@ -350,7 +351,8 @@ public class SavedReportsMetaDataProvider
|
||||
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "savedReportId", "renderedReportStatusId")))
|
||||
.withSection(new QFieldSection("input", new QIcon().withName("input"), Tier.T2, List.of("userId", "reportFormat")))
|
||||
.withSection(new QFieldSection("output", new QIcon().withName("output"), Tier.T2, List.of("jobUuid", "resultPath", "rowCount", "errorMessage", "startTime", "endTime")))
|
||||
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")));
|
||||
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")))
|
||||
.withoutCapabilities(Capability.allWriteCapabilities());
|
||||
|
||||
table.getField("renderedReportStatusId").setAdornments(List.of(new FieldAdornment(AdornmentType.CHIP)
|
||||
.withValues(AdornmentType.ChipValues.iconAndColorValues(RenderedReportStatus.RUNNING.getId(), "pending", AdornmentType.ChipValues.COLOR_SECONDARY))
|
||||
|
@ -25,7 +25,6 @@ package com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
@ -111,10 +110,6 @@ public class BulkInsertLoadStep extends LoadViaInsertStep implements ProcessSumm
|
||||
if(field.getType().isNumeric())
|
||||
{
|
||||
ProcessSummaryLine idsLine = new ProcessSummaryLine(Status.INFO, "Inserted " + field.getLabel() + " values between " + firstInsertedPrimaryKey + " and " + lastInsertedPrimaryKey);
|
||||
if(Objects.equals(firstInsertedPrimaryKey, lastInsertedPrimaryKey))
|
||||
{
|
||||
idsLine.setMessage("Inserted " + field.getLabel() + " " + firstInsertedPrimaryKey);
|
||||
}
|
||||
idsLine.setCount(null);
|
||||
processSummary.add(idsLine);
|
||||
}
|
||||
|
@ -24,12 +24,8 @@ package com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.StorageAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
@ -41,14 +37,9 @@ import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.mapp
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.mapping.BulkLoadTableStructureBuilder;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.model.BulkLoadFileRow;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.model.BulkLoadProfile;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.model.BulkLoadProfileField;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.bulk.insert.model.BulkLoadTableStructure;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.StreamedETLWithFrontendProcess;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -81,7 +72,7 @@ public class BulkInsertPrepareFileMappingStep implements BackendStep
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
List<String> headerValues = (List<String>) runBackendStepOutput.getValue("headerValues");
|
||||
buildSuggestedMapping(headerValues, getPrepopulatedValues(runBackendStepInput), tableStructure, runBackendStepOutput);
|
||||
buildSuggestedMapping(headerValues, tableStructure, runBackendStepOutput);
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,62 +81,10 @@ public class BulkInsertPrepareFileMappingStep implements BackendStep
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private Map<String, Serializable> getPrepopulatedValues(RunBackendStepInput runBackendStepInput)
|
||||
{
|
||||
String prepopulatedValuesJson = runBackendStepInput.getValueString("prepopulatedValues");
|
||||
if(StringUtils.hasContent(prepopulatedValuesJson))
|
||||
{
|
||||
Map<String, Serializable> rs = new LinkedHashMap<>();
|
||||
JSONObject jsonObject = JsonUtils.toJSONObject(prepopulatedValuesJson);
|
||||
for(String key : jsonObject.keySet())
|
||||
{
|
||||
rs.put(key, jsonObject.optString(key, null));
|
||||
}
|
||||
return (rs);
|
||||
}
|
||||
|
||||
return (Collections.emptyMap());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private void buildSuggestedMapping(List<String> headerValues, Map<String, Serializable> prepopulatedValues, BulkLoadTableStructure tableStructure, RunBackendStepOutput runBackendStepOutput)
|
||||
private void buildSuggestedMapping(List<String> headerValues, BulkLoadTableStructure tableStructure, RunBackendStepOutput runBackendStepOutput)
|
||||
{
|
||||
BulkLoadMappingSuggester bulkLoadMappingSuggester = new BulkLoadMappingSuggester();
|
||||
BulkLoadProfile bulkLoadProfile = bulkLoadMappingSuggester.suggestBulkLoadMappingProfile(tableStructure, headerValues);
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(prepopulatedValues))
|
||||
{
|
||||
for(Map.Entry<String, Serializable> entry : prepopulatedValues.entrySet())
|
||||
{
|
||||
String fieldName = entry.getKey();
|
||||
boolean foundFieldInProfile = false;
|
||||
|
||||
for(BulkLoadProfileField bulkLoadProfileField : bulkLoadProfile.getFieldList())
|
||||
{
|
||||
if(bulkLoadProfileField.getFieldName().equals(fieldName))
|
||||
{
|
||||
foundFieldInProfile = true;
|
||||
bulkLoadProfileField.setColumnIndex(null);
|
||||
bulkLoadProfileField.setHeaderName(null);
|
||||
bulkLoadProfileField.setDefaultValue(entry.getValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!foundFieldInProfile)
|
||||
{
|
||||
BulkLoadProfileField bulkLoadProfileField = new BulkLoadProfileField();
|
||||
bulkLoadProfileField.setFieldName(fieldName);
|
||||
bulkLoadProfileField.setDefaultValue(entry.getValue());
|
||||
bulkLoadProfile.getFieldList().add(bulkLoadProfileField);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runBackendStepOutput.addValue("bulkLoadProfile", bulkLoadProfile);
|
||||
runBackendStepOutput.addValue("suggestedBulkLoadProfile", bulkLoadProfile);
|
||||
}
|
||||
|
@ -76,23 +76,6 @@ public class XlsxFileToRows extends AbstractIteratorBasedFileToRows<org.dhatim.f
|
||||
}
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
** open/go-to a specific sheet (by 0-based index). resets rows & iterator.
|
||||
***************************************************************************/
|
||||
public void openSheet(int index) throws IOException
|
||||
{
|
||||
Optional<Sheet> sheet = workbook.getSheet(index);
|
||||
|
||||
if(sheet.isEmpty())
|
||||
{
|
||||
throw (new IOException("No sheet found for index: " + index));
|
||||
}
|
||||
|
||||
rows = sheet.get().openStream();
|
||||
setIterator(rows.iterator());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
|
@ -53,7 +53,7 @@ public class BaseStreamedETLStep
|
||||
protected AbstractExtractStep getExtractStep(RunBackendStepInput runBackendStepInput)
|
||||
{
|
||||
QCodeReference codeReference = (QCodeReference) runBackendStepInput.getValue(StreamedETLWithFrontendProcess.FIELD_EXTRACT_CODE);
|
||||
return (QCodeLoader.getAdHoc(AbstractExtractStep.class, codeReference));
|
||||
return (QCodeLoader.getBackendStep(AbstractExtractStep.class, codeReference));
|
||||
}
|
||||
|
||||
|
||||
|
@ -22,13 +22,13 @@
|
||||
package com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class CouldNotFindQueryFilterForExtractStepException extends QUserFacingException
|
||||
public class CouldNotFindQueryFilterForExtractStepException extends QException
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
|
@ -279,7 +279,7 @@ public class ExtractViaQueryStep extends AbstractExtractStep
|
||||
return (new QQueryFilter().withCriteria(new QFilterCriteria(table.getPrimaryKeyField(), QCriteriaOperator.IN, idStrings)));
|
||||
}
|
||||
|
||||
throw (new CouldNotFindQueryFilterForExtractStepException("No records were selected for running this process."));
|
||||
throw (new CouldNotFindQueryFilterForExtractStepException("Could not find query filter for Extract step."));
|
||||
}
|
||||
|
||||
|
||||
|
@ -92,21 +92,16 @@ public class RenderSavedReportExecuteStep implements BackendStep
|
||||
////////////////////////////////
|
||||
// read inputs, set up params //
|
||||
////////////////////////////////
|
||||
String sesProviderName = runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.SES_PROVIDER_NAME);
|
||||
String fromEmailAddress = runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FROM_EMAIL_ADDRESS);
|
||||
String replyToEmailAddress = runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.REPLY_TO_EMAIL_ADDRESS);
|
||||
String storageTableName = runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FIELD_NAME_STORAGE_TABLE_NAME);
|
||||
ReportFormat reportFormat = ReportFormat.fromString(runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FIELD_NAME_REPORT_FORMAT));
|
||||
String sendToEmailAddress = runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FIELD_NAME_EMAIL_ADDRESS);
|
||||
String emailSubject = runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FIELD_NAME_EMAIL_SUBJECT);
|
||||
SavedReport savedReport = new SavedReport(runBackendStepInput.getRecords().get(0));
|
||||
|
||||
String downloadFileBaseName = getDownloadFileBaseName(runBackendStepInput, savedReport);
|
||||
String storageReference = runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FIELD_NAME_STORAGE_REFERENCE);
|
||||
if(!StringUtils.hasContent(storageReference))
|
||||
{
|
||||
storageReference = LocalDate.now() + "/" + LocalTime.now().toString().replaceAll(":", "").replaceFirst("\\..*", "") + "/" + UUID.randomUUID() + "/" + downloadFileBaseName + "." + reportFormat.getExtension();
|
||||
}
|
||||
String sesProviderName = runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.SES_PROVIDER_NAME);
|
||||
String fromEmailAddress = runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FROM_EMAIL_ADDRESS);
|
||||
String replyToEmailAddress = runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.REPLY_TO_EMAIL_ADDRESS);
|
||||
String storageTableName = runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FIELD_NAME_STORAGE_TABLE_NAME);
|
||||
ReportFormat reportFormat = ReportFormat.fromString(runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FIELD_NAME_REPORT_FORMAT));
|
||||
String sendToEmailAddress = runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FIELD_NAME_EMAIL_ADDRESS);
|
||||
String emailSubject = runBackendStepInput.getValueString(RenderSavedReportMetaDataProducer.FIELD_NAME_EMAIL_SUBJECT);
|
||||
SavedReport savedReport = new SavedReport(runBackendStepInput.getRecords().get(0));
|
||||
String downloadFileBaseName = getDownloadFileBaseName(runBackendStepInput, savedReport);
|
||||
String storageReference = LocalDate.now() + "/" + LocalTime.now().toString().replaceAll(":", "").replaceFirst("\\..*", "") + "/" + UUID.randomUUID() + "/" + downloadFileBaseName + "." + reportFormat.getExtension();
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if sending an email (or emails), validate the addresses before doing anything so user gets error and can fix //
|
||||
@ -246,7 +241,7 @@ public class RenderSavedReportExecuteStep implements BackendStep
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static String getDownloadFileBaseName(RunBackendStepInput runBackendStepInput, SavedReport report)
|
||||
private String getDownloadFileBaseName(RunBackendStepInput runBackendStepInput, SavedReport report)
|
||||
{
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HHmm").withZone(ZoneId.systemDefault());
|
||||
String datePart = formatter.format(Instant.now());
|
||||
|
@ -56,7 +56,6 @@ public class RenderSavedReportMetaDataProducer implements MetaDataProducerInterf
|
||||
public static final String FROM_EMAIL_ADDRESS = "fromEmailAddress";
|
||||
public static final String REPLY_TO_EMAIL_ADDRESS = "replyToEmailAddress";
|
||||
public static final String FIELD_NAME_STORAGE_TABLE_NAME = "storageTableName";
|
||||
public static final String FIELD_NAME_STORAGE_REFERENCE = "storageReference";
|
||||
public static final String FIELD_NAME_REPORT_FORMAT = "reportFormat";
|
||||
public static final String FIELD_NAME_EMAIL_ADDRESS = "reportDestinationEmailAddress";
|
||||
public static final String FIELD_NAME_EMAIL_SUBJECT = "emailSubject";
|
||||
@ -82,7 +81,6 @@ public class RenderSavedReportMetaDataProducer implements MetaDataProducerInterf
|
||||
.withField(new QFieldMetaData(FROM_EMAIL_ADDRESS, QFieldType.STRING))
|
||||
.withField(new QFieldMetaData(REPLY_TO_EMAIL_ADDRESS, QFieldType.STRING))
|
||||
.withField(new QFieldMetaData(FIELD_NAME_STORAGE_TABLE_NAME, QFieldType.STRING))
|
||||
.withField(new QFieldMetaData(FIELD_NAME_STORAGE_REFERENCE, QFieldType.STRING))
|
||||
.withRecordListMetaData(new QRecordListMetaData().withTableName(SavedReport.TABLE_NAME)))
|
||||
.withCode(new QCodeReference(RenderSavedReportPreStep.class)))
|
||||
|
||||
|
@ -318,7 +318,7 @@ public class SavedReportToReportMetaDataAdapter
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QReportField makeQReportField(String fieldName, FieldAndJoinTable fieldAndJoinTable)
|
||||
private static QReportField makeQReportField(String fieldName, FieldAndJoinTable fieldAndJoinTable)
|
||||
{
|
||||
QReportField reportField = new QReportField();
|
||||
|
||||
@ -404,5 +404,5 @@ public class SavedReportToReportMetaDataAdapter
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public record FieldAndJoinTable(QFieldMetaData field, QTableMetaData joinTable) {}
|
||||
private record FieldAndJoinTable(QFieldMetaData field, QTableMetaData joinTable) {}
|
||||
}
|
||||
|
@ -173,21 +173,8 @@ public abstract class AbstractTableSyncTransformStep extends AbstractTransformSt
|
||||
*******************************************************************************/
|
||||
protected QQueryFilter getExistingRecordQueryFilter(RunBackendStepInput runBackendStepInput, List<Serializable> sourceKeyList)
|
||||
{
|
||||
String destinationTableForeignKeyFieldName = getSyncProcessConfig().destinationTableForeignKey;
|
||||
String destinationTableName = getSyncProcessConfig().destinationTable;
|
||||
QFieldMetaData destinationForeignKeyField = QContext.getQInstance().getTable(destinationTableName).getField(destinationTableForeignKeyFieldName);
|
||||
|
||||
List<Serializable> sourceKeysInDestinationKeyTypeList = null;
|
||||
if(sourceKeyList != null)
|
||||
{
|
||||
sourceKeysInDestinationKeyTypeList = new ArrayList<>();
|
||||
for(Serializable sourceKey : sourceKeyList)
|
||||
{
|
||||
sourceKeysInDestinationKeyTypeList.add(ValueUtils.getValueAsFieldType(destinationForeignKeyField.getType(), sourceKey));
|
||||
}
|
||||
}
|
||||
|
||||
return new QQueryFilter().withCriteria(new QFilterCriteria(destinationTableForeignKeyFieldName, QCriteriaOperator.IN, sourceKeysInDestinationKeyTypeList));
|
||||
String destinationTableForeignKeyField = getSyncProcessConfig().destinationTableForeignKey;
|
||||
return new QQueryFilter().withCriteria(new QFilterCriteria(destinationTableForeignKeyField, QCriteriaOperator.IN, sourceKeyList));
|
||||
}
|
||||
|
||||
|
||||
@ -356,12 +343,12 @@ public abstract class AbstractTableSyncTransformStep extends AbstractTransformSt
|
||||
{
|
||||
if(existingRecord != null)
|
||||
{
|
||||
LOG.debug("Skipping storing existing record because this sync process is set to not perform updates");
|
||||
LOG.info("Skipping storing existing record because this sync process is set to not perform updates");
|
||||
willNotInsert.incrementCountAndAddPrimaryKey(sourcePrimaryKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.debug("Skipping storing new record because this sync process is set to not perform inserts");
|
||||
LOG.info("Skipping storing new record because this sync process is set to not perform inserts");
|
||||
willNotUpdate.incrementCountAndAddPrimaryKey(sourcePrimaryKey);
|
||||
}
|
||||
continue;
|
||||
|
@ -1,343 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.scheduler;
|
||||
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** class to give a human-friendly descriptive string from a cron expression.
|
||||
** (written in half by my friend Mr. Chatty G)
|
||||
*******************************************************************************/
|
||||
public class CronDescriber
|
||||
{
|
||||
private static final Map<String, String> DAY_OF_WEEK_MAP = new HashMap<>();
|
||||
private static final Map<String, String> MONTH_MAP = new HashMap<>();
|
||||
|
||||
static
|
||||
{
|
||||
DAY_OF_WEEK_MAP.put("1", "Sunday");
|
||||
DAY_OF_WEEK_MAP.put("2", "Monday");
|
||||
DAY_OF_WEEK_MAP.put("3", "Tuesday");
|
||||
DAY_OF_WEEK_MAP.put("4", "Wednesday");
|
||||
DAY_OF_WEEK_MAP.put("5", "Thursday");
|
||||
DAY_OF_WEEK_MAP.put("6", "Friday");
|
||||
DAY_OF_WEEK_MAP.put("7", "Saturday");
|
||||
|
||||
////////////////////////////////
|
||||
// Quartz also allows SUN-SAT //
|
||||
////////////////////////////////
|
||||
DAY_OF_WEEK_MAP.put("SUN", "Sunday");
|
||||
DAY_OF_WEEK_MAP.put("MON", "Monday");
|
||||
DAY_OF_WEEK_MAP.put("TUE", "Tuesday");
|
||||
DAY_OF_WEEK_MAP.put("WED", "Wednesday");
|
||||
DAY_OF_WEEK_MAP.put("THU", "Thursday");
|
||||
DAY_OF_WEEK_MAP.put("FRI", "Friday");
|
||||
DAY_OF_WEEK_MAP.put("SAT", "Saturday");
|
||||
|
||||
MONTH_MAP.put("1", "January");
|
||||
MONTH_MAP.put("2", "February");
|
||||
MONTH_MAP.put("3", "March");
|
||||
MONTH_MAP.put("4", "April");
|
||||
MONTH_MAP.put("5", "May");
|
||||
MONTH_MAP.put("6", "June");
|
||||
MONTH_MAP.put("7", "July");
|
||||
MONTH_MAP.put("8", "August");
|
||||
MONTH_MAP.put("9", "September");
|
||||
MONTH_MAP.put("10", "October");
|
||||
MONTH_MAP.put("11", "November");
|
||||
MONTH_MAP.put("12", "December");
|
||||
|
||||
////////////////////////////////
|
||||
// Quartz also allows JAN-DEC //
|
||||
////////////////////////////////
|
||||
MONTH_MAP.put("JAN", "January");
|
||||
MONTH_MAP.put("FEB", "February");
|
||||
MONTH_MAP.put("MAR", "March");
|
||||
MONTH_MAP.put("APR", "April");
|
||||
MONTH_MAP.put("MAY", "May");
|
||||
MONTH_MAP.put("JUN", "June");
|
||||
MONTH_MAP.put("JUL", "July");
|
||||
MONTH_MAP.put("AUG", "August");
|
||||
MONTH_MAP.put("SEP", "September");
|
||||
MONTH_MAP.put("OCT", "October");
|
||||
MONTH_MAP.put("NOV", "November");
|
||||
MONTH_MAP.put("DEC", "December");
|
||||
}
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static String getDescription(String cronExpression) throws ParseException
|
||||
{
|
||||
String[] parts = cronExpression.trim().toUpperCase().split("\\s+");
|
||||
if(parts.length < 6 || parts.length > 7)
|
||||
{
|
||||
throw new ParseException("Invalid cron expression: " + cronExpression, 0);
|
||||
}
|
||||
|
||||
String seconds = parts[0];
|
||||
String minutes = parts[1];
|
||||
String hours = parts[2];
|
||||
String dayOfMonth = parts[3];
|
||||
String month = parts[4];
|
||||
String dayOfWeek = parts[5];
|
||||
String year = parts.length == 7 ? parts[6] : "*";
|
||||
|
||||
StringBuilder description = new StringBuilder();
|
||||
|
||||
description.append("At ");
|
||||
description.append(describeTime(seconds, minutes, hours));
|
||||
description.append(", on ");
|
||||
description.append(describeDayOfMonth(dayOfMonth));
|
||||
description.append(" of ");
|
||||
description.append(describeMonth(month));
|
||||
description.append(", ");
|
||||
description.append(describeDayOfWeek(dayOfWeek));
|
||||
if(!year.equals("*"))
|
||||
{
|
||||
description.append(", in ").append(year);
|
||||
}
|
||||
description.append(".");
|
||||
|
||||
return description.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private static String describeTime(String seconds, String minutes, String hours)
|
||||
{
|
||||
return String.format("%s, %s, %s", describePart(seconds, "second"), describePart(minutes, "minute"), describePart(hours, "hour"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private static String describeDayOfMonth(String dayOfMonth)
|
||||
{
|
||||
if(dayOfMonth.equals("?"))
|
||||
{
|
||||
return "every day";
|
||||
}
|
||||
else if(dayOfMonth.equals("L"))
|
||||
{
|
||||
return "the last day";
|
||||
}
|
||||
else if(dayOfMonth.contains("W"))
|
||||
{
|
||||
return "the nearest weekday to day " + dayOfMonth.replace("W", "");
|
||||
}
|
||||
else
|
||||
{
|
||||
return (describePart(dayOfMonth, "day"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private static String describeMonth(String month)
|
||||
{
|
||||
if(month.equals("*"))
|
||||
{
|
||||
return "every month";
|
||||
}
|
||||
else if(month.contains("-"))
|
||||
{
|
||||
String[] parts = month.split("-");
|
||||
return String.format("%s to %s", MONTH_MAP.getOrDefault(parts[0], parts[0]), MONTH_MAP.getOrDefault(parts[1], parts[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
String[] months = month.split(",");
|
||||
List<String> monthNames = Arrays.stream(months).map(m -> MONTH_MAP.getOrDefault(m, m)).toList();
|
||||
return StringUtils.joinWithCommasAndAnd(monthNames);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private static String describeDayOfWeek(String dayOfWeek)
|
||||
{
|
||||
if(dayOfWeek.equals("?") || dayOfWeek.equals("*"))
|
||||
{
|
||||
return "every day of the week";
|
||||
}
|
||||
else if(dayOfWeek.equals("L"))
|
||||
{
|
||||
return "the last day of the week";
|
||||
}
|
||||
else if(dayOfWeek.contains("#"))
|
||||
{
|
||||
String[] parts = dayOfWeek.split("#");
|
||||
return String.format("the %s %s of the month", ordinal(parts[1]), DAY_OF_WEEK_MAP.getOrDefault(parts[0], parts[0]));
|
||||
}
|
||||
else if(dayOfWeek.contains("-"))
|
||||
{
|
||||
String[] parts = dayOfWeek.split("-");
|
||||
return String.format("from %s to %s", DAY_OF_WEEK_MAP.getOrDefault(parts[0], parts[0]), DAY_OF_WEEK_MAP.getOrDefault(parts[1], parts[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
String[] days = dayOfWeek.split(",");
|
||||
List<String> dayNames = Arrays.stream(days).map(d -> DAY_OF_WEEK_MAP.getOrDefault(d, d)).toList();
|
||||
return StringUtils.joinWithCommasAndAnd(dayNames);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private static String describePart(String part, String label)
|
||||
{
|
||||
if(part.equals("*"))
|
||||
{
|
||||
return "every " + label;
|
||||
}
|
||||
else if(part.contains("/"))
|
||||
{
|
||||
String[] parts = part.split("/");
|
||||
if(parts[0].equals("*"))
|
||||
{
|
||||
parts[0] = "0";
|
||||
}
|
||||
return String.format("every %s " + label + "s starting at %s", parts[1], parts[0]);
|
||||
}
|
||||
else if(part.contains(","))
|
||||
{
|
||||
List<String> partsList = Arrays.stream(part.split(",")).toList();
|
||||
|
||||
if(label.equals("hour"))
|
||||
{
|
||||
List<String> hourNames = partsList.stream().map(p -> hourToAmPm(p)).toList();
|
||||
return StringUtils.joinWithCommasAndAnd(hourNames);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(label.equals("day"))
|
||||
{
|
||||
return "days " + StringUtils.joinWithCommasAndAnd(partsList);
|
||||
}
|
||||
else
|
||||
{
|
||||
return StringUtils.joinWithCommasAndAnd(partsList) + " " + label + "s";
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(part.contains("-"))
|
||||
{
|
||||
String[] parts = part.split("-");
|
||||
if(label.equals("day"))
|
||||
{
|
||||
return String.format("%ss from %s to %s", label, parts[0], parts[1]);
|
||||
}
|
||||
else if(label.equals("hour"))
|
||||
{
|
||||
return String.format("from %s to %s", hourToAmPm(parts[0]), hourToAmPm(parts[1]));
|
||||
}
|
||||
else
|
||||
{
|
||||
return String.format("from %s to %s %s", parts[0], parts[1], label + "s");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(label.equals("day"))
|
||||
{
|
||||
return label + " " + part;
|
||||
}
|
||||
if(label.equals("hour"))
|
||||
{
|
||||
return hourToAmPm(part);
|
||||
}
|
||||
else
|
||||
{
|
||||
return part + " " + label + "s";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private static String hourToAmPm(String part)
|
||||
{
|
||||
try
|
||||
{
|
||||
int hour = Integer.parseInt(part);
|
||||
return switch(hour)
|
||||
{
|
||||
case 0 -> "midnight";
|
||||
case 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 -> hour + " AM";
|
||||
case 12 -> "noon";
|
||||
case 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 -> (hour - 12) + " PM";
|
||||
default -> hour + " hours";
|
||||
};
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
return part + " hours";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private static String ordinal(String number)
|
||||
{
|
||||
int n = Integer.parseInt(number);
|
||||
if(n >= 11 && n <= 13)
|
||||
{
|
||||
return n + "th";
|
||||
}
|
||||
|
||||
return switch(n % 10)
|
||||
{
|
||||
case 1 -> n + "st";
|
||||
case 2 -> n + "nd";
|
||||
case 3 -> n + "rd";
|
||||
default -> n + "th";
|
||||
};
|
||||
}
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.scheduler;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.ValueBehaviorApplier;
|
||||
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.AdornmentType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldDisplayBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Field display behavior, to add a human-redable tooltip to cron-expressions.
|
||||
*******************************************************************************/
|
||||
public class CronExpressionTooltipFieldBehavior implements FieldDisplayBehavior<CronExpressionTooltipFieldBehavior>
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
** Add both this behavior, and the tooltip adornment to a field
|
||||
** Note, if either was already there, then that part is left alone.
|
||||
***************************************************************************/
|
||||
public static void addToField(QFieldMetaData fieldMetaData)
|
||||
{
|
||||
CronExpressionTooltipFieldBehavior existingBehavior = fieldMetaData.getBehaviorOnlyIfSet(CronExpressionTooltipFieldBehavior.class);
|
||||
if(existingBehavior == null)
|
||||
{
|
||||
fieldMetaData.withBehavior(new CronExpressionTooltipFieldBehavior());
|
||||
}
|
||||
|
||||
if(fieldMetaData.getAdornment(AdornmentType.TOOLTIP).isEmpty())
|
||||
{
|
||||
fieldMetaData.withFieldAdornment((new FieldAdornment(AdornmentType.TOOLTIP)
|
||||
.withValue(AdornmentType.TooltipValues.TOOLTIP_DYNAMIC, true)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void apply(ValueBehaviorApplier.Action action, List<QRecord> recordList, QInstance instance, QTableMetaData table, QFieldMetaData field)
|
||||
{
|
||||
for(QRecord record : recordList)
|
||||
{
|
||||
try
|
||||
{
|
||||
String cronExpression = record.getValueString(field.getName());
|
||||
if(StringUtils.hasContent(cronExpression))
|
||||
{
|
||||
String description = CronDescriber.getDescription(cronExpression);
|
||||
record.setDisplayValue(field.getName() + ":" + AdornmentType.TooltipValues.TOOLTIP_DYNAMIC, description);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
/////////////////////
|
||||
// just leave null //
|
||||
/////////////////////
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -475,31 +475,4 @@ public class StringUtils
|
||||
return (s);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static String appendIncrementingSuffix(String input)
|
||||
{
|
||||
////////////////////////////////
|
||||
// remove any existing suffix //
|
||||
////////////////////////////////
|
||||
String base = input.replaceAll(" \\(\\d+\\)$", "");
|
||||
if(input.matches(".* \\(\\d+\\)$"))
|
||||
{
|
||||
//////////////////////////
|
||||
// increment if matches //
|
||||
//////////////////////////
|
||||
int current = Integer.parseInt(input.replaceAll(".* \\((\\d+)\\)$", "$1"));
|
||||
return base + " (" + (current + 1) + ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
////////////////////////////////////
|
||||
// no match so put a 1 at the end //
|
||||
////////////////////////////////////
|
||||
return base + " (1)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -247,16 +247,6 @@ public class Memoization<K, V>
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void clearKey(K key)
|
||||
{
|
||||
this.map.remove(key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for timeoutSeconds
|
||||
**
|
||||
|
@ -18,14 +18,21 @@
|
||||
</File>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Logger name="org.mongodb.driver" level="WARN" />
|
||||
<Logger name="org.eclipse.jetty" level="INFO" />
|
||||
<Logger name="io.javalin" level="INFO" />
|
||||
<Logger name="org.apache.log4j.xml" additivity="false">
|
||||
</Logger>
|
||||
<Logger name="org.mongodb.driver" level="WARN">
|
||||
</Logger>
|
||||
<Logger name="org.eclipse.jetty" level="INFO">
|
||||
</Logger>
|
||||
<Logger name="io.javalin" level="INFO">
|
||||
</Logger>
|
||||
<!-- c3p0 -->
|
||||
<Logger name="com.mchange.v2" level="INFO" />
|
||||
<Logger name="org.quartz" level="INFO" />
|
||||
<Logger name="liquibase" level="INFO" />
|
||||
<Logger name="com.amazonaws" level="INFO" />
|
||||
<Logger name="com.mchange.v2" level="INFO">
|
||||
</Logger>
|
||||
<Logger name="org.quartz" level="INFO">
|
||||
</Logger>
|
||||
<Logger name="liquibase" level="INFO">
|
||||
</Logger>
|
||||
<Root level="all">
|
||||
<AppenderRef ref="SystemOutAppender"/>
|
||||
<AppenderRef ref="SyslogAppender"/>
|
||||
|
@ -22,19 +22,12 @@
|
||||
package com.kingsrook.qqq.backend.core.actions.customizers;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.InitializableViaCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReferenceWithProperties;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.Timer;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -87,7 +80,6 @@ class QCodeLoaderTest extends BaseTest
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -99,50 +91,4 @@ class QCodeLoaderTest extends BaseTest
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testCodeReferenceWithProperties()
|
||||
{
|
||||
assertNull(QCodeLoader.getAdHoc(SomeClass.class, new QCodeReference(SomeClass.class)));
|
||||
|
||||
SomeClass someObject = QCodeLoader.getAdHoc(SomeClass.class, new QCodeReferenceWithProperties(SomeClass.class, Map.of("property", "someValue")));
|
||||
assertEquals("someValue", someObject.someProperty);
|
||||
|
||||
SomeClass someOtherObject = QCodeLoader.getAdHoc(SomeClass.class, new QCodeReferenceWithProperties(SomeClass.class, Map.of("property", "someOtherValue")));
|
||||
assertEquals("someOtherValue", someOtherObject.someProperty);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static class SomeClass implements InitializableViaCodeReference
|
||||
{
|
||||
private String someProperty;
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void initialize(QCodeReference codeReference)
|
||||
{
|
||||
if(codeReference instanceof QCodeReferenceWithProperties codeReferenceWithProperties)
|
||||
{
|
||||
someProperty = ValueUtils.getValueAsString(codeReferenceWithProperties.getProperties().get("property"));
|
||||
}
|
||||
|
||||
if(!StringUtils.hasContent(someProperty))
|
||||
{
|
||||
throw new IllegalStateException("Missing property");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,188 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.actions.dashboard.widgets;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
||||
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.QFilterOrderBy;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.dashboard.widgets.ChildRecordListData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for RecordListWidgetRenderer
|
||||
*******************************************************************************/
|
||||
class RecordListWidgetRendererTest extends BaseTest
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
private QWidgetMetaData defineWidget()
|
||||
{
|
||||
return RecordListWidgetRenderer.widgetMetaDataBuilder("testRecordListWidget")
|
||||
.withTableName(TestUtils.TABLE_NAME_SHAPE)
|
||||
.withMaxRows(20)
|
||||
.withLabel("Some Shapes")
|
||||
.withFilter(new QQueryFilter()
|
||||
.withCriteria("id", QCriteriaOperator.LESS_THAN_OR_EQUALS, "${input.maxShapeId}")
|
||||
.withCriteria("name", QCriteriaOperator.NOT_EQUALS, "Square")
|
||||
.withOrderBy(new QFilterOrderBy("id", false))
|
||||
).getWidgetMetaData();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testValidation() throws QInstanceValidationException
|
||||
{
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QWidgetMetaData widgetMetaData = defineWidget();
|
||||
widgetMetaData.getDefaultValues().remove("tableName");
|
||||
qInstance.addWidget(widgetMetaData);
|
||||
|
||||
assertThatThrownBy(() -> new QInstanceValidator().validate(qInstance))
|
||||
.isInstanceOf(QInstanceValidationException.class)
|
||||
.hasMessageContaining("defaultValue for tableName must be given");
|
||||
}
|
||||
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QWidgetMetaData widgetMetaData = defineWidget();
|
||||
widgetMetaData.getDefaultValues().remove("filter");
|
||||
qInstance.addWidget(widgetMetaData);
|
||||
|
||||
assertThatThrownBy(() -> new QInstanceValidator().validate(qInstance))
|
||||
.isInstanceOf(QInstanceValidationException.class)
|
||||
.hasMessageContaining("defaultValue for filter must be given");
|
||||
}
|
||||
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QWidgetMetaData widgetMetaData = defineWidget();
|
||||
widgetMetaData.getDefaultValues().remove("tableName");
|
||||
widgetMetaData.getDefaultValues().remove("filter");
|
||||
qInstance.addWidget(widgetMetaData);
|
||||
|
||||
assertThatThrownBy(() -> new QInstanceValidator().validate(qInstance))
|
||||
.isInstanceOf(QInstanceValidationException.class)
|
||||
.hasMessageContaining("defaultValue for filter must be given")
|
||||
.hasMessageContaining("defaultValue for tableName must be given");
|
||||
}
|
||||
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QWidgetMetaData widgetMetaData = defineWidget();
|
||||
QQueryFilter filter = (QQueryFilter) widgetMetaData.getDefaultValues().get("filter");
|
||||
filter.addCriteria(new QFilterCriteria("noField", QCriteriaOperator.EQUALS, "noValue"));
|
||||
qInstance.addWidget(widgetMetaData);
|
||||
|
||||
assertThatThrownBy(() -> new QInstanceValidator().validate(qInstance))
|
||||
.isInstanceOf(QInstanceValidationException.class)
|
||||
.hasMessageContaining("Criteria fieldName noField is not a field in this table");
|
||||
}
|
||||
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QWidgetMetaData widgetMetaData = defineWidget();
|
||||
qInstance.addWidget(widgetMetaData);
|
||||
|
||||
//////////////////////////////////
|
||||
// make sure valid setup passes //
|
||||
//////////////////////////////////
|
||||
new QInstanceValidator().validate(qInstance);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testRender() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QWidgetMetaData widgetMetaData = defineWidget();
|
||||
qInstance.addWidget(widgetMetaData);
|
||||
|
||||
TestUtils.insertDefaultShapes(qInstance);
|
||||
TestUtils.insertExtraShapes(qInstance);
|
||||
|
||||
{
|
||||
RecordListWidgetRenderer recordListWidgetRenderer = new RecordListWidgetRenderer();
|
||||
RenderWidgetInput input = new RenderWidgetInput();
|
||||
input.setWidgetMetaData(widgetMetaData);
|
||||
input.setQueryParams(Map.of("maxShapeId", "1"));
|
||||
RenderWidgetOutput output = recordListWidgetRenderer.render(input);
|
||||
|
||||
ChildRecordListData widgetData = (ChildRecordListData) output.getWidgetData();
|
||||
assertEquals(1, widgetData.getTotalRows());
|
||||
assertEquals(1, widgetData.getQueryOutput().getRecords().get(0).getValue("id"));
|
||||
assertEquals("Triangle", widgetData.getQueryOutput().getRecords().get(0).getValue("name"));
|
||||
}
|
||||
|
||||
{
|
||||
RecordListWidgetRenderer recordListWidgetRenderer = new RecordListWidgetRenderer();
|
||||
RenderWidgetInput input = new RenderWidgetInput();
|
||||
input.setWidgetMetaData(widgetMetaData);
|
||||
input.setQueryParams(Map.of("maxShapeId", "4"));
|
||||
RenderWidgetOutput output = recordListWidgetRenderer.render(input);
|
||||
|
||||
ChildRecordListData widgetData = (ChildRecordListData) output.getWidgetData();
|
||||
assertEquals(3, widgetData.getTotalRows());
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// id=2,name=Square was skipped due to NOT_EQUALS Square in the filter //
|
||||
// max-shape-id applied we don't get id=5 or 6 //
|
||||
// and they're ordered as specified in the filter (id desc) //
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
assertEquals(4, widgetData.getQueryOutput().getRecords().get(0).getValue("id"));
|
||||
assertEquals("Rectangle", widgetData.getQueryOutput().getRecords().get(0).getValue("name"));
|
||||
|
||||
assertEquals(3, widgetData.getQueryOutput().getRecords().get(1).getValue("id"));
|
||||
assertEquals("Circle", widgetData.getQueryOutput().getRecords().get(1).getValue("name"));
|
||||
|
||||
assertEquals(1, widgetData.getQueryOutput().getRecords().get(2).getValue("id"));
|
||||
assertEquals("Triangle", widgetData.getQueryOutput().getRecords().get(2).getValue("name"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -364,7 +364,6 @@ class MetaDataActionTest extends BaseTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
@Deprecated(since = "migrated to metaDataCustomizer")
|
||||
void testFilter() throws QException
|
||||
{
|
||||
QCollectingLogger collectingLogger = QLogger.activateCollectingLoggerForClass(MetaDataAction.class);
|
||||
@ -398,7 +397,7 @@ class MetaDataActionTest extends BaseTest
|
||||
// run again (with the same instance as before) to assert about memoization of the filter based on the QInstance //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
new MetaDataAction().execute(new MetaDataInput());
|
||||
assertThat(collectingLogger.getCollectedMessages()).filteredOn(clm -> clm.getMessage().contains("actionCustomizer (via metaDataFilter reference) of type: DenyAllFilter")).hasSize(1);
|
||||
assertThat(collectingLogger.getCollectedMessages()).filteredOn(clm -> clm.getMessage().contains("filter of type: DenyAllFilter")).hasSize(1);
|
||||
|
||||
QLogger.deactivateCollectingLoggerForClass(MetaDataAction.class);
|
||||
|
||||
@ -414,59 +413,6 @@ class MetaDataActionTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testCustomizer() throws QException
|
||||
{
|
||||
QCollectingLogger collectingLogger = QLogger.activateCollectingLoggerForClass(MetaDataAction.class);
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// run default version, and assert tables are found //
|
||||
//////////////////////////////////////////////////////
|
||||
MetaDataOutput result = new MetaDataAction().execute(new MetaDataInput());
|
||||
assertFalse(result.getTables().isEmpty(), "should be some tables");
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// run again (with the same instance as before) to assert about memoization of the filter based on the QInstance //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
new MetaDataAction().execute(new MetaDataInput());
|
||||
assertThat(collectingLogger.getCollectedMessages()).filteredOn(clm -> clm.getMessage().contains("Using new default")).hasSize(1);
|
||||
|
||||
/////////////////////////////////////////////////////////////
|
||||
// set up new instance to use a custom filter, to deny all //
|
||||
/////////////////////////////////////////////////////////////
|
||||
QInstance instance = TestUtils.defineInstance();
|
||||
instance.setMetaDataActionCustomizer(new QCodeReference(DenyAllFilteringCustomizer.class));
|
||||
reInitInstanceInContext(instance);
|
||||
|
||||
/////////////////////////////////////////////////////
|
||||
// re-run, and assert all tables are filtered away //
|
||||
/////////////////////////////////////////////////////
|
||||
result = new MetaDataAction().execute(new MetaDataInput());
|
||||
assertTrue(result.getTables().isEmpty(), "should be no tables");
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// run again (with the same instance as before) to assert about memoization of the filter based on the QInstance //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
new MetaDataAction().execute(new MetaDataInput());
|
||||
assertThat(collectingLogger.getCollectedMessages()).filteredOn(clm -> clm.getMessage().contains("meta-data actionCustomizer of type: DenyAllFilteringCustomizer")).hasSize(1);
|
||||
|
||||
QLogger.deactivateCollectingLoggerForClass(MetaDataAction.class);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// run now with the DefaultNoopMetaDataActionCustomizer, confirm we get tables //
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
instance = TestUtils.defineInstance();
|
||||
instance.setMetaDataActionCustomizer(new QCodeReference(DefaultNoopMetaDataActionCustomizer.class));
|
||||
reInitInstanceInContext(instance);
|
||||
result = new MetaDataAction().execute(new MetaDataInput());
|
||||
assertFalse(result.getTables().isEmpty(), "should be some tables");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@ -516,67 +462,6 @@ class MetaDataActionTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public boolean allowWidget(MetaDataInput input, QWidgetMetaDataInterface widget)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
public static class DenyAllFilteringCustomizer implements MetaDataActionCustomizerInterface
|
||||
{
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public boolean allowTable(MetaDataInput input, QTableMetaData table)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public boolean allowProcess(MetaDataInput input, QProcessMetaData process)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public boolean allowReport(MetaDataInput input, QReportMetaData report)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public boolean allowApp(MetaDataInput input, QAppMetaData app)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
|
@ -38,7 +38,6 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.excel.TestExcelStyler;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.excel.fastexcel.ExcelFastexcelExportStreamer;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.excel.poi.BoldHeaderAndFooterPoiExcelStyler;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.excel.poi.ExcelPoiBasedStreamingExportStreamer;
|
||||
@ -57,7 +56,6 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
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.DisplayFormat;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
@ -492,34 +490,6 @@ public class GenerateReportActionTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void runXlsxWithStyleCustomizer() throws Exception
|
||||
{
|
||||
ReportFormat format = ReportFormat.XLSX;
|
||||
String name = "/tmp/report-customized.xlsx";
|
||||
try(FileOutputStream fileOutputStream = new FileOutputStream(name))
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
qInstance.addReport(defineTableOnlyReport());
|
||||
insertPersonRecords(qInstance);
|
||||
|
||||
ReportInput reportInput = new ReportInput();
|
||||
reportInput.setReportName(REPORT_NAME);
|
||||
reportInput.setReportDestination(new ReportDestination().withReportFormat(format).withReportOutputStream(fileOutputStream));
|
||||
reportInput.setInputValues(Map.of("startDate", LocalDate.of(1970, Month.MAY, 15), "endDate", LocalDate.now()));
|
||||
reportInput.setExportStyleCustomizer(new QCodeReference(TestExcelStyler.class));
|
||||
new GenerateReportAction().execute(reportInput);
|
||||
System.out.println("Wrote File: " + name);
|
||||
|
||||
LocalMacDevUtils.openFile(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -1,76 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.actions.reporting.excel;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.excel.poi.ExcelPoiBasedStreamingStyleCustomizerInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportView;
|
||||
import org.apache.poi.ss.usermodel.CreationHelper;
|
||||
import org.apache.poi.ss.usermodel.Font;
|
||||
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class TestExcelStyler implements ExcelPoiBasedStreamingStyleCustomizerInterface
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public List<Integer> getColumnWidthsForView(QReportView view)
|
||||
{
|
||||
return List.of(60, 50, 40);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public List<String> getMergedRangesForView(QReportView view)
|
||||
{
|
||||
return List.of("A1:B1");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void customizeStyles(Map<String, XSSFCellStyle> styles, XSSFWorkbook workbook, CreationHelper createHelper)
|
||||
{
|
||||
Font font = workbook.createFont();
|
||||
font.setFontHeightInPoints((short) 16);
|
||||
font.setBold(true);
|
||||
XSSFCellStyle cellStyle = workbook.createCellStyle();
|
||||
cellStyle.setFont(font);
|
||||
styles.put("header", cellStyle);
|
||||
}
|
||||
}
|
@ -23,14 +23,10 @@ package com.kingsrook.qqq.backend.core.actions.tables;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
|
||||
@ -54,18 +50,4 @@ class CountActionTest extends BaseTest
|
||||
CountOutput result = new CountAction().execute(request);
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testStaticWrapper() throws QException
|
||||
{
|
||||
TestUtils.insertDefaultShapes(QContext.getQInstance());
|
||||
assertEquals(3, CountAction.execute(TestUtils.TABLE_NAME_SHAPE, null));
|
||||
assertEquals(3, CountAction.execute(TestUtils.TABLE_NAME_SHAPE, new QQueryFilter()));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,23 +30,19 @@ import java.time.LocalTime;
|
||||
import java.time.Month;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
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.AdornmentType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.DateTimeDisplayValueBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.DisplayFormat;
|
||||
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.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
@ -241,102 +237,4 @@ class QValueFormatterTest extends BaseTest
|
||||
assertEquals("2024-04-04 02:12:00 PM CDT", record.getDisplayValue("createDate"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testBlobValuesToDownloadUrls()
|
||||
{
|
||||
byte[] blobBytes = "hello".getBytes();
|
||||
{
|
||||
QTableMetaData table = new QTableMetaData()
|
||||
.withName("testTable")
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||
.withField(new QFieldMetaData("blobField", QFieldType.BLOB)
|
||||
.withFieldAdornment(new FieldAdornment().withType(AdornmentType.FILE_DOWNLOAD)
|
||||
.withValue(AdornmentType.FileDownloadValues.FILE_NAME_FORMAT, "blob-%s.txt")
|
||||
.withValue(AdornmentType.FileDownloadValues.FILE_NAME_FORMAT_FIELDS, new ArrayList<>(List.of("id")))));
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// verify display value gets set to formated file-name + fields //
|
||||
// and raw value becomes URL for downloading the byte //
|
||||
//////////////////////////////////////////////////////////////////
|
||||
QRecord record = new QRecord().withValue("id", 47).withValue("blobField", blobBytes);
|
||||
QValueFormatter.setBlobValuesToDownloadUrls(table, List.of(record));
|
||||
assertEquals("/data/testTable/47/blobField/blob-47.txt", record.getValueString("blobField"));
|
||||
assertEquals("blob-47.txt", record.getDisplayValue("blobField"));
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
// verify that w/ no blob value, we don't do anything //
|
||||
////////////////////////////////////////////////////////
|
||||
QRecord recordWithoutBlobValue = new QRecord().withValue("id", 47);
|
||||
QValueFormatter.setBlobValuesToDownloadUrls(table, List.of(recordWithoutBlobValue));
|
||||
assertNull(recordWithoutBlobValue.getValue("blobField"));
|
||||
assertNull(recordWithoutBlobValue.getDisplayValue("blobField"));
|
||||
}
|
||||
|
||||
{
|
||||
FieldAdornment adornment = new FieldAdornment().withType(AdornmentType.FILE_DOWNLOAD)
|
||||
.withValue(AdornmentType.FileDownloadValues.FILE_NAME_FIELD, "fileName");
|
||||
|
||||
QTableMetaData table = new QTableMetaData()
|
||||
.withName("testTable")
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||
.withField(new QFieldMetaData("fileName", QFieldType.STRING))
|
||||
.withField(new QFieldMetaData("blobField", QFieldType.BLOB)
|
||||
.withFieldAdornment(adornment));
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// here get the file name directly from one field //
|
||||
////////////////////////////////////////////////////
|
||||
QRecord record = new QRecord().withValue("id", 47).withValue("blobField", blobBytes).withValue("fileName", "myBlob.txt");
|
||||
QValueFormatter.setBlobValuesToDownloadUrls(table, List.of(record));
|
||||
assertEquals("/data/testTable/47/blobField/myBlob.txt", record.getValueString("blobField"));
|
||||
assertEquals("myBlob.txt", record.getDisplayValue("blobField"));
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// switch to use dynamic url, rerun, and assert we get the values as they were on the record before the call //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
adornment.withValue(AdornmentType.FileDownloadValues.DOWNLOAD_URL_DYNAMIC, true);
|
||||
record = new QRecord().withValue("id", 47).withValue("blobField", blobBytes).withValue("fileName", "myBlob.txt")
|
||||
.withDisplayValue("blobField:" + AdornmentType.FileDownloadValues.DOWNLOAD_URL_DYNAMIC, "/something-custom/")
|
||||
.withDisplayValue("blobField", "myDisplayValue");
|
||||
QValueFormatter.setBlobValuesToDownloadUrls(table, List.of(record));
|
||||
assertArrayEquals(blobBytes, record.getValueByteArray("blobField"));
|
||||
assertEquals("myDisplayValue", record.getDisplayValue("blobField"));
|
||||
}
|
||||
|
||||
{
|
||||
FieldAdornment adornment = new FieldAdornment().withType(AdornmentType.FILE_DOWNLOAD);
|
||||
|
||||
QTableMetaData table = new QTableMetaData()
|
||||
.withName("testTable")
|
||||
.withLabel("Test Table")
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||
.withField(new QFieldMetaData("blobField", QFieldType.BLOB).withLabel("Blob").withFieldAdornment(adornment));
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// w/o file name format or whatever, generate a file name from table & id & field labels //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
QRecord record = new QRecord().withValue("id", 47).withValue("blobField", blobBytes);
|
||||
QValueFormatter.setBlobValuesToDownloadUrls(table, List.of(record));
|
||||
assertEquals("/data/testTable/47/blobField/Test%20Table%2047%20Blob", record.getValueString("blobField"));
|
||||
assertEquals("Test Table 47 Blob", record.getDisplayValue("blobField"));
|
||||
|
||||
////////////////////////////////////////
|
||||
// add a default extension and re-run //
|
||||
////////////////////////////////////////
|
||||
adornment.withValue(AdornmentType.FileDownloadValues.DEFAULT_EXTENSION, "html");
|
||||
record = new QRecord().withValue("id", 47).withValue("blobField", blobBytes);
|
||||
QValueFormatter.setBlobValuesToDownloadUrls(table, List.of(record));
|
||||
assertEquals("/data/testTable/47/blobField/Test%20Table%2047%20Blob.html", record.getValueString("blobField"));
|
||||
assertEquals("Test Table 47 Blob.html", record.getDisplayValue("blobField"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -27,8 +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.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.instances.enrichment.testplugins.TestEnricherPlugin;
|
||||
import com.kingsrook.qqq.backend.core.instances.enrichment.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;
|
||||
@ -596,31 +595,27 @@ class QInstanceEnricherTest extends BaseTest
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
|
||||
QInstanceEnricher.addEnricherPlugin(new TestEnricherPlugin());
|
||||
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")));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testDiscoverAndAddPlugins() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
new QInstanceEnricher(qInstance).enrich();
|
||||
qInstance.getTables().values().forEach(table -> table.getFields().values().forEach(field -> assertThat(field.getLabel()).doesNotEndWith("Plugged")));
|
||||
|
||||
qInstance = TestUtils.defineInstance();
|
||||
QInstanceEnricher.discoverAndAddPluginsInPackage(getClass().getPackageName() + ".enrichment.testplugins");
|
||||
new QInstanceEnricher(qInstance).enrich();
|
||||
qInstance.getTables().values().forEach(table -> table.getFields().values().forEach(field -> assertThat(field.getLabel()).endsWith("Plugged")));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -40,7 +40,6 @@ import com.kingsrook.qqq.backend.core.actions.dashboard.PersonsByCreateDateBarCh
|
||||
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.metadata.AllowAllMetaDataFilter;
|
||||
import com.kingsrook.qqq.backend.core.actions.metadata.DefaultNoopMetaDataActionCustomizer;
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.CancelProcessActionTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
|
||||
import com.kingsrook.qqq.backend.core.actions.reporting.customizers.ReportCustomRecordSourceInterface;
|
||||
@ -161,20 +160,6 @@ public class QInstanceValidatorTest extends BaseTest
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testMetaDataActionCustomizer()
|
||||
{
|
||||
assertValidationFailureReasons((qInstance) -> qInstance.setMetaDataActionCustomizer(new QCodeReference(QInstanceValidator.class)),
|
||||
"Instance metaDataActionCustomizer CodeReference is not of the expected type");
|
||||
|
||||
assertValidationSuccess((qInstance) -> qInstance.setMetaDataActionCustomizer(new QCodeReference(DefaultNoopMetaDataActionCustomizer.class)));
|
||||
assertValidationSuccess((qInstance) -> qInstance.setMetaDataActionCustomizer(null));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Test an instance with null backends - should throw.
|
||||
|
@ -1,48 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.enrichment.testplugins;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.instances.enrichment.plugins.QInstanceEnricherPluginInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class TestEnricherPlugin implements QInstanceEnricherPluginInterface<QFieldMetaData>
|
||||
{
|
||||
|
||||
/***************************************************************************
|
||||
**
|
||||
***************************************************************************/
|
||||
@Override
|
||||
public void enrich(QFieldMetaData field, QInstance qInstance)
|
||||
{
|
||||
if(field != null)
|
||||
{
|
||||
field.setLabel(field.getLabel() + " Plugged");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -66,7 +66,7 @@ class NowWithOffsetTest extends BaseTest
|
||||
assertThat(twoWeeksFromNowMillis).isCloseTo(now + (14 * DAY_IN_MILLIS), allowedDiff);
|
||||
|
||||
long oneMonthAgoMillis = ((Instant) NowWithOffset.minus(1, ChronoUnit.MONTHS).evaluate(dateTimeField)).toEpochMilli();
|
||||
assertThat(oneMonthAgoMillis).isCloseTo(now - (30 * DAY_IN_MILLIS), allowedDiffPlusTwoDays); // two days, to work on 3/1...
|
||||
assertThat(oneMonthAgoMillis).isCloseTo(now - (30 * DAY_IN_MILLIS), allowedDiffPlusOneDay);
|
||||
|
||||
long twoMonthsFromNowMillis = ((Instant) NowWithOffset.plus(2, ChronoUnit.MONTHS).evaluate(dateTimeField)).toEpochMilli();
|
||||
assertThat(twoMonthsFromNowMillis).isCloseTo(now + (60 * DAY_IN_MILLIS), allowedDiffPlusTwoDays);
|
||||
|
@ -41,7 +41,6 @@ import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
@ -567,22 +566,4 @@ class QRecordEntityTest extends BaseTest
|
||||
assertEquals(0, order.getLineItems().size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTableName() throws QException
|
||||
{
|
||||
assertEquals(Item.TABLE_NAME, QRecordEntity.getTableName(Item.class));
|
||||
assertEquals(Item.TABLE_NAME, Item.getTableName(Item.class));
|
||||
assertEquals(Item.TABLE_NAME, new Item().tableName());
|
||||
|
||||
//////////////////////////////////
|
||||
// no TABLE_NAME in Order class //
|
||||
//////////////////////////////////
|
||||
assertThatThrownBy(() -> Order.getTableName(Order.class));
|
||||
}
|
||||
|
||||
}
|
@ -1,151 +0,0 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2025. 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.model.metadata.qbits;
|
||||
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
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.qbits.testqbit.TestQBitConfig;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.qbits.testqbit.TestQBitProducer;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.qbits.testqbit.metadata.OtherTableMetaDataProducer;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.qbits.testqbit.metadata.SomeTableMetaDataProducer;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for QBitProducer
|
||||
*******************************************************************************/
|
||||
class QBitProducerTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test() throws QException
|
||||
{
|
||||
TestQBitConfig config = new TestQBitConfig()
|
||||
.withOtherTableConfig(ProvidedOrSuppliedTableConfig.provideTableUsingBackendNamed(TestUtils.MEMORY_BACKEND_NAME))
|
||||
.withIsSomeTableEnabled(true)
|
||||
.withSomeSetting("yes")
|
||||
.withTableMetaDataCustomizer((i, table) ->
|
||||
{
|
||||
if(table.getBackendName() == null)
|
||||
{
|
||||
table.setBackendName(TestUtils.DEFAULT_BACKEND_NAME);
|
||||
}
|
||||
|
||||
table.addField(new QFieldMetaData("custom", QFieldType.STRING));
|
||||
|
||||
return (table);
|
||||
});
|
||||
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
new TestQBitProducer().withTestQBitConfig(config).produce(qInstance);
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// OtherTable should have been provided by the qbit, with the backend name we told it above (MEMORY) //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QTableMetaData otherTable = qInstance.getTable(OtherTableMetaDataProducer.NAME);
|
||||
assertNotNull(otherTable);
|
||||
assertEquals(TestUtils.MEMORY_BACKEND_NAME, otherTable.getBackendName());
|
||||
assertNotNull(otherTable.getField("custom"));
|
||||
|
||||
QBitMetaData sourceQBit = otherTable.getSourceQBit();
|
||||
assertEquals("testQBit", sourceQBit.getArtifactId());
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SomeTable should have been provided, w/ backend name set by the customizer //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
QTableMetaData someTable = qInstance.getTable(SomeTableMetaDataProducer.NAME);
|
||||
assertNotNull(someTable);
|
||||
assertEquals(TestUtils.DEFAULT_BACKEND_NAME, someTable.getBackendName());
|
||||
assertNotNull(otherTable.getField("custom"));
|
||||
|
||||
TestQBitConfig qBitConfig = (TestQBitConfig) someTable.getSourceQBitConfig();
|
||||
assertEquals("yes", qBitConfig.getSomeSetting());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testDisableThings() throws QException
|
||||
{
|
||||
TestQBitConfig config = new TestQBitConfig()
|
||||
.withOtherTableConfig(ProvidedOrSuppliedTableConfig.useSuppliedTaleNamed(TestUtils.TABLE_NAME_PERSON_MEMORY))
|
||||
.withIsSomeTableEnabled(false);
|
||||
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
new TestQBitProducer().withTestQBitConfig(config).produce(qInstance);
|
||||
|
||||
//////////////////////////////////////
|
||||
// neither table should be produced //
|
||||
//////////////////////////////////////
|
||||
QTableMetaData otherTable = qInstance.getTable(OtherTableMetaDataProducer.NAME);
|
||||
assertNull(otherTable);
|
||||
|
||||
QTableMetaData someTable = qInstance.getTable(SomeTableMetaDataProducer.NAME);
|
||||
assertNull(someTable);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testValidationErrors() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
TestQBitConfig config = new TestQBitConfig();
|
||||
|
||||
assertThatThrownBy(() -> new TestQBitProducer().withTestQBitConfig(config).produce(qInstance))
|
||||
.isInstanceOf(QBitConfigValidationException.class)
|
||||
.hasMessageContaining("otherTableConfig must be set")
|
||||
.hasMessageContaining("isSomeTableEnabled must be set");
|
||||
qInstance.setQBits(new LinkedHashMap<>());
|
||||
|
||||
config.setIsSomeTableEnabled(true);
|
||||
assertThatThrownBy(() -> new TestQBitProducer().withTestQBitConfig(config).produce(qInstance))
|
||||
.isInstanceOf(QBitConfigValidationException.class)
|
||||
.hasMessageContaining("otherTableConfig must be set");
|
||||
qInstance.setQBits(new LinkedHashMap<>());
|
||||
|
||||
config.setOtherTableConfig(ProvidedOrSuppliedTableConfig.useSuppliedTaleNamed(TestUtils.TABLE_NAME_PERSON_MEMORY));
|
||||
new TestQBitProducer().withTestQBitConfig(config).produce(qInstance);
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user