mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
Add RunReportForRecordProcess; 1st version of AbstractProcessMetaDataBuilder
This commit is contained in:
@ -22,6 +22,7 @@
|
|||||||
package com.kingsrook.qqq.backend.core.actions.dashboard;
|
package com.kingsrook.qqq.backend.core.actions.dashboard;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||||
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
import com.kingsrook.qqq.backend.core.actions.customizers.QCodeLoader;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
||||||
@ -40,6 +41,8 @@ public class RenderWidgetAction
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public RenderWidgetOutput execute(RenderWidgetInput input) throws QException
|
public RenderWidgetOutput execute(RenderWidgetInput input) throws QException
|
||||||
{
|
{
|
||||||
|
ActionHelper.validateSession(input);
|
||||||
|
|
||||||
AbstractWidgetRenderer widgetRenderer = QCodeLoader.getAdHoc(AbstractWidgetRenderer.class, input.getWidgetMetaData().getCodeReference());
|
AbstractWidgetRenderer widgetRenderer = QCodeLoader.getAdHoc(AbstractWidgetRenderer.class, input.getWidgetMetaData().getCodeReference());
|
||||||
return (widgetRenderer.render(input));
|
return (widgetRenderer.render(input));
|
||||||
}
|
}
|
||||||
|
@ -31,13 +31,16 @@ import java.util.Map;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationHandler;
|
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.customizers.TableCustomizers;
|
||||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||||
import com.kingsrook.qqq.backend.core.actions.values.QCustomPossibleValueProvider;
|
import com.kingsrook.qqq.backend.core.actions.values.QCustomPossibleValueProvider;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
||||||
|
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.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.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeType;
|
||||||
@ -50,6 +53,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaD
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
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.processes.QStepMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.queues.SQSQueueProviderMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.queues.SQSQueueProviderMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportDataSource;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportView;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
||||||
@ -118,6 +123,7 @@ public class QInstanceValidator
|
|||||||
validateAutomationProviders(qInstance);
|
validateAutomationProviders(qInstance);
|
||||||
validateTables(qInstance);
|
validateTables(qInstance);
|
||||||
validateProcesses(qInstance);
|
validateProcesses(qInstance);
|
||||||
|
validateReports(qInstance);
|
||||||
validateApps(qInstance);
|
validateApps(qInstance);
|
||||||
validatePossibleValueSources(qInstance);
|
validatePossibleValueSources(qInstance);
|
||||||
validateQueuesAndProviders(qInstance);
|
validateQueuesAndProviders(qInstance);
|
||||||
@ -186,6 +192,8 @@ public class QInstanceValidator
|
|||||||
qInstance.getBackends().forEach((backendName, backend) ->
|
qInstance.getBackends().forEach((backendName, backend) ->
|
||||||
{
|
{
|
||||||
assertCondition(Objects.equals(backendName, backend.getName()), "Inconsistent naming for backend: " + backendName + "/" + backend.getName() + ".");
|
assertCondition(Objects.equals(backendName, backend.getName()), "Inconsistent naming for backend: " + backendName + "/" + backend.getName() + ".");
|
||||||
|
|
||||||
|
backend.performValidation(this);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,6 +266,8 @@ public class QInstanceValidator
|
|||||||
//////////////////////////////////////////
|
//////////////////////////////////////////
|
||||||
Set<String> fieldNamesInSections = new HashSet<>();
|
Set<String> fieldNamesInSections = new HashSet<>();
|
||||||
QFieldSection tier1Section = null;
|
QFieldSection tier1Section = null;
|
||||||
|
Set<String> usedSectionNames = new HashSet<>();
|
||||||
|
Set<String> usedSectionLabels = new HashSet<>();
|
||||||
if(table.getSections() != null)
|
if(table.getSections() != null)
|
||||||
{
|
{
|
||||||
for(QFieldSection section : table.getSections())
|
for(QFieldSection section : table.getSections())
|
||||||
@ -268,6 +278,12 @@ public class QInstanceValidator
|
|||||||
assertCondition(tier1Section == null, "Table " + tableName + " has more than 1 section listed as Tier 1");
|
assertCondition(tier1Section == null, "Table " + tableName + " has more than 1 section listed as Tier 1");
|
||||||
tier1Section = section;
|
tier1Section = section;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assertCondition(!usedSectionNames.contains(section.getName()), "Table " + tableName + " has more than 1 section named " + section.getName());
|
||||||
|
usedSectionNames.add(section.getName());
|
||||||
|
|
||||||
|
assertCondition(!usedSectionLabels.contains(section.getLabel()), "Table " + tableName + " has more than 1 section labeled " + section.getLabel());
|
||||||
|
usedSectionLabels.add(section.getLabel());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -716,6 +732,133 @@ public class QInstanceValidator
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private void validateReports(QInstance qInstance)
|
||||||
|
{
|
||||||
|
if(CollectionUtils.nullSafeHasContents(qInstance.getReports()))
|
||||||
|
{
|
||||||
|
qInstance.getReports().forEach((reportName, report) ->
|
||||||
|
{
|
||||||
|
assertCondition(Objects.equals(reportName, report.getName()), "Inconsistent naming for report: " + reportName + "/" + report.getName() + ".");
|
||||||
|
validateAppChildHasValidParentAppName(qInstance, report);
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
// validate dataSources in the report //
|
||||||
|
////////////////////////////////////////
|
||||||
|
Set<String> usedDataSourceNames = new HashSet<>();
|
||||||
|
if(assertCondition(CollectionUtils.nullSafeHasContents(report.getDataSources()), "At least 1 data source must be defined in report " + reportName + "."))
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
for(QReportDataSource dataSource : report.getDataSources())
|
||||||
|
{
|
||||||
|
assertCondition(StringUtils.hasContent(dataSource.getName()), "Missing name for a dataSource at index " + index + " in report " + reportName);
|
||||||
|
index++;
|
||||||
|
|
||||||
|
assertCondition(!usedDataSourceNames.contains(dataSource.getName()), "More than one dataSource with name " + dataSource.getName() + " in report " + reportName);
|
||||||
|
usedDataSourceNames.add(dataSource.getName());
|
||||||
|
|
||||||
|
String dataSourceErrorPrefix = "Report " + reportName + " data source " + dataSource.getName() + " ";
|
||||||
|
|
||||||
|
if(StringUtils.hasContent(dataSource.getSourceTable()))
|
||||||
|
{
|
||||||
|
assertCondition(dataSource.getStaticDataSupplier() == null, dataSourceErrorPrefix + "has both a sourceTable and a staticDataSupplier (exactly 1 is required).");
|
||||||
|
if(assertCondition(qInstance.getTable(dataSource.getSourceTable()) != null, dataSourceErrorPrefix + "source table " + dataSource.getSourceTable() + " is not a table in this instance."))
|
||||||
|
{
|
||||||
|
if(dataSource.getQueryFilter() != null)
|
||||||
|
{
|
||||||
|
validateQueryFilter("In " + dataSourceErrorPrefix + "query filter - ", qInstance.getTable(dataSource.getSourceTable()), dataSource.getQueryFilter());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(dataSource.getStaticDataSupplier() != null)
|
||||||
|
{
|
||||||
|
validateSimpleCodeReference(dataSourceErrorPrefix, dataSource.getStaticDataSupplier(), Supplier.class);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
errors.add(dataSourceErrorPrefix + "does not have a sourceTable or a staticDataSupplier (exactly 1 is required).");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////
|
||||||
|
// validate dataSources in the report //
|
||||||
|
////////////////////////////////////////
|
||||||
|
if(assertCondition(CollectionUtils.nullSafeHasContents(report.getViews()), "At least 1 view must be defined in report " + reportName + "."))
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
Set<String> usedViewNames = new HashSet<>();
|
||||||
|
for(QReportView view : report.getViews())
|
||||||
|
{
|
||||||
|
assertCondition(StringUtils.hasContent(view.getName()), "Missing name for a view at index " + index + " in report " + reportName);
|
||||||
|
index++;
|
||||||
|
|
||||||
|
assertCondition(!usedViewNames.contains(view.getName()), "More than one view with name " + view.getName() + " in report " + reportName);
|
||||||
|
usedViewNames.add(view.getName());
|
||||||
|
|
||||||
|
String viewErrorPrefix = "Report " + reportName + " view " + view.getName() + " ";
|
||||||
|
assertCondition(view.getType() != null, viewErrorPrefix + " is missing its type.");
|
||||||
|
if(assertCondition(StringUtils.hasContent(view.getDataSourceName()), viewErrorPrefix + " is missing a dataSourceName"))
|
||||||
|
{
|
||||||
|
assertCondition(usedDataSourceNames.contains(view.getDataSourceName()), viewErrorPrefix + " has an unrecognized dataSourceName: " + view.getDataSourceName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(StringUtils.hasContent(view.getVarianceDataSourceName()))
|
||||||
|
{
|
||||||
|
assertCondition(usedDataSourceNames.contains(view.getVarianceDataSourceName()), viewErrorPrefix + " has an unrecognized varianceDataSourceName: " + view.getVarianceDataSourceName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// actually, this is okay if there's a customizer, so...
|
||||||
|
assertCondition(CollectionUtils.nullSafeHasContents(view.getColumns()), viewErrorPrefix + " does not have any columns.");
|
||||||
|
|
||||||
|
// todo - all these too...
|
||||||
|
// view.getPivotFields();
|
||||||
|
// view.getViewCustomizer(); // validate code ref
|
||||||
|
// view.getRecordTransformStep(); // validate code ref
|
||||||
|
// view.getOrderByFields(); // make sure valid field names?
|
||||||
|
// view.getIncludePivotSubTotals(); // only for pivot type
|
||||||
|
// view.getTitleFormat(); view.getTitleFields(); // validate these match?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private void validateQueryFilter(String context, QTableMetaData table, QQueryFilter queryFilter)
|
||||||
|
{
|
||||||
|
for(QFilterCriteria criterion : CollectionUtils.nonNullList(queryFilter.getCriteria()))
|
||||||
|
{
|
||||||
|
if(assertCondition(StringUtils.hasContent(criterion.getFieldName()), context + "Missing fieldName for a criteria"))
|
||||||
|
{
|
||||||
|
assertNoException(() -> table.getField(criterion.getFieldName()), context + "Criteria fieldName " + criterion.getFieldName() + " is not a field in this table.");
|
||||||
|
}
|
||||||
|
assertCondition(criterion.getOperator() != null, context + "Missing operator for a criteria on fieldName " + criterion.getFieldName());
|
||||||
|
assertCondition(criterion.getValues() != null, context + "Missing values for a criteria on fieldName " + criterion.getFieldName()); // todo - what about ops w/ no value (BLANK)
|
||||||
|
}
|
||||||
|
|
||||||
|
for(QFilterOrderBy orderBy : CollectionUtils.nonNullList(queryFilter.getOrderBys()))
|
||||||
|
{
|
||||||
|
if(assertCondition(StringUtils.hasContent(orderBy.getFieldName()), context + "Missing fieldName for an orderBy"))
|
||||||
|
{
|
||||||
|
assertNoException(() -> table.getField(orderBy.getFieldName()), context + "OrderBy fieldName " + orderBy.getFieldName() + " is not a field in this table.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(QQueryFilter subFilter : CollectionUtils.nonNullList(queryFilter.getSubFilters()))
|
||||||
|
{
|
||||||
|
validateQueryFilter(context, table, subFilter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -978,7 +1121,7 @@ public class QInstanceValidator
|
|||||||
** But if it's false, add the provided message to the list of errors (and return false,
|
** But if it's false, add the provided message to the list of errors (and return false,
|
||||||
** e.g., in case you need to stop evaluating rules to avoid exceptions).
|
** e.g., in case you need to stop evaluating rules to avoid exceptions).
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private boolean assertCondition(boolean condition, String message)
|
public boolean assertCondition(boolean condition, String message)
|
||||||
{
|
{
|
||||||
if(!condition)
|
if(!condition)
|
||||||
{
|
{
|
||||||
@ -1035,4 +1178,15 @@ public class QInstanceValidator
|
|||||||
LOG.info("Validation warning: " + message);
|
LOG.info("Validation warning: " + message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for errors
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public List<String> getErrors()
|
||||||
|
{
|
||||||
|
return errors;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import java.util.Arrays;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.serialization.QBackendMetaDataDeserializer;
|
import com.kingsrook.qqq.backend.core.model.metadata.serialization.QBackendMetaDataDeserializer;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||||
@ -321,4 +322,15 @@ public class QBackendMetaData
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void performValidation(QInstanceValidator qInstanceValidator)
|
||||||
|
{
|
||||||
|
////////////////////////
|
||||||
|
// noop in base class //
|
||||||
|
////////////////////////
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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.model.metadata.processes;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class AbstractProcessMetaDataBuilder
|
||||||
|
{
|
||||||
|
protected QProcessMetaData processMetaData;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public AbstractProcessMetaDataBuilder(QProcessMetaData processMetaData)
|
||||||
|
{
|
||||||
|
this.processMetaData = processMetaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for processMetaData
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QProcessMetaData getProcessMetaData()
|
||||||
|
{
|
||||||
|
return processMetaData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
protected void setInputFieldDefaultValue(String fieldName, Serializable value)
|
||||||
|
{
|
||||||
|
processMetaData.getInputFields().stream()
|
||||||
|
.filter(f -> f.getName().equals(fieldName)).findFirst()
|
||||||
|
.ifPresent(f -> f.setDefaultValue(value));
|
||||||
|
}
|
||||||
|
}
|
@ -90,10 +90,17 @@ public class StreamedETLExecuteStep extends BaseStreamedETLStep implements Backe
|
|||||||
updateRecordsWithDisplayValuesAndPossibleValues(runBackendStepInput, loadedRecordList);
|
updateRecordsWithDisplayValuesAndPossibleValues(runBackendStepInput, loadedRecordList);
|
||||||
runBackendStepOutput.setRecords(loadedRecordList);
|
runBackendStepOutput.setRecords(loadedRecordList);
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// get the process summary from the ... transform step? the load step? each knows some... todo? //
|
// get the process summary from the load step, if it's a summary-provider -- else, use the transform step (which is always a provider) //
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if(loadStep instanceof ProcessSummaryProviderInterface provider)
|
||||||
|
{
|
||||||
|
runBackendStepOutput.addValue(StreamedETLWithFrontendProcess.FIELD_PROCESS_SUMMARY, provider.doGetProcessSummary(runBackendStepOutput, true));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
runBackendStepOutput.addValue(StreamedETLWithFrontendProcess.FIELD_PROCESS_SUMMARY, transformStep.doGetProcessSummary(runBackendStepOutput, true));
|
runBackendStepOutput.addValue(StreamedETLWithFrontendProcess.FIELD_PROCESS_SUMMARY, transformStep.doGetProcessSummary(runBackendStepOutput, true));
|
||||||
|
}
|
||||||
|
|
||||||
transformStep.postRun(runBackendStepInput, runBackendStepOutput);
|
transformStep.postRun(runBackendStepInput, runBackendStepOutput);
|
||||||
loadStep.postRun(runBackendStepInput, runBackendStepOutput);
|
loadStep.postRun(runBackendStepInput, runBackendStepOutput);
|
||||||
|
@ -23,11 +23,16 @@ package com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwit
|
|||||||
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.AbstractProcessMetaDataBuilder;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData;
|
||||||
@ -131,8 +136,8 @@ public class StreamedETLWithFrontendProcess
|
|||||||
.withField(new QFieldMetaData(FIELD_DESTINATION_TABLE, QFieldType.STRING).withDefaultValue(defaultFieldValues.get(FIELD_DESTINATION_TABLE)))
|
.withField(new QFieldMetaData(FIELD_DESTINATION_TABLE, QFieldType.STRING).withDefaultValue(defaultFieldValues.get(FIELD_DESTINATION_TABLE)))
|
||||||
.withField(new QFieldMetaData(FIELD_SUPPORTS_FULL_VALIDATION, QFieldType.BOOLEAN).withDefaultValue(defaultFieldValues.getOrDefault(FIELD_SUPPORTS_FULL_VALIDATION, true)))
|
.withField(new QFieldMetaData(FIELD_SUPPORTS_FULL_VALIDATION, QFieldType.BOOLEAN).withDefaultValue(defaultFieldValues.getOrDefault(FIELD_SUPPORTS_FULL_VALIDATION, true)))
|
||||||
.withField(new QFieldMetaData(FIELD_DEFAULT_QUERY_FILTER, QFieldType.STRING).withDefaultValue(defaultFieldValues.get(FIELD_DEFAULT_QUERY_FILTER)))
|
.withField(new QFieldMetaData(FIELD_DEFAULT_QUERY_FILTER, QFieldType.STRING).withDefaultValue(defaultFieldValues.get(FIELD_DEFAULT_QUERY_FILTER)))
|
||||||
.withField(new QFieldMetaData(FIELD_EXTRACT_CODE, QFieldType.STRING).withDefaultValue(new QCodeReference(extractStepClass)))
|
.withField(new QFieldMetaData(FIELD_EXTRACT_CODE, QFieldType.STRING).withDefaultValue(extractStepClass == null ? null : new QCodeReference(extractStepClass)))
|
||||||
.withField(new QFieldMetaData(FIELD_TRANSFORM_CODE, QFieldType.STRING).withDefaultValue(new QCodeReference(transformStepClass)))
|
.withField(new QFieldMetaData(FIELD_TRANSFORM_CODE, QFieldType.STRING).withDefaultValue(transformStepClass == null ? null : new QCodeReference(transformStepClass)))
|
||||||
.withField(new QFieldMetaData(FIELD_PREVIEW_MESSAGE, QFieldType.STRING).withDefaultValue(defaultFieldValues.getOrDefault(FIELD_PREVIEW_MESSAGE, DEFAULT_PREVIEW_MESSAGE_FOR_INSERT)))
|
.withField(new QFieldMetaData(FIELD_PREVIEW_MESSAGE, QFieldType.STRING).withDefaultValue(defaultFieldValues.getOrDefault(FIELD_PREVIEW_MESSAGE, DEFAULT_PREVIEW_MESSAGE_FOR_INSERT)))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -153,7 +158,7 @@ public class StreamedETLWithFrontendProcess
|
|||||||
.withName(STEP_NAME_EXECUTE)
|
.withName(STEP_NAME_EXECUTE)
|
||||||
.withCode(new QCodeReference(StreamedETLExecuteStep.class))
|
.withCode(new QCodeReference(StreamedETLExecuteStep.class))
|
||||||
.withInputData(new QFunctionInputMetaData()
|
.withInputData(new QFunctionInputMetaData()
|
||||||
.withField(new QFieldMetaData(FIELD_LOAD_CODE, QFieldType.STRING).withDefaultValue(new QCodeReference(loadStepClass))))
|
.withField(new QFieldMetaData(FIELD_LOAD_CODE, QFieldType.STRING).withDefaultValue(loadStepClass == null ? null : new QCodeReference(loadStepClass))))
|
||||||
.withOutputMetaData(new QFunctionOutputMetaData()
|
.withOutputMetaData(new QFunctionOutputMetaData()
|
||||||
.withField(new QFieldMetaData(FIELD_PROCESS_SUMMARY, QFieldType.STRING))
|
.withField(new QFieldMetaData(FIELD_PROCESS_SUMMARY, QFieldType.STRING))
|
||||||
);
|
);
|
||||||
@ -169,4 +174,204 @@ public class StreamedETLWithFrontendProcess
|
|||||||
.addStep(executeStep)
|
.addStep(executeStep)
|
||||||
.addStep(resultStep);
|
.addStep(resultStep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static Builder processMetaDataBuilder()
|
||||||
|
{
|
||||||
|
return (new Builder(defineProcessMetaData(null, null, null, Collections.emptyMap())));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static class Builder extends AbstractProcessMetaDataBuilder
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder(QProcessMetaData processMetaData)
|
||||||
|
{
|
||||||
|
super(processMetaData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for extractStepClass
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withExtractStepClass(Class<? extends AbstractExtractStep> extractStepClass)
|
||||||
|
{
|
||||||
|
setInputFieldDefaultValue(FIELD_EXTRACT_CODE, new QCodeReference(extractStepClass));
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for transformStepClass
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withTransformStepClass(Class<? extends AbstractTransformStep> transformStepClass)
|
||||||
|
{
|
||||||
|
setInputFieldDefaultValue(FIELD_TRANSFORM_CODE, new QCodeReference(transformStepClass));
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for loadStepClass
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withLoadStepClass(Class<? extends AbstractLoadStep> loadStepClass)
|
||||||
|
{
|
||||||
|
setInputFieldDefaultValue(FIELD_LOAD_CODE, new QCodeReference(loadStepClass));
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for sourceTable
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withSourceTable(String sourceTable)
|
||||||
|
{
|
||||||
|
setInputFieldDefaultValue(FIELD_SOURCE_TABLE, sourceTable);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for destinationTable
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withDestinationTable(String destinationTable)
|
||||||
|
{
|
||||||
|
setInputFieldDefaultValue(FIELD_DESTINATION_TABLE, destinationTable);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for supportsFullValidation
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withSupportsFullValidation(Boolean supportsFullValidation)
|
||||||
|
{
|
||||||
|
setInputFieldDefaultValue(FIELD_SUPPORTS_FULL_VALIDATION, supportsFullValidation);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for doFullValidation
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withDoFullValidation(Boolean doFullValidation)
|
||||||
|
{
|
||||||
|
setInputFieldDefaultValue(FIELD_DO_FULL_VALIDATION, doFullValidation);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for defaultQueryFilter
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withDefaultQueryFilter(QQueryFilter defaultQueryFilter)
|
||||||
|
{
|
||||||
|
setInputFieldDefaultValue(FIELD_DEFAULT_QUERY_FILTER, defaultQueryFilter);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for previewMessage
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withPreviewMessage(String previewMessage)
|
||||||
|
{
|
||||||
|
setInputFieldDefaultValue(FIELD_PREVIEW_MESSAGE, previewMessage);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for name
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withName(String name)
|
||||||
|
{
|
||||||
|
processMetaData.setName(name);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for label
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withLabel(String name)
|
||||||
|
{
|
||||||
|
processMetaData.setLabel(name);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for tableName
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withTableName(String tableName)
|
||||||
|
{
|
||||||
|
processMetaData.setTableName(tableName);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for icon
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withIcon(QIcon icon)
|
||||||
|
{
|
||||||
|
processMetaData.setIcon(icon);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withReviewStepRecordFields(List<QFieldMetaData> fieldList)
|
||||||
|
{
|
||||||
|
QFrontendStepMetaData reviewStep = processMetaData.getFrontendStep(StreamedETLWithFrontendProcess.STEP_NAME_REVIEW);
|
||||||
|
for(QFieldMetaData fieldMetaData : fieldList)
|
||||||
|
{
|
||||||
|
reviewStep.withRecordListField(fieldMetaData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutp
|
|||||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
|
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportFormat;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportInput;
|
import com.kingsrook.qqq.backend.core.model.actions.reporting.ReportInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -46,6 +47,10 @@ import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class ExecuteReportStep implements BackendStep
|
public class ExecuteReportStep implements BackendStep
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
@Override
|
@Override
|
||||||
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||||
{
|
{
|
||||||
@ -70,10 +75,9 @@ public class ExecuteReportStep implements BackendStep
|
|||||||
|
|
||||||
new GenerateReportAction().execute(reportInput);
|
new GenerateReportAction().execute(reportInput);
|
||||||
|
|
||||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HHmm").withZone(ZoneId.systemDefault());
|
String downloadFileBaseName = getDownloadFileBaseName(runBackendStepInput, report);
|
||||||
String datePart = formatter.format(Instant.now());
|
|
||||||
|
|
||||||
runBackendStepOutput.addValue("downloadFileName", report.getLabel() + " " + datePart + ".xlsx");
|
runBackendStepOutput.addValue("downloadFileName", downloadFileBaseName + ".xlsx");
|
||||||
runBackendStepOutput.addValue("serverFilePath", tmpFile.getCanonicalPath());
|
runBackendStepOutput.addValue("serverFilePath", tmpFile.getCanonicalPath());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -82,4 +86,26 @@ public class ExecuteReportStep implements BackendStep
|
|||||||
throw (new QException("Error running report", e));
|
throw (new QException("Error running report", e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private String getDownloadFileBaseName(RunBackendStepInput runBackendStepInput, QReportMetaData report)
|
||||||
|
{
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd-HHmm").withZone(ZoneId.systemDefault());
|
||||||
|
String datePart = formatter.format(Instant.now());
|
||||||
|
|
||||||
|
String downloadFileBaseName = runBackendStepInput.getValueString("downloadFileBaseName");
|
||||||
|
if(!StringUtils.hasContent(downloadFileBaseName))
|
||||||
|
{
|
||||||
|
downloadFileBaseName = report.getLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
downloadFileBaseName = downloadFileBaseName.replaceAll("/", "-");
|
||||||
|
|
||||||
|
return (downloadFileBaseName + " - " + datePart);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* 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.processes.implementations.reports;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||||
|
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.get.GetInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Version of PrepareReportStep for a report that runs off a single record.
|
||||||
|
*******************************************************************************/
|
||||||
|
public class PrepareReportForRecordStep extends PrepareReportStep
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||||
|
{
|
||||||
|
super.run(runBackendStepInput, runBackendStepOutput);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// look for the recordId having been posted to the process - error if not found //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
|
Serializable recordId = null;
|
||||||
|
if("recordIds".equals(runBackendStepInput.getValueString("recordsParam")))
|
||||||
|
{
|
||||||
|
String recordIdsString = runBackendStepInput.getValueString("recordIds");
|
||||||
|
String[] recordIdsArray = recordIdsString.split(",");
|
||||||
|
if(recordIdsArray.length != 1)
|
||||||
|
{
|
||||||
|
throw (new QUserFacingException("Exactly 1 record must be selected as input to this report."));
|
||||||
|
}
|
||||||
|
|
||||||
|
recordId = recordIdsArray[0];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw (new QUserFacingException("No record was selected as input to this report."));
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// look for the recordI input field on the process - put the input recordId in that field. //
|
||||||
|
// then remove that input field from the process's inputFieldList //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
ArrayList<QFieldMetaData> inputFieldList = (ArrayList<QFieldMetaData>) runBackendStepOutput.getValue("inputFieldList");
|
||||||
|
if(CollectionUtils.nullSafeHasContents(inputFieldList))
|
||||||
|
{
|
||||||
|
Iterator<QFieldMetaData> inputFieldListIterator = inputFieldList.iterator();
|
||||||
|
while(inputFieldListIterator.hasNext())
|
||||||
|
{
|
||||||
|
QFieldMetaData fieldMetaData = inputFieldListIterator.next();
|
||||||
|
if(fieldMetaData.getName().equals(RunReportForRecordProcess.FIELD_RECORD_ID))
|
||||||
|
{
|
||||||
|
runBackendStepOutput.addValue(RunReportForRecordProcess.FIELD_RECORD_ID, recordId);
|
||||||
|
inputFieldListIterator.remove();
|
||||||
|
runBackendStepOutput.addValue("inputFieldList", inputFieldList);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GetInput getInput = new GetInput(runBackendStepInput.getInstance());
|
||||||
|
getInput.setSession(runBackendStepInput.getSession());
|
||||||
|
getInput.setTableName(runBackendStepInput.getTableName());
|
||||||
|
getInput.setPrimaryKey(recordId);
|
||||||
|
getInput.setShouldGenerateDisplayValues(true);
|
||||||
|
GetOutput getOutput = new GetAction().execute(getInput);
|
||||||
|
QRecord record = getOutput.getRecord();
|
||||||
|
if(record == null)
|
||||||
|
{
|
||||||
|
throw (new QUserFacingException("The selected record for the report was not found."));
|
||||||
|
}
|
||||||
|
|
||||||
|
String reportName = runBackendStepInput.getValueString("reportName");
|
||||||
|
QReportMetaData report = runBackendStepInput.getInstance().getReport(reportName);
|
||||||
|
// runBackendStepOutput.addValue("downloadFileBaseName", runBackendStepInput.getTable().getLabel() + " " + record.getRecordLabel());
|
||||||
|
runBackendStepOutput.addValue("downloadFileBaseName", report.getLabel() + " - " + record.getRecordLabel());
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// if there are no more input fields, then remove the INPUT step from the process. //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
inputFieldList = (ArrayList<QFieldMetaData>) runBackendStepOutput.getValue("inputFieldList");
|
||||||
|
if(!CollectionUtils.nullSafeHasContents(inputFieldList))
|
||||||
|
{
|
||||||
|
removeInputStepFromProcess(runBackendStepOutput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -43,6 +43,10 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class PrepareReportStep implements BackendStep
|
public class PrepareReportStep implements BackendStep
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
@Override
|
@Override
|
||||||
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||||
{
|
{
|
||||||
@ -67,6 +71,17 @@ public class PrepareReportStep implements BackendStep
|
|||||||
runBackendStepOutput.addValue("inputFieldList", inputFieldList);
|
runBackendStepOutput.addValue("inputFieldList", inputFieldList);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
removeInputStepFromProcess(runBackendStepOutput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
protected void removeInputStepFromProcess(RunBackendStepOutput runBackendStepOutput)
|
||||||
{
|
{
|
||||||
//////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////
|
||||||
// no input? re-route the process to skip the input screen //
|
// no input? re-route the process to skip the input screen //
|
||||||
@ -75,5 +90,4 @@ public class PrepareReportStep implements BackendStep
|
|||||||
stepList.removeIf(s -> s.equals(BasicRunReportProcess.STEP_NAME_INPUT));
|
stepList.removeIf(s -> s.equals(BasicRunReportProcess.STEP_NAME_INPUT));
|
||||||
runBackendStepOutput.getProcessState().setStepList(stepList);
|
runBackendStepOutput.getProcessState().setStepList(stepList);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* 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.processes.implementations.reports;
|
||||||
|
|
||||||
|
|
||||||
|
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.fields.QFieldType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.AbstractProcessMetaDataBuilder;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QComponentType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponentMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Definition for Basic process to run a report.
|
||||||
|
*******************************************************************************/
|
||||||
|
public class RunReportForRecordProcess
|
||||||
|
{
|
||||||
|
public static final String PROCESS_NAME = "reports.forRecord";
|
||||||
|
|
||||||
|
public static final String STEP_NAME_PREPARE = "prepare";
|
||||||
|
public static final String STEP_NAME_INPUT = "input";
|
||||||
|
public static final String STEP_NAME_EXECUTE = "execute";
|
||||||
|
public static final String STEP_NAME_ACCESS = "accessReport";
|
||||||
|
|
||||||
|
public static final String FIELD_REPORT_NAME = "reportName";
|
||||||
|
public static final String FIELD_RECORD_ID = "recordId";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static Builder processMetaDataBuilder()
|
||||||
|
{
|
||||||
|
return (new Builder(defineProcessMetaData()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static QProcessMetaData defineProcessMetaData()
|
||||||
|
{
|
||||||
|
QStepMetaData prepareStep = new QBackendStepMetaData()
|
||||||
|
.withName(STEP_NAME_PREPARE)
|
||||||
|
.withCode(new QCodeReference(PrepareReportForRecordStep.class))
|
||||||
|
.withInputData(new QFunctionInputMetaData()
|
||||||
|
.withField(new QFieldMetaData(FIELD_REPORT_NAME, QFieldType.STRING))
|
||||||
|
.withField(new QFieldMetaData(FIELD_RECORD_ID, QFieldType.STRING)));
|
||||||
|
|
||||||
|
QStepMetaData inputStep = new QFrontendStepMetaData()
|
||||||
|
.withName(STEP_NAME_INPUT)
|
||||||
|
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.EDIT_FORM));
|
||||||
|
|
||||||
|
QStepMetaData executeStep = new QBackendStepMetaData()
|
||||||
|
.withName(STEP_NAME_EXECUTE)
|
||||||
|
.withCode(new QCodeReference(ExecuteReportStep.class))
|
||||||
|
.withInputData(new QFunctionInputMetaData()
|
||||||
|
.withField(new QFieldMetaData(FIELD_REPORT_NAME, QFieldType.STRING)));
|
||||||
|
|
||||||
|
QStepMetaData accessStep = new QFrontendStepMetaData()
|
||||||
|
.withName(STEP_NAME_ACCESS)
|
||||||
|
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.DOWNLOAD_FORM));
|
||||||
|
// .withViewField(new QFieldMetaData("outputFile", QFieldType.STRING))
|
||||||
|
// .withViewField(new QFieldMetaData("message", QFieldType.STRING));
|
||||||
|
|
||||||
|
return new QProcessMetaData()
|
||||||
|
.withName(PROCESS_NAME)
|
||||||
|
.addStep(prepareStep)
|
||||||
|
.addStep(inputStep)
|
||||||
|
.addStep(executeStep)
|
||||||
|
.addStep(accessStep);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static class Builder extends AbstractProcessMetaDataBuilder
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder(QProcessMetaData processMetaData)
|
||||||
|
{
|
||||||
|
super(processMetaData);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for name
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withProcessName(String name)
|
||||||
|
{
|
||||||
|
processMetaData.setName(name);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for tableName
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withTableName(String tableName)
|
||||||
|
{
|
||||||
|
processMetaData.setTableName(tableName);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for icon
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withIcon(QIcon icon)
|
||||||
|
{
|
||||||
|
processMetaData.setIcon(icon);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for reportName
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public Builder withReportName(String reportName)
|
||||||
|
{
|
||||||
|
setInputFieldDefaultValue(RunReportForRecordProcess.FIELD_REPORT_NAME, reportName);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -54,6 +54,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleVal
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.queues.SQSQueueProviderMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.queues.SQSQueueProviderMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportDataSource;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
||||||
@ -143,9 +144,8 @@ class QInstanceValidatorTest
|
|||||||
qInstance.setTables(null);
|
qInstance.setTables(null);
|
||||||
qInstance.setProcesses(null);
|
qInstance.setProcesses(null);
|
||||||
},
|
},
|
||||||
"At least 1 table must be defined",
|
true,
|
||||||
"Unrecognized table shape for possibleValueSource shape",
|
"At least 1 table must be defined");
|
||||||
"Unrecognized processName for queue");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -162,9 +162,8 @@ class QInstanceValidatorTest
|
|||||||
qInstance.setTables(new HashMap<>());
|
qInstance.setTables(new HashMap<>());
|
||||||
qInstance.setProcesses(new HashMap<>());
|
qInstance.setProcesses(new HashMap<>());
|
||||||
},
|
},
|
||||||
"At least 1 table must be defined",
|
true,
|
||||||
"Unrecognized table shape for possibleValueSource shape",
|
"At least 1 table must be defined");
|
||||||
"Unrecognized processName for queue");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -570,6 +569,40 @@ class QInstanceValidatorTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testFieldSectionDuplicateName()
|
||||||
|
{
|
||||||
|
QTableMetaData table = new QTableMetaData().withName("test")
|
||||||
|
.withBackendName(TestUtils.DEFAULT_BACKEND_NAME)
|
||||||
|
.withSection(new QFieldSection("section1", "Section 1", new QIcon("person"), Tier.T1, List.of("id")))
|
||||||
|
.withSection(new QFieldSection("section1", "Section 2", new QIcon("person"), Tier.T2, List.of("name")))
|
||||||
|
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||||
|
.withField(new QFieldMetaData("name", QFieldType.INTEGER));
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.addTable(table), "more than 1 section named");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testFieldSectionDuplicateLabel()
|
||||||
|
{
|
||||||
|
QTableMetaData table = new QTableMetaData().withName("test")
|
||||||
|
.withBackendName(TestUtils.DEFAULT_BACKEND_NAME)
|
||||||
|
.withSection(new QFieldSection("section1", "Section 1", new QIcon("person"), Tier.T1, List.of("id")))
|
||||||
|
.withSection(new QFieldSection("section2", "Section 1", new QIcon("person"), Tier.T2, List.of("name")))
|
||||||
|
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||||
|
.withField(new QFieldMetaData("name", QFieldType.INTEGER));
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.addTable(table), "more than 1 section labeled");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -1258,6 +1291,138 @@ class QInstanceValidatorTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testReportName()
|
||||||
|
{
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).withName(null),
|
||||||
|
"Inconsistent naming for report");
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).withName(""),
|
||||||
|
"Inconsistent naming for report");
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).withName("wrongName"),
|
||||||
|
"Inconsistent naming for report");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testReportNoDataSources()
|
||||||
|
{
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).withDataSources(null),
|
||||||
|
"At least 1 data source",
|
||||||
|
"unrecognized dataSourceName");
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).withDataSources(new ArrayList<>()),
|
||||||
|
"At least 1 data source",
|
||||||
|
"unrecognized dataSourceName");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testReportDataSourceNames()
|
||||||
|
{
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).getDataSources().get(0).setName(null),
|
||||||
|
"Missing name for a dataSource",
|
||||||
|
"unrecognized dataSourceName");
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).getDataSources().get(0).setName(""),
|
||||||
|
"Missing name for a dataSource",
|
||||||
|
"unrecognized dataSourceName");
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance) ->
|
||||||
|
{
|
||||||
|
List<QReportDataSource> dataSources = new ArrayList<>(qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).getDataSources());
|
||||||
|
dataSources.add(dataSources.get(0));
|
||||||
|
qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).setDataSources(dataSources);
|
||||||
|
},
|
||||||
|
"More than one dataSource with name");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testReportDataSourceTables()
|
||||||
|
{
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).getDataSources().get(0).setSourceTable("notATable"),
|
||||||
|
"is not a table in this instance");
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).getDataSources().get(0).setSourceTable(null),
|
||||||
|
"does not have a sourceTable");
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).getDataSources().get(0).setSourceTable(""),
|
||||||
|
"does not have a sourceTable");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testReportDataSourceTablesFilter()
|
||||||
|
{
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).getDataSources().get(0).getQueryFilter().getCriteria().get(0).setFieldName(null),
|
||||||
|
"Missing fieldName for a criteria");
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).getDataSources().get(0).getQueryFilter().getCriteria().get(0).setFieldName("notAField"),
|
||||||
|
"is not a field in this table");
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).getDataSources().get(0).getQueryFilter().getCriteria().get(0).setOperator(null),
|
||||||
|
"Missing operator for a criteria");
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).getDataSources().get(0).getQueryFilter().withOrderBy(new QFilterOrderBy(null)),
|
||||||
|
"Missing fieldName for an orderBy");
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).getDataSources().get(0).getQueryFilter().withOrderBy(new QFilterOrderBy("notAField")),
|
||||||
|
"is not a field in this table");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testReportDataSourceStaticDataSupplier()
|
||||||
|
{
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).getDataSources().get(0).withStaticDataSupplier(new QCodeReference()),
|
||||||
|
"has both a sourceTable and a staticDataSupplier");
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance) ->
|
||||||
|
{
|
||||||
|
QReportDataSource dataSource = qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).getDataSources().get(0);
|
||||||
|
dataSource.setSourceTable(null);
|
||||||
|
dataSource.setStaticDataSupplier(new QCodeReference(null, QCodeType.JAVA, null));
|
||||||
|
},
|
||||||
|
"missing a code reference name");
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance) ->
|
||||||
|
{
|
||||||
|
QReportDataSource dataSource = qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).getDataSources().get(0);
|
||||||
|
dataSource.setSourceTable(null);
|
||||||
|
dataSource.setStaticDataSupplier(new QCodeReference(ArrayList.class, null));
|
||||||
|
},
|
||||||
|
"is not of the expected type");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -0,0 +1,62 @@
|
|||||||
|
/*
|
||||||
|
* 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.processes.implementations.reports;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for BasicRunReportProcess
|
||||||
|
*******************************************************************************/
|
||||||
|
class RunReportForRecordProcessTest
|
||||||
|
{
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testRunReport() throws QException
|
||||||
|
{
|
||||||
|
QInstance instance = TestUtils.defineInstance();
|
||||||
|
TestUtils.insertDefaultShapes(instance);
|
||||||
|
|
||||||
|
RunProcessInput runProcessInput = new RunProcessInput(instance);
|
||||||
|
runProcessInput.setSession(TestUtils.getMockSession());
|
||||||
|
runProcessInput.setProcessName(TestUtils.PROCESS_NAME_RUN_SHAPES_PERSON_REPORT);
|
||||||
|
runProcessInput.addValue(BasicRunReportProcess.FIELD_REPORT_NAME, TestUtils.REPORT_NAME_SHAPES_PERSON);
|
||||||
|
runProcessInput.addValue("recordsParam", "recordIds");
|
||||||
|
runProcessInput.addValue("recordIds", "1");
|
||||||
|
|
||||||
|
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
||||||
|
|
||||||
|
// runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
||||||
|
// assertThat(runProcessOutput.getProcessState().getNextStepName()).isPresent().get().isEqualTo(BasicRunReportProcess.STEP_NAME_ACCESS);
|
||||||
|
// assertThat(runProcessOutput.getValues()).containsKeys("downloadFileName", "serverFilePath");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -76,6 +76,11 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QRecordListMetaDa
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.queues.QQueueMetaData;
|
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.queues.QQueueProviderMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.queues.SQSQueueProviderMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.queues.SQSQueueProviderMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportDataSource;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportField;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportView;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.reporting.ReportType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.AutomationStatusTracking;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.AutomationStatusTracking;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.AutomationStatusTrackingType;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.AutomationStatusTrackingType;
|
||||||
@ -91,6 +96,7 @@ import com.kingsrook.qqq.backend.core.processes.implementations.basepull.Basepul
|
|||||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.basic.BasicETLProcess;
|
import com.kingsrook.qqq.backend.core.processes.implementations.etl.basic.BasicETLProcess;
|
||||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamed.StreamedETLProcess;
|
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamed.StreamedETLProcess;
|
||||||
import com.kingsrook.qqq.backend.core.processes.implementations.mock.MockBackendStep;
|
import com.kingsrook.qqq.backend.core.processes.implementations.mock.MockBackendStep;
|
||||||
|
import com.kingsrook.qqq.backend.core.processes.implementations.reports.RunReportForRecordProcess;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
@ -118,10 +124,12 @@ public class TestUtils
|
|||||||
public static final String PROCESS_NAME_INCREASE_BIRTHDATE = "increaseBirthdate";
|
public static final String PROCESS_NAME_INCREASE_BIRTHDATE = "increaseBirthdate";
|
||||||
public static final String PROCESS_NAME_ADD_TO_PEOPLES_AGE = "addToPeoplesAge";
|
public static final String PROCESS_NAME_ADD_TO_PEOPLES_AGE = "addToPeoplesAge";
|
||||||
public static final String PROCESS_NAME_BASEPULL = "basepullTest";
|
public static final String PROCESS_NAME_BASEPULL = "basepullTest";
|
||||||
|
public static final String PROCESS_NAME_RUN_SHAPES_PERSON_REPORT = "runShapesPersonReport";
|
||||||
public static final String TABLE_NAME_PERSON_FILE = "personFile";
|
public static final String TABLE_NAME_PERSON_FILE = "personFile";
|
||||||
public static final String TABLE_NAME_PERSON_MEMORY = "personMemory";
|
public static final String TABLE_NAME_PERSON_MEMORY = "personMemory";
|
||||||
public static final String TABLE_NAME_ID_AND_NAME_ONLY = "idAndNameOnly";
|
public static final String TABLE_NAME_ID_AND_NAME_ONLY = "idAndNameOnly";
|
||||||
public static final String TABLE_NAME_BASEPULL = "basepullTest";
|
public static final String TABLE_NAME_BASEPULL = "basepullTest";
|
||||||
|
public static final String REPORT_NAME_SHAPES_PERSON = "shapesPersonReport";
|
||||||
|
|
||||||
public static final String POSSIBLE_VALUE_SOURCE_STATE = "state"; // enum-type
|
public static final String POSSIBLE_VALUE_SOURCE_STATE = "state"; // enum-type
|
||||||
public static final String POSSIBLE_VALUE_SOURCE_SHAPE = "shape"; // table-type
|
public static final String POSSIBLE_VALUE_SOURCE_SHAPE = "shape"; // table-type
|
||||||
@ -167,6 +175,9 @@ public class TestUtils
|
|||||||
qInstance.addProcess(defineProcessIncreasePersonBirthdate());
|
qInstance.addProcess(defineProcessIncreasePersonBirthdate());
|
||||||
qInstance.addProcess(defineProcessBasepull());
|
qInstance.addProcess(defineProcessBasepull());
|
||||||
|
|
||||||
|
qInstance.addReport(defineShapesPersonsReport());
|
||||||
|
qInstance.addProcess(defineShapesPersonReportProcess());
|
||||||
|
|
||||||
qInstance.addAutomationProvider(definePollingAutomationProvider());
|
qInstance.addAutomationProvider(definePollingAutomationProvider());
|
||||||
|
|
||||||
qInstance.addQueueProvider(defineSqsProvider());
|
qInstance.addQueueProvider(defineSqsProvider());
|
||||||
@ -951,4 +962,52 @@ public class TestUtils
|
|||||||
.withProcessName(PROCESS_NAME_INCREASE_BIRTHDATE));
|
.withProcessName(PROCESS_NAME_INCREASE_BIRTHDATE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static QReportMetaData defineShapesPersonsReport()
|
||||||
|
{
|
||||||
|
return new QReportMetaData()
|
||||||
|
.withName(REPORT_NAME_SHAPES_PERSON)
|
||||||
|
.withProcessName(PROCESS_NAME_RUN_SHAPES_PERSON_REPORT)
|
||||||
|
.withInputFields(List.of(
|
||||||
|
new QFieldMetaData(RunReportForRecordProcess.FIELD_RECORD_ID, QFieldType.INTEGER).withIsRequired(true)
|
||||||
|
))
|
||||||
|
.withDataSources(List.of(
|
||||||
|
new QReportDataSource()
|
||||||
|
.withName("persons")
|
||||||
|
.withSourceTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||||
|
.withQueryFilter(new QQueryFilter()
|
||||||
|
.withCriteria(new QFilterCriteria("favoriteShapeId", QCriteriaOperator.EQUALS, List.of("${input." + RunReportForRecordProcess.FIELD_RECORD_ID + "}")))
|
||||||
|
)
|
||||||
|
))
|
||||||
|
.withViews(List.of(
|
||||||
|
new QReportView()
|
||||||
|
.withName("person")
|
||||||
|
.withDataSourceName("persons")
|
||||||
|
.withType(ReportType.TABLE)
|
||||||
|
.withColumns(List.of(
|
||||||
|
new QReportField().withName("id"),
|
||||||
|
new QReportField().withName("firstName"),
|
||||||
|
new QReportField().withName("lastName")
|
||||||
|
))
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static QProcessMetaData defineShapesPersonReportProcess()
|
||||||
|
{
|
||||||
|
return RunReportForRecordProcess.processMetaDataBuilder()
|
||||||
|
.withProcessName(PROCESS_NAME_RUN_SHAPES_PERSON_REPORT)
|
||||||
|
.withReportName(REPORT_NAME_SHAPES_PERSON)
|
||||||
|
.withTableName(TestUtils.TABLE_NAME_SHAPE)
|
||||||
|
.getProcessMetaData();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,10 @@ package com.kingsrook.qqq.backend.module.api.model.metadata;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
import com.kingsrook.qqq.backend.module.api.APIBackendModule;
|
import com.kingsrook.qqq.backend.module.api.APIBackendModule;
|
||||||
import com.kingsrook.qqq.backend.module.api.model.AuthorizationType;
|
import com.kingsrook.qqq.backend.module.api.model.AuthorizationType;
|
||||||
|
|
||||||
@ -379,4 +381,14 @@ public class APIBackendMetaData extends QBackendMetaData
|
|||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void performValidation(QInstanceValidator qInstanceValidator)
|
||||||
|
{
|
||||||
|
qInstanceValidator.assertCondition(StringUtils.hasContent(baseUrl), "Missing baseUrl for API backend: " + getName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,9 +32,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||||
import com.kingsrook.qqq.backend.module.api.model.metadata.APIBackendMetaData;
|
import com.kingsrook.qqq.backend.module.api.model.metadata.APIBackendMetaData;
|
||||||
|
import org.junit.jupiter.api.Disabled;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.DisabledOnOs;
|
|
||||||
import org.junit.jupiter.api.condition.OS;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
@ -43,7 +42,7 @@ import static org.junit.jupiter.api.Assertions.assertNull;
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@DisabledOnOs(OS.LINUX)
|
@Disabled // OnOs(OS.LINUX)
|
||||||
public class EasyPostApiTest
|
public class EasyPostApiTest
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* 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.module.api.model.metadata;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for APIBackendMetaData
|
||||||
|
*******************************************************************************/
|
||||||
|
class APIBackendMetaDataTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void test()
|
||||||
|
{
|
||||||
|
APIBackendMetaData apiBackendMetaData = new APIBackendMetaData()
|
||||||
|
.withName("test");
|
||||||
|
QInstanceValidator qInstanceValidator = new QInstanceValidator();
|
||||||
|
apiBackendMetaData.performValidation(qInstanceValidator);
|
||||||
|
assertEquals(1, qInstanceValidator.getErrors().size());
|
||||||
|
assertThat(qInstanceValidator.getErrors()).anyMatch(e -> e.contains("Missing baseUrl"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user