Much implementation of joins for RDBMS

This commit is contained in:
2022-11-23 16:37:54 -06:00
parent 6685e61500
commit b2d76e8206
26 changed files with 1873 additions and 104 deletions

View File

@ -46,6 +46,7 @@ import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutp
import com.kingsrook.qqq.backend.core.model.actions.reporting.ExportInput;
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.tables.query.JoinsContext;
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;
@ -185,7 +186,7 @@ public class GenerateReportAction
/*******************************************************************************
**
*******************************************************************************/
private void startTableView(ReportInput reportInput, QReportDataSource dataSource, QReportView reportView) throws QReportingException
private void startTableView(ReportInput reportInput, QReportDataSource dataSource, QReportView reportView) throws QException
{
QTableMetaData table = reportInput.getInstance().getTable(dataSource.getSourceTable());
@ -200,6 +201,8 @@ public class GenerateReportAction
exportInput.setIncludeHeaderRow(reportView.getIncludeHeaderRow());
exportInput.setReportOutputStream(reportInput.getReportOutputStream());
JoinsContext joinsContext = new JoinsContext(exportInput.getInstance(), dataSource.getSourceTable(), dataSource.getQueryJoins());
List<QFieldMetaData> fields;
if(CollectionUtils.nullSafeHasContents(reportView.getColumns()))
{
@ -212,7 +215,14 @@ public class GenerateReportAction
}
else
{
QFieldMetaData field = table.getField(column.getName()).clone();
JoinsContext.FieldAndTableNameOrAlias fieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(column.getName());
if(fieldAndTableNameOrAlias.field() == null)
{
throw new QReportingException("Could not find field named [" + column.getName() + "] on table [" + table.getName() + "]");
}
QFieldMetaData field = fieldAndTableNameOrAlias.field().clone();
field.setName(column.getName());
if(StringUtils.hasContent(column.getLabel()))
{
field.setLabel(column.getLabel());
@ -278,6 +288,7 @@ public class GenerateReportAction
queryInput.setRecordPipe(recordPipe);
queryInput.setTableName(dataSource.getSourceTable());
queryInput.setFilter(queryFilter);
queryInput.setQueryJoins(dataSource.getQueryJoins());
queryInput.setShouldTranslatePossibleValues(true); // todo - any limits or conditions on this?
return (new QueryAction().execute(queryInput));
}

View File

@ -49,7 +49,9 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendComponen
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
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.reporting.QReportDataSource;
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.tables.QFieldSection;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
@ -365,6 +367,50 @@ public class QInstanceEnricher
{
report.getInputFields().forEach(this::enrichField);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
// if there's only 1 data source in the report, and it doesn't have a name, give it a default name //
/////////////////////////////////////////////////////////////////////////////////////////////////////
String singleDataSourceName = null;
if(report.getDataSources() != null)
{
if(report.getDataSources().size() == 1)
{
QReportDataSource dataSource = report.getDataSources().get(0);
if(!StringUtils.hasContent(dataSource.getName()))
{
dataSource.setName("DEFAULT");
}
singleDataSourceName = dataSource.getName();
}
}
if(report.getViews() != null)
{
//////////////////////////////////////////////////////////////////////////////////////////////
// if there's only 1 view in the report, and it doesn't have a name, give it a default name //
//////////////////////////////////////////////////////////////////////////////////////////////
if(report.getViews().size() == 1)
{
QReportView view = report.getViews().get(0);
if(!StringUtils.hasContent(view.getName()))
{
view.setName("DEFAULT");
}
}
/////////////////////////////////////////////////////////////////////////////
// for any views in the report, if they don't specify a data source name, //
// but there's only 1 data source, then use that single data source's name //
/////////////////////////////////////////////////////////////////////////////
for(QReportView view : report.getViews())
{
if(!StringUtils.hasContent(view.getDataSourceName()) && singleDataSourceName != null)
{
view.setDataSourceName(singleDataSourceName);
}
}
}
}

View File

@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.List;
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
@ -39,6 +40,8 @@ public class AggregateInput extends AbstractTableActionInput
private List<Aggregate> aggregates;
private List<String> groupByFieldNames;
private List<QueryJoin> queryJoins = null;
/*******************************************************************************
@ -192,4 +195,54 @@ public class AggregateInput extends AbstractTableActionInput
return (this);
}
/*******************************************************************************
** Getter for queryJoins
**
*******************************************************************************/
public List<QueryJoin> getQueryJoins()
{
return queryJoins;
}
/*******************************************************************************
** Setter for queryJoins
**
*******************************************************************************/
public void setQueryJoins(List<QueryJoin> queryJoins)
{
this.queryJoins = queryJoins;
}
/*******************************************************************************
** Fluent setter for queryJoins
**
*******************************************************************************/
public AggregateInput withQueryJoins(List<QueryJoin> queryJoins)
{
this.queryJoins = queryJoins;
return (this);
}
/*******************************************************************************
** Fluent setter for queryJoins
**
*******************************************************************************/
public AggregateInput withQueryJoin(QueryJoin queryJoin)
{
if(this.queryJoins == null)
{
this.queryJoins = new ArrayList<>();
}
this.queryJoins.add(queryJoin);
return (this);
}
}

View File

@ -22,8 +22,11 @@
package com.kingsrook.qqq.backend.core.model.actions.tables.count;
import java.util.ArrayList;
import java.util.List;
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
@ -35,6 +38,8 @@ public class CountInput extends AbstractTableActionInput
{
private QQueryFilter filter;
private List<QueryJoin> queryJoins = null;
/*******************************************************************************
@ -78,4 +83,52 @@ public class CountInput extends AbstractTableActionInput
/*******************************************************************************
** Getter for queryJoins
**
*******************************************************************************/
public List<QueryJoin> getQueryJoins()
{
return queryJoins;
}
/*******************************************************************************
** Setter for queryJoins
**
*******************************************************************************/
public void setQueryJoins(List<QueryJoin> queryJoins)
{
this.queryJoins = queryJoins;
}
/*******************************************************************************
** Fluent setter for queryJoins
**
*******************************************************************************/
public CountInput withQueryJoins(List<QueryJoin> queryJoins)
{
this.queryJoins = queryJoins;
return (this);
}
/*******************************************************************************
** Fluent setter for queryJoins
**
*******************************************************************************/
public CountInput withQueryJoin(QueryJoin queryJoin)
{
if(this.queryJoins == null)
{
this.queryJoins = new ArrayList<>();
}
this.queryJoins.add(queryJoin);
return (this);
}
}

View File

@ -0,0 +1,134 @@
/*
* 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.actions.tables.query;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
/*******************************************************************************
** Helper object used throughout query (and related (count, aggregate, reporting))
** actions that need to track joins and aliases.
*******************************************************************************/
public class JoinsContext
{
private final QInstance instance;
private final String mainTableName;
private final List<QueryJoin> queryJoins;
private final Map<String, String> aliasToTableNameMap = new HashMap<>();
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public JoinsContext(QInstance instance, String tableName, List<QueryJoin> queryJoins) throws QException
{
this.instance = instance;
this.mainTableName = tableName;
this.queryJoins = CollectionUtils.nonNullList(queryJoins);
for(QueryJoin queryJoin : this.queryJoins)
{
QTableMetaData joinTable = instance.getTable(queryJoin.getRightTable());
String tableNameOrAlias = queryJoin.getAliasOrRightTable();
if(aliasToTableNameMap.containsKey(tableNameOrAlias))
{
throw (new QException("Duplicate table name or alias: " + tableNameOrAlias));
}
aliasToTableNameMap.put(tableNameOrAlias, joinTable.getName());
}
}
/*******************************************************************************
** Getter for queryJoins
**
*******************************************************************************/
public List<QueryJoin> getQueryJoins()
{
return queryJoins;
}
/*******************************************************************************
**
*******************************************************************************/
public String resolveTableNameOrAliasToTableName(String nameOrAlias)
{
if(aliasToTableNameMap.containsKey(nameOrAlias))
{
return (aliasToTableNameMap.get(nameOrAlias));
}
return (nameOrAlias);
}
/*******************************************************************************
**
*******************************************************************************/
public FieldAndTableNameOrAlias getFieldAndTableNameOrAlias(String fieldName)
{
if(fieldName.contains("."))
{
String[] parts = fieldName.split("\\.");
if(parts.length != 2)
{
throw new IllegalArgumentException("Mal-formatted field name in query: " + fieldName);
}
String tableOrAlias = parts[0];
String baseFieldName = parts[1];
String tableName = resolveTableNameOrAliasToTableName(tableOrAlias);
QTableMetaData table = instance.getTable(tableName);
if(table == null)
{
throw new IllegalArgumentException("Could not find table [" + tableName + "] in instance for query");
}
return new FieldAndTableNameOrAlias(table.getField(baseFieldName), tableOrAlias);
}
return new FieldAndTableNameOrAlias(instance.getTable(mainTableName).getField(fieldName), mainTableName);
}
/*******************************************************************************
**
*******************************************************************************/
public record FieldAndTableNameOrAlias(QFieldMetaData field, String tableNameOrAlias)
{
}
}

View File

@ -27,6 +27,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -43,6 +44,8 @@ public class QFilterCriteria implements Serializable, Cloneable
private QCriteriaOperator operator;
private List<Serializable> values;
private String otherFieldName;
/*******************************************************************************
@ -220,6 +223,40 @@ public class QFilterCriteria implements Serializable, Cloneable
/*******************************************************************************
** Getter for otherFieldName
**
*******************************************************************************/
public String getOtherFieldName()
{
return otherFieldName;
}
/*******************************************************************************
** Setter for otherFieldName
**
*******************************************************************************/
public void setOtherFieldName(String otherFieldName)
{
this.otherFieldName = otherFieldName;
}
/*******************************************************************************
** Fluent setter for otherFieldName
**
*******************************************************************************/
public QFilterCriteria withOtherFieldName(String otherFieldName)
{
this.otherFieldName = otherFieldName;
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
@ -232,21 +269,28 @@ public class QFilterCriteria implements Serializable, Cloneable
rs.append(" ").append(operator).append(" ");
if(CollectionUtils.nullSafeHasContents(values))
{
if(values.size() == 1)
if(StringUtils.hasContent(otherFieldName))
{
rs.append(values.get(0));
rs.append(otherFieldName);
}
else
{
int index = 0;
for(Serializable value : values)
if(values.size() == 1)
{
if(index++ > 9)
rs.append(values.get(0));
}
else
{
int index = 0;
for(Serializable value : values)
{
rs.append("and ").append(values.size() - index).append(" more");
break;
if(index++ > 9)
{
rs.append("and ").append(values.size() - index).append(" more");
break;
}
rs.append(value).append(",");
}
rs.append(value).append(",");
}
}
}

View File

@ -22,6 +22,8 @@
package com.kingsrook.qqq.backend.core.model.actions.tables.query;
import java.util.ArrayList;
import java.util.List;
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
import com.kingsrook.qqq.backend.core.actions.reporting.RecordPipe;
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
@ -45,6 +47,8 @@ public class QueryInput extends AbstractTableActionInput
private boolean shouldTranslatePossibleValues = false;
private boolean shouldGenerateDisplayValues = false;
private List<QueryJoin> queryJoins = null;
/*******************************************************************************
@ -241,4 +245,54 @@ public class QueryInput extends AbstractTableActionInput
return (this);
}
/*******************************************************************************
** Getter for queryJoins
**
*******************************************************************************/
public List<QueryJoin> getQueryJoins()
{
return queryJoins;
}
/*******************************************************************************
** Setter for queryJoins
**
*******************************************************************************/
public void setQueryJoins(List<QueryJoin> queryJoins)
{
this.queryJoins = queryJoins;
}
/*******************************************************************************
** Fluent setter for queryJoins
**
*******************************************************************************/
public QueryInput withQueryJoins(List<QueryJoin> queryJoins)
{
this.queryJoins = queryJoins;
return (this);
}
/*******************************************************************************
** Fluent setter for queryJoins
**
*******************************************************************************/
public QueryInput withQueryJoin(QueryJoin queryJoin)
{
if(this.queryJoins == null)
{
this.queryJoins = new ArrayList<>();
}
this.queryJoins.add(queryJoin);
return (this);
}
}

View File

@ -0,0 +1,306 @@
/*
* 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.actions.tables.query;
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
/*******************************************************************************
** Part of query (or count, aggregate) input, to do a Join as part of a query.
*******************************************************************************/
public class QueryJoin
{
private String leftTableOrAlias;
private String rightTable;
private QJoinMetaData joinMetaData;
private String alias;
private boolean select = false;
private Type type = Type.INNER;
/*******************************************************************************
**
*******************************************************************************/
public enum Type
{INNER, LEFT, RIGHT, FULL}
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public QueryJoin()
{
}
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public QueryJoin(String leftTableOrAlias, String rightTable)
{
this.leftTableOrAlias = leftTableOrAlias;
this.rightTable = rightTable;
}
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public QueryJoin(QJoinMetaData joinMetaData)
{
setJoinMetaData(joinMetaData);
}
/*******************************************************************************
** Getter for leftTableOrAlias
**
*******************************************************************************/
public String getLeftTableOrAlias()
{
return leftTableOrAlias;
}
/*******************************************************************************
** Setter for leftTableOrAlias
**
*******************************************************************************/
public void setLeftTableOrAlias(String leftTableOrAlias)
{
this.leftTableOrAlias = leftTableOrAlias;
}
/*******************************************************************************
** Fluent setter for leftTableOrAlias
**
*******************************************************************************/
public QueryJoin withLeftTableOrAlias(String leftTableOrAlias)
{
this.leftTableOrAlias = leftTableOrAlias;
return (this);
}
/*******************************************************************************
** Getter for rightTable
**
*******************************************************************************/
public String getRightTable()
{
return rightTable;
}
/*******************************************************************************
** Setter for rightTable
**
*******************************************************************************/
public void setRightTable(String rightTable)
{
this.rightTable = rightTable;
}
/*******************************************************************************
** Fluent setter for rightTable
**
*******************************************************************************/
public QueryJoin withRightTable(String rightTable)
{
this.rightTable = rightTable;
return (this);
}
/*******************************************************************************
** Getter for alias
**
*******************************************************************************/
public String getAlias()
{
return alias;
}
/*******************************************************************************
** Setter for alias
**
*******************************************************************************/
public void setAlias(String alias)
{
this.alias = alias;
}
/*******************************************************************************
** Fluent setter for alias
**
*******************************************************************************/
public QueryJoin withAlias(String alias)
{
this.alias = alias;
return (this);
}
/*******************************************************************************
** Getter for select
**
*******************************************************************************/
public boolean getSelect()
{
return select;
}
/*******************************************************************************
** Setter for select
**
*******************************************************************************/
public void setSelect(boolean select)
{
this.select = select;
}
/*******************************************************************************
** Fluent setter for select
**
*******************************************************************************/
public QueryJoin withSelect(boolean select)
{
this.select = select;
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
public String getAliasOrRightTable()
{
if(StringUtils.hasContent(alias))
{
return (alias);
}
return (rightTable);
}
/*******************************************************************************
** Getter for type
**
*******************************************************************************/
public Type getType()
{
return type;
}
/*******************************************************************************
** Setter for type
**
*******************************************************************************/
public void setType(Type type)
{
this.type = type;
}
/*******************************************************************************
** Fluent setter for type
**
*******************************************************************************/
public QueryJoin withType(Type type)
{
this.type = type;
return (this);
}
/*******************************************************************************
** Getter for joinMetaData
**
*******************************************************************************/
public QJoinMetaData getJoinMetaData()
{
return joinMetaData;
}
/*******************************************************************************
** Setter for joinMetaData
**
*******************************************************************************/
public void setJoinMetaData(QJoinMetaData joinMetaData)
{
this.joinMetaData = joinMetaData;
if(!StringUtils.hasContent(this.leftTableOrAlias) && !StringUtils.hasContent(this.rightTable))
{
setLeftTableOrAlias(joinMetaData.getLeftTable());
setRightTable(joinMetaData.getRightTable());
}
}
/*******************************************************************************
** Fluent setter for joinMetaData
**
*******************************************************************************/
public QueryJoin withJoinMetaData(QJoinMetaData joinMetaData)
{
setJoinMetaData(joinMetaData);
return (this);
}
}

View File

@ -23,7 +23,8 @@ package com.kingsrook.qqq.backend.core.model.metadata.joins;
/*******************************************************************************
**
** Specification for (at least part of) how two tables join together - e.g.,
** leftField = rightField. Used as part of a list in a QJoinMetaData.
*******************************************************************************/
public class JoinOn
{
@ -54,6 +55,16 @@ public class JoinOn
/*******************************************************************************
** Return a new JoinOn, with the fields of this one, but flipped (right ←→ left)
*******************************************************************************/
public JoinOn flip()
{
return new JoinOn(rightField, leftField);
}
/*******************************************************************************
** Getter for leftField
**

View File

@ -25,7 +25,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.joins;
/*******************************************************************************
** Type for a QJoin.
**
** - One to One - what about zero??
** - One to One - or zero, i guess...
** - One to Many - e.g., where the parent record really "owns" all of the child
** records. Like Order -> OrderLine.
** - Many to One - e.g., where a child references a parent, but we'd never really
@ -37,5 +37,21 @@ public enum JoinType
ONE_TO_ONE,
ONE_TO_MANY,
MANY_TO_ONE,
MANY_TO_MANY
MANY_TO_MANY;
/*******************************************************************************
**
*******************************************************************************/
@SuppressWarnings("checkstyle:indentation")
public JoinType flip()
{
return switch(this)
{
case ONE_TO_MANY -> MANY_TO_ONE;
case MANY_TO_ONE -> ONE_TO_MANY;
case MANY_TO_MANY, ONE_TO_ONE -> this;
};
}
}

View File

@ -29,7 +29,7 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
/*******************************************************************************
**
** Definition of how 2 tables join together within a QQQ Instance.
*******************************************************************************/
public class QJoinMetaData
{
@ -43,6 +43,22 @@ public class QJoinMetaData
/*******************************************************************************
** Return a new QJoinMetaData, with all the same data as this one, but flipped
** right ←→ left, for the tables, the type, and the joinOns.
*******************************************************************************/
public QJoinMetaData flip()
{
return (new QJoinMetaData()
.withLeftTable(rightTable)
.withRightTable(leftTable)
.withType(type.flip())
.withJoinOns(joinOns.stream().map(JoinOn::flip).toList()));
// todo - what about order bys??
}
/*******************************************************************************
** Getter for name
**

View File

@ -22,7 +22,10 @@
package com.kingsrook.qqq.backend.core.model.metadata.reporting;
import java.util.ArrayList;
import java.util.List;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryJoin;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
@ -32,9 +35,12 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
*******************************************************************************/
public class QReportDataSource
{
private String name;
private String sourceTable;
private QQueryFilter queryFilter;
private String name;
private String sourceTable;
private QQueryFilter queryFilter;
private List<QueryJoin> queryJoins = null;
private QCodeReference staticDataSupplier;
@ -174,4 +180,54 @@ public class QReportDataSource
return (this);
}
/*******************************************************************************
** Getter for queryJoins
**
*******************************************************************************/
public List<QueryJoin> getQueryJoins()
{
return queryJoins;
}
/*******************************************************************************
** Setter for queryJoins
**
*******************************************************************************/
public void setQueryJoins(List<QueryJoin> queryJoins)
{
this.queryJoins = queryJoins;
}
/*******************************************************************************
** Fluent setter for queryJoins
**
*******************************************************************************/
public QReportDataSource withQueryJoins(List<QueryJoin> queryJoins)
{
this.queryJoins = queryJoins;
return (this);
}
/*******************************************************************************
** Fluent setter for queryJoins
**
*******************************************************************************/
public QReportDataSource withQueryJoin(QueryJoin queryJoin)
{
if(this.queryJoins == null)
{
this.queryJoins = new ArrayList<>();
}
this.queryJoins.add(queryJoin);
return (this);
}
}

View File

@ -45,6 +45,27 @@ public class QReportField
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public QReportField()
{
}
/*******************************************************************************
** Constructor
**
*******************************************************************************/
public QReportField(String name)
{
this.name = name;
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -22,6 +22,7 @@
package com.kingsrook.qqq.backend.core.model.metadata.reporting;
import java.util.ArrayList;
import java.util.List;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
@ -150,6 +151,21 @@ public class QReportMetaData implements QAppChildMetaData
/*******************************************************************************
**
*******************************************************************************/
public QReportMetaData withInputField(QFieldMetaData inputField)
{
if(this.inputFields == null)
{
this.inputFields = new ArrayList<>();
}
this.inputFields.add(inputField);
return (this);
}
/*******************************************************************************
** Getter for processName
**
@ -218,6 +234,22 @@ public class QReportMetaData implements QAppChildMetaData
/*******************************************************************************
** Fluent setter for dataSources
**
*******************************************************************************/
public QReportMetaData withDataSource(QReportDataSource dataSource)
{
if(this.dataSources == null)
{
this.dataSources = new ArrayList<>();
}
this.dataSources.add(dataSource);
return (this);
}
/*******************************************************************************
** Getter for views
**
@ -252,6 +284,22 @@ public class QReportMetaData implements QAppChildMetaData
/*******************************************************************************
** Fluent setter for views
**
*******************************************************************************/
public QReportMetaData withView(QReportView view)
{
if(this.views == null)
{
this.views = new ArrayList<>();
}
this.views.add(view);
return (this);
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -34,6 +34,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMeta
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.reporting.QReportMetaData;
/*******************************************************************************
@ -63,6 +64,21 @@ public class RunReportForRecordProcess
/*******************************************************************************
** Create a process meta data builder for this type of process, pre-populated
** with attributes based on a given report.
*******************************************************************************/
public static Builder processMetaDataBuilder(QReportMetaData reportMetaData)
{
return (new Builder(defineProcessMetaData())
.withProcessName(reportMetaData.getProcessName())
.withReportName(reportMetaData.getName())
.withTableName(reportMetaData.getDataSources().get(0).getSourceTable())
.withIcon(reportMetaData.getIcon()));
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -55,6 +55,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleVal
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.reporting.QReportDataSource;
import com.kingsrook.qqq.backend.core.model.metadata.reporting.QReportMetaData;
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;
@ -1344,11 +1345,35 @@ class QInstanceValidatorTest
@Test
void testReportDataSourceNames()
{
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).getDataSources().get(0).setName(null),
assertValidationFailureReasons((qInstance) ->
{
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// enricher will give us a default name if only 1 data source, so, set 1st one to null name, then add a second //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
QReportMetaData report = qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON);
report.setDataSources(new ArrayList<>(report.getDataSources()));
report.getDataSources().get(0).setName(null);
report.getDataSources().add(new QReportDataSource()
.withName("2nd")
.withSourceTable(TestUtils.TABLE_NAME_PERSON)
);
},
"Missing name for a dataSource",
"unrecognized dataSourceName");
assertValidationFailureReasons((qInstance) -> qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON).getDataSources().get(0).setName(""),
assertValidationFailureReasons((qInstance) ->
{
///////////////////////////////////
// same as above, but "" vs null //
///////////////////////////////////
QReportMetaData report = qInstance.getReport(TestUtils.REPORT_NAME_SHAPES_PERSON);
report.setDataSources(new ArrayList<>(report.getDataSources()));
report.getDataSources().get(0).setName("");
report.getDataSources().add(new QReportDataSource()
.withName("2nd")
.withSourceTable(TestUtils.TABLE_NAME_PERSON)
);
},
"Missing name for a dataSource",
"unrecognized dataSourceName");