mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
Merged dev into feature/CE-847-bug-triggers-running
This commit is contained in:
@ -65,13 +65,14 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.Automatio
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.QTableAutomationDetails;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.QTableAutomationDetails;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TableAutomationAction;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TableAutomationAction;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TriggerEvent;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TriggerEvent;
|
||||||
import com.kingsrook.qqq.backend.core.model.savedfilters.SavedFilter;
|
import com.kingsrook.qqq.backend.core.model.savedviews.SavedView;
|
||||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||||
import org.apache.commons.lang.NotImplementedException;
|
import org.apache.commons.lang.NotImplementedException;
|
||||||
|
import org.json.JSONObject;
|
||||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||||
|
|
||||||
|
|
||||||
@ -387,13 +388,15 @@ public class PollingAutomationPerTableRunner implements Runnable
|
|||||||
if(filterId != null)
|
if(filterId != null)
|
||||||
{
|
{
|
||||||
GetInput getInput = new GetInput();
|
GetInput getInput = new GetInput();
|
||||||
getInput.setTableName(SavedFilter.TABLE_NAME);
|
getInput.setTableName(SavedView.TABLE_NAME);
|
||||||
getInput.setPrimaryKey(filterId);
|
getInput.setPrimaryKey(filterId);
|
||||||
GetOutput getOutput = new GetAction().execute(getInput);
|
GetOutput getOutput = new GetAction().execute(getInput);
|
||||||
if(getOutput.getRecord() != null)
|
if(getOutput.getRecord() != null)
|
||||||
{
|
{
|
||||||
SavedFilter savedFilter = new SavedFilter(getOutput.getRecord());
|
SavedView savedView = new SavedView(getOutput.getRecord());
|
||||||
filter = JsonUtils.toObject(savedFilter.getFilterJson(), QQueryFilter.class);
|
JSONObject viewJson = new JSONObject(savedView.getViewJson());
|
||||||
|
JSONObject queryFilter = viewJson.getJSONObject("queryFilter");
|
||||||
|
filter = JsonUtils.toObject(queryFilter.toString(), QQueryFilter.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -297,4 +297,21 @@ public enum DateTimeGroupBy
|
|||||||
ZonedDateTime zoned = instant.atZone(zoneId);
|
ZonedDateTime zoned = instant.atZone(zoneId);
|
||||||
return (zoned.plus(noOfChronoUnitsToAdd, chronoUnitToAdd).toInstant());
|
return (zoned.plus(noOfChronoUnitsToAdd, chronoUnitToAdd).toInstant());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static DateTimeFormatter sqlDateFormatToSelectedDateTimeFormatter(String sqlDateFormat)
|
||||||
|
{
|
||||||
|
for(DateTimeGroupBy value : values())
|
||||||
|
{
|
||||||
|
if(value.sqlDateFormat.equals(sqlDateFormat))
|
||||||
|
{
|
||||||
|
return (value.selectedStringFormatter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.AssociatedScript;
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Association;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.ExposedJoin;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.ExposedJoin;
|
||||||
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.QSupplementalTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
||||||
@ -87,6 +88,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.cache.CacheUseCase;
|
|||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.lambdas.UnsafeLambda;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -493,6 +495,11 @@ public class QInstanceValidator
|
|||||||
validateTableRecordSecurityLocks(qInstance, table);
|
validateTableRecordSecurityLocks(qInstance, table);
|
||||||
validateTableAssociations(qInstance, table);
|
validateTableAssociations(qInstance, table);
|
||||||
validateExposedJoins(qInstance, joinGraph, table);
|
validateExposedJoins(qInstance, joinGraph, table);
|
||||||
|
|
||||||
|
for(QSupplementalTableMetaData supplementalTableMetaData : CollectionUtils.nonNullMap(table.getSupplementalMetaData()).values())
|
||||||
|
{
|
||||||
|
supplementalTableMetaData.validate(qInstance, table, this);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1784,20 +1791,6 @@ public class QInstanceValidator
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@FunctionalInterface
|
|
||||||
interface UnsafeLambda
|
|
||||||
{
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
void run() throws Exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -28,7 +28,7 @@ import com.kingsrook.qqq.backend.core.model.data.QField;
|
|||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.TablesPossibleValueSourceMetaDataProvider;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.TablesPossibleValueSourceMetaDataProvider;
|
||||||
import com.kingsrook.qqq.backend.core.model.savedfilters.SavedFilter;
|
import com.kingsrook.qqq.backend.core.model.savedviews.SavedView;
|
||||||
import com.kingsrook.qqq.backend.core.model.scripts.Script;
|
import com.kingsrook.qqq.backend.core.model.scripts.Script;
|
||||||
|
|
||||||
|
|
||||||
@ -51,7 +51,7 @@ public class TableTrigger extends QRecordEntity
|
|||||||
@QField(possibleValueSourceName = TablesPossibleValueSourceMetaDataProvider.NAME)
|
@QField(possibleValueSourceName = TablesPossibleValueSourceMetaDataProvider.NAME)
|
||||||
private String tableName;
|
private String tableName;
|
||||||
|
|
||||||
@QField(possibleValueSourceName = SavedFilter.TABLE_NAME)
|
@QField(possibleValueSourceName = SavedView.TABLE_NAME)
|
||||||
private Integer filterId;
|
private Integer filterId;
|
||||||
|
|
||||||
@QField(possibleValueSourceName = Script.TABLE_NAME)
|
@QField(possibleValueSourceName = Script.TABLE_NAME)
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
package com.kingsrook.qqq.backend.core.model.metadata.tables;
|
package com.kingsrook.qqq.backend.core.model.metadata.tables;
|
||||||
|
|
||||||
|
|
||||||
|
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
|
|
||||||
|
|
||||||
@ -69,4 +70,16 @@ public abstract class QSupplementalTableMetaData
|
|||||||
// noop in base class //
|
// noop in base class //
|
||||||
////////////////////////
|
////////////////////////
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void validate(QInstance qInstance, QTableMetaData tableMetaData, QInstanceValidator qInstanceValidator)
|
||||||
|
{
|
||||||
|
////////////////////////
|
||||||
|
// noop in base class //
|
||||||
|
////////////////////////
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* QQQ - Low-code Application Framework for Engineers.
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
* contact@kingsrook.com
|
* contact@kingsrook.com
|
||||||
* https://github.com/Kingsrook/
|
* https://github.com/Kingsrook/
|
||||||
@ -19,7 +19,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.kingsrook.qqq.backend.core.model.savedfilters;
|
package com.kingsrook.qqq.backend.core.model.savedviews;
|
||||||
|
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@ -32,9 +32,9 @@ import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Entity bean for the saved filter table
|
** Entity bean for the saved filter table
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class SavedFilter extends QRecordEntity
|
public class SavedView extends QRecordEntity
|
||||||
{
|
{
|
||||||
public static final String TABLE_NAME = "savedFilter";
|
public static final String TABLE_NAME = "savedView";
|
||||||
|
|
||||||
@QField(isEditable = false)
|
@QField(isEditable = false)
|
||||||
private Integer id;
|
private Integer id;
|
||||||
@ -55,7 +55,7 @@ public class SavedFilter extends QRecordEntity
|
|||||||
private String userId;
|
private String userId;
|
||||||
|
|
||||||
@QField(isEditable = false)
|
@QField(isEditable = false)
|
||||||
private String filterJson;
|
private String viewJson;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -63,7 +63,7 @@ public class SavedFilter extends QRecordEntity
|
|||||||
** Constructor
|
** Constructor
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public SavedFilter()
|
public SavedView()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ public class SavedFilter extends QRecordEntity
|
|||||||
** Constructor
|
** Constructor
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public SavedFilter(QRecord qRecord) throws QException
|
public SavedView(QRecord qRecord) throws QException
|
||||||
{
|
{
|
||||||
populateFromQRecord(qRecord);
|
populateFromQRecord(qRecord);
|
||||||
}
|
}
|
||||||
@ -172,7 +172,7 @@ public class SavedFilter extends QRecordEntity
|
|||||||
** Fluent setter for label
|
** Fluent setter for label
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public SavedFilter withLabel(String label)
|
public SavedView withLabel(String label)
|
||||||
{
|
{
|
||||||
this.label = label;
|
this.label = label;
|
||||||
return (this);
|
return (this);
|
||||||
@ -206,7 +206,7 @@ public class SavedFilter extends QRecordEntity
|
|||||||
** Fluent setter for tableName
|
** Fluent setter for tableName
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public SavedFilter withTableName(String tableName)
|
public SavedView withTableName(String tableName)
|
||||||
{
|
{
|
||||||
this.tableName = tableName;
|
this.tableName = tableName;
|
||||||
return (this);
|
return (this);
|
||||||
@ -240,7 +240,7 @@ public class SavedFilter extends QRecordEntity
|
|||||||
** Fluent setter for userId
|
** Fluent setter for userId
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public SavedFilter withUserId(String userId)
|
public SavedView withUserId(String userId)
|
||||||
{
|
{
|
||||||
this.userId = userId;
|
this.userId = userId;
|
||||||
return (this);
|
return (this);
|
||||||
@ -249,34 +249,31 @@ public class SavedFilter extends QRecordEntity
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for filterJson
|
** Getter for viewJson
|
||||||
**
|
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public String getFilterJson()
|
public String getViewJson()
|
||||||
{
|
{
|
||||||
return filterJson;
|
return (this.viewJson);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Setter for filterJson
|
** Setter for viewJson
|
||||||
**
|
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void setFilterJson(String filterJson)
|
public void setViewJson(String viewJson)
|
||||||
{
|
{
|
||||||
this.filterJson = filterJson;
|
this.viewJson = viewJson;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent setter for filterJson
|
** Fluent setter for viewJson
|
||||||
**
|
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public SavedFilter withFilterJson(String filterJson)
|
public SavedView withViewJson(String viewJson)
|
||||||
{
|
{
|
||||||
this.filterJson = filterJson;
|
this.viewJson = viewJson;
|
||||||
return (this);
|
return (this);
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* QQQ - Low-code Application Framework for Engineers.
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
* contact@kingsrook.com
|
* contact@kingsrook.com
|
||||||
* https://github.com/Kingsrook/
|
* https://github.com/Kingsrook/
|
||||||
@ -19,25 +19,31 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.kingsrook.qqq.backend.core.model.savedfilters;
|
package com.kingsrook.qqq.backend.core.model.savedviews;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
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.QInstance;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PVSValueFormatAndFields;
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.PVSValueFormatAndFields;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
||||||
|
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.processes.implementations.savedfilters.DeleteSavedFilterProcess;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
||||||
import com.kingsrook.qqq.backend.core.processes.implementations.savedfilters.QuerySavedFilterProcess;
|
import com.kingsrook.qqq.backend.core.processes.implementations.savedviews.DeleteSavedViewProcess;
|
||||||
import com.kingsrook.qqq.backend.core.processes.implementations.savedfilters.StoreSavedFilterProcess;
|
import com.kingsrook.qqq.backend.core.processes.implementations.savedviews.QuerySavedViewProcess;
|
||||||
|
import com.kingsrook.qqq.backend.core.processes.implementations.savedviews.StoreSavedViewProcess;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class SavedFiltersMetaDataProvider
|
public class SavedViewsMetaDataProvider
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
@ -46,11 +52,11 @@ public class SavedFiltersMetaDataProvider
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void defineAll(QInstance instance, String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
public void defineAll(QInstance instance, String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||||
{
|
{
|
||||||
instance.addTable(defineSavedFilterTable(backendName, backendDetailEnricher));
|
instance.addTable(defineSavedViewTable(backendName, backendDetailEnricher));
|
||||||
instance.addPossibleValueSource(defineSavedFilterPossibleValueSource());
|
instance.addPossibleValueSource(defineSavedViewPossibleValueSource());
|
||||||
instance.addProcess(QuerySavedFilterProcess.getProcessMetaData());
|
instance.addProcess(QuerySavedViewProcess.getProcessMetaData());
|
||||||
instance.addProcess(StoreSavedFilterProcess.getProcessMetaData());
|
instance.addProcess(StoreSavedViewProcess.getProcessMetaData());
|
||||||
instance.addProcess(DeleteSavedFilterProcess.getProcessMetaData());
|
instance.addProcess(DeleteSavedViewProcess.getProcessMetaData());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -58,16 +64,21 @@ public class SavedFiltersMetaDataProvider
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private QTableMetaData defineSavedFilterTable(String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
public QTableMetaData defineSavedViewTable(String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||||
{
|
{
|
||||||
QTableMetaData table = new QTableMetaData()
|
QTableMetaData table = new QTableMetaData()
|
||||||
.withName(SavedFilter.TABLE_NAME)
|
.withName(SavedView.TABLE_NAME)
|
||||||
.withLabel("Saved Filter")
|
.withLabel("Saved View")
|
||||||
.withRecordLabelFormat("%s")
|
.withRecordLabelFormat("%s")
|
||||||
.withRecordLabelFields("label")
|
.withRecordLabelFields("label")
|
||||||
.withBackendName(backendName)
|
.withBackendName(backendName)
|
||||||
.withPrimaryKeyField("id")
|
.withPrimaryKeyField("id")
|
||||||
.withFieldsFromEntity(SavedFilter.class);
|
.withFieldsFromEntity(SavedView.class)
|
||||||
|
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "label")))
|
||||||
|
.withSection(new QFieldSection("data", new QIcon().withName("text_snippet"), Tier.T2, List.of("userId", "tableName", "viewJson")))
|
||||||
|
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")));
|
||||||
|
|
||||||
|
table.getField("viewJson").withFieldAdornment(new FieldAdornment(AdornmentType.CODE_EDITOR).withValue(AdornmentType.CodeEditorValues.languageMode("json")));
|
||||||
|
|
||||||
if(backendDetailEnricher != null)
|
if(backendDetailEnricher != null)
|
||||||
{
|
{
|
||||||
@ -82,12 +93,12 @@ public class SavedFiltersMetaDataProvider
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private QPossibleValueSource defineSavedFilterPossibleValueSource()
|
private QPossibleValueSource defineSavedViewPossibleValueSource()
|
||||||
{
|
{
|
||||||
return new QPossibleValueSource()
|
return new QPossibleValueSource()
|
||||||
.withName(SavedFilter.TABLE_NAME)
|
.withName(SavedView.TABLE_NAME)
|
||||||
.withType(QPossibleValueSourceType.TABLE)
|
.withType(QPossibleValueSourceType.TABLE)
|
||||||
.withTableName(SavedFilter.TABLE_NAME)
|
.withTableName(SavedView.TABLE_NAME)
|
||||||
.withValueFormatAndFields(PVSValueFormatAndFields.LABEL_ONLY)
|
.withValueFormatAndFields(PVSValueFormatAndFields.LABEL_ONLY)
|
||||||
.withOrderByField("label");
|
.withOrderByField("label");
|
||||||
}
|
}
|
@ -24,6 +24,10 @@ package com.kingsrook.qqq.backend.core.modules.backend.implementations.memory;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@ -35,6 +39,7 @@ import java.util.Objects;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.DateTimeGroupBy;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.helpers.ValidateRecordSecurityLockHelper;
|
import com.kingsrook.qqq.backend.core.actions.tables.helpers.ValidateRecordSecurityLockHelper;
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
@ -66,6 +71,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
|||||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.utils.BackendQueryFilterUtils;
|
import com.kingsrook.qqq.backend.core.modules.backend.implementations.utils.BackendQueryFilterUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
|
|
||||||
|
|
||||||
@ -577,7 +583,11 @@ public class MemoryRecordStore
|
|||||||
for(GroupBy groupBy : groupBys)
|
for(GroupBy groupBy : groupBys)
|
||||||
{
|
{
|
||||||
Serializable groupByValue = record.getValue(groupBy.getFieldName());
|
Serializable groupByValue = record.getValue(groupBy.getFieldName());
|
||||||
if(groupBy.getType() != null)
|
if(StringUtils.hasContent(groupBy.getFormatString()))
|
||||||
|
{
|
||||||
|
groupByValue = applyFormatString(groupByValue, groupBy);
|
||||||
|
}
|
||||||
|
else if(groupBy.getType() != null)
|
||||||
{
|
{
|
||||||
groupByValue = ValueUtils.getValueAsFieldType(groupBy.getType(), groupByValue);
|
groupByValue = ValueUtils.getValueAsFieldType(groupBy.getType(), groupByValue);
|
||||||
}
|
}
|
||||||
@ -629,7 +639,9 @@ public class MemoryRecordStore
|
|||||||
/////////////////////
|
/////////////////////
|
||||||
if(aggregateInput.getFilter() != null && CollectionUtils.nullSafeHasContents(aggregateInput.getFilter().getOrderBys()))
|
if(aggregateInput.getFilter() != null && CollectionUtils.nullSafeHasContents(aggregateInput.getFilter().getOrderBys()))
|
||||||
{
|
{
|
||||||
Comparator<AggregateResult> comparator = null;
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// lambda to compare 2 serializables, as we'll assume (& cast) them to Comparables //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
Comparator<Serializable> serializableComparator = (Serializable a, Serializable b) ->
|
Comparator<Serializable> serializableComparator = (Serializable a, Serializable b) ->
|
||||||
{
|
{
|
||||||
if(a == null && b == null)
|
if(a == null && b == null)
|
||||||
@ -647,9 +659,15 @@ public class MemoryRecordStore
|
|||||||
return ((Comparable) a).compareTo(b);
|
return ((Comparable) a).compareTo(b);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// reverse of the lambda above (we had some errors calling .reversed() on the comparator we were building, so this seemed simpler & worked) //
|
||||||
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
Comparator<Serializable> reverseSerializableComparator = (Serializable a, Serializable b) -> -serializableComparator.compare(a, b);
|
||||||
|
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
// build a comparator out of all the orderBys //
|
// build a comparator out of all the orderBys //
|
||||||
////////////////////////////////////////////////
|
////////////////////////////////////////////////
|
||||||
|
Comparator<AggregateResult> comparator = null;
|
||||||
for(QFilterOrderBy orderBy : aggregateInput.getFilter().getOrderBys())
|
for(QFilterOrderBy orderBy : aggregateInput.getFilter().getOrderBys())
|
||||||
{
|
{
|
||||||
Function<AggregateResult, Serializable> keyExtractor = aggregateResult ->
|
Function<AggregateResult, Serializable> keyExtractor = aggregateResult ->
|
||||||
@ -670,16 +688,11 @@ public class MemoryRecordStore
|
|||||||
|
|
||||||
if(comparator == null)
|
if(comparator == null)
|
||||||
{
|
{
|
||||||
comparator = Comparator.comparing(keyExtractor, serializableComparator);
|
comparator = Comparator.comparing(keyExtractor, orderBy.getIsAscending() ? serializableComparator : reverseSerializableComparator);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
comparator = comparator.thenComparing(keyExtractor, serializableComparator);
|
comparator = comparator.thenComparing(keyExtractor, orderBy.getIsAscending() ? serializableComparator : reverseSerializableComparator);
|
||||||
}
|
|
||||||
|
|
||||||
if(!orderBy.getIsAscending())
|
|
||||||
{
|
|
||||||
comparator = comparator.reversed();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -696,6 +709,57 @@ public class MemoryRecordStore
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private Serializable applyFormatString(Serializable value, GroupBy groupBy) throws QException
|
||||||
|
{
|
||||||
|
if(value == null)
|
||||||
|
{
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
|
String formatString = groupBy.getFormatString();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(formatString.startsWith("DATE_FORMAT"))
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// one known-use case we have here looks like this: //
|
||||||
|
// DATE_FORMAT(CONVERT_TZ(%s, 'UTC', 'UTC'), '%%Y-%%m-%%dT%%H') //
|
||||||
|
// ... for now, let's just try to support the formatting bit at the end... //
|
||||||
|
// todo - support the CONVERT_TZ bit too! //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
String sqlDateTimeFormat = formatString.replaceFirst(".*'%%", "%%").replaceFirst("'.*", "");
|
||||||
|
DateTimeFormatter dateTimeFormatter = DateTimeGroupBy.sqlDateFormatToSelectedDateTimeFormatter(sqlDateTimeFormat);
|
||||||
|
if(dateTimeFormatter == null)
|
||||||
|
{
|
||||||
|
throw (new QException("Unsupported sql dateTime format string [" + sqlDateTimeFormat + "] for MemoryRecordStore"));
|
||||||
|
}
|
||||||
|
|
||||||
|
String valueAsString = ValueUtils.getValueAsString(value);
|
||||||
|
Instant valueAsInstant = ValueUtils.getValueAsInstant(valueAsString);
|
||||||
|
ZonedDateTime zonedDateTime = valueAsInstant.atZone(ZoneId.systemDefault());
|
||||||
|
return (dateTimeFormatter.format(zonedDateTime));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw (new QException("Unsupported group-by format string [" + formatString + "] for MemoryRecordStore"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(QException qe)
|
||||||
|
{
|
||||||
|
throw (qe);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
throw (new QException("Error applying format string [" + formatString + "] to group by value [" + value + "]", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -25,6 +25,8 @@ package com.kingsrook.qqq.backend.core.processes.implementations.columnstats;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.ZoneId;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@ -173,11 +175,10 @@ public class ColumnStatsStep implements BackendStep
|
|||||||
Aggregate aggregate = new Aggregate(table.getPrimaryKeyField(), AggregateOperator.COUNT).withFieldType(QFieldType.DECIMAL);
|
Aggregate aggregate = new Aggregate(table.getPrimaryKeyField(), AggregateOperator.COUNT).withFieldType(QFieldType.DECIMAL);
|
||||||
GroupBy groupBy = new GroupBy(field.getType(), fieldName);
|
GroupBy groupBy = new GroupBy(field.getType(), fieldName);
|
||||||
|
|
||||||
// todo - something here about "by-date, not time"
|
// todo - something here about an input param to specify how you want dates & date-times grouped
|
||||||
if(field.getType().equals(QFieldType.DATE_TIME))
|
if(field.getType().equals(QFieldType.DATE_TIME))
|
||||||
{
|
{
|
||||||
// groupBy = new GroupBy(field.getType(), fieldName, "DATE(%s)");
|
String sqlExpression = DateTimeGroupBy.HOUR.getSqlExpression(ZoneId.systemDefault());
|
||||||
String sqlExpression = DateTimeGroupBy.HOUR.getSqlExpression();
|
|
||||||
groupBy = new GroupBy(QFieldType.STRING, fieldName, sqlExpression);
|
groupBy = new GroupBy(QFieldType.STRING, fieldName, sqlExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,6 +231,12 @@ public class ColumnStatsStep implements BackendStep
|
|||||||
for(AggregateResult result : aggregateOutput.getResults())
|
for(AggregateResult result : aggregateOutput.getResults())
|
||||||
{
|
{
|
||||||
Serializable value = result.getGroupByValue(groupBy);
|
Serializable value = result.getGroupByValue(groupBy);
|
||||||
|
|
||||||
|
if(field.getType().equals(QFieldType.DATE_TIME) && value != null)
|
||||||
|
{
|
||||||
|
value = Instant.parse(value + ":00:00Z");
|
||||||
|
}
|
||||||
|
|
||||||
Integer count = ValueUtils.getValueAsInteger(result.getAggregateValue(aggregate));
|
Integer count = ValueUtils.getValueAsInteger(result.getAggregateValue(aggregate));
|
||||||
valueCounts.add(new QRecord().withValue(fieldName, value).withValue("count", count));
|
valueCounts.add(new QRecord().withValue(fieldName, value).withValue("count", count));
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* QQQ - Low-code Application Framework for Engineers.
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
* contact@kingsrook.com
|
* contact@kingsrook.com
|
||||||
* https://github.com/Kingsrook/
|
* https://github.com/Kingsrook/
|
||||||
@ -19,7 +19,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.kingsrook.qqq.backend.core.processes.implementations.savedfilters;
|
package com.kingsrook.qqq.backend.core.processes.implementations.savedviews;
|
||||||
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -34,15 +34,15 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
|||||||
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.processes.QBackendStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||||
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.savedfilters.SavedFilter;
|
import com.kingsrook.qqq.backend.core.model.savedviews.SavedView;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Process used by the delete filter dialog
|
** Process used by the delete view dialog
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class DeleteSavedFilterProcess implements BackendStep
|
public class DeleteSavedViewProcess implements BackendStep
|
||||||
{
|
{
|
||||||
private static final QLogger LOG = QLogger.getLogger(DeleteSavedFilterProcess.class);
|
private static final QLogger LOG = QLogger.getLogger(DeleteSavedViewProcess.class);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -52,10 +52,10 @@ public class DeleteSavedFilterProcess implements BackendStep
|
|||||||
public static QProcessMetaData getProcessMetaData()
|
public static QProcessMetaData getProcessMetaData()
|
||||||
{
|
{
|
||||||
return (new QProcessMetaData()
|
return (new QProcessMetaData()
|
||||||
.withName("deleteSavedFilter")
|
.withName("deleteSavedView")
|
||||||
.withStepList(List.of(
|
.withStepList(List.of(
|
||||||
new QBackendStepMetaData()
|
new QBackendStepMetaData()
|
||||||
.withCode(new QCodeReference(DeleteSavedFilterProcess.class))
|
.withCode(new QCodeReference(DeleteSavedViewProcess.class))
|
||||||
.withName("delete")
|
.withName("delete")
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
@ -72,16 +72,16 @@ public class DeleteSavedFilterProcess implements BackendStep
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Integer savedFilterId = runBackendStepInput.getValueInteger("id");
|
Integer savedViewId = runBackendStepInput.getValueInteger("id");
|
||||||
|
|
||||||
DeleteInput input = new DeleteInput();
|
DeleteInput input = new DeleteInput();
|
||||||
input.setTableName(SavedFilter.TABLE_NAME);
|
input.setTableName(SavedView.TABLE_NAME);
|
||||||
input.setPrimaryKeys(List.of(savedFilterId));
|
input.setPrimaryKeys(List.of(savedViewId));
|
||||||
new DeleteAction().execute(input);
|
new DeleteAction().execute(input);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
LOG.warn("Error deleting saved filter", e);
|
LOG.warn("Error deleting saved view", e);
|
||||||
throw (e);
|
throw (e);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* QQQ - Low-code Application Framework for Engineers.
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
* contact@kingsrook.com
|
* contact@kingsrook.com
|
||||||
* https://github.com/Kingsrook/
|
* https://github.com/Kingsrook/
|
||||||
@ -19,7 +19,7 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.kingsrook.qqq.backend.core.processes.implementations.savedfilters;
|
package com.kingsrook.qqq.backend.core.processes.implementations.savedviews;
|
||||||
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
@ -43,15 +43,15 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
|||||||
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.processes.QBackendStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||||
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.savedfilters.SavedFilter;
|
import com.kingsrook.qqq.backend.core.model.savedviews.SavedView;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Process used by the saved filter dialogs
|
** Process used by the saved view dialogs
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class QuerySavedFilterProcess implements BackendStep
|
public class QuerySavedViewProcess implements BackendStep
|
||||||
{
|
{
|
||||||
private static final QLogger LOG = QLogger.getLogger(QuerySavedFilterProcess.class);
|
private static final QLogger LOG = QLogger.getLogger(QuerySavedViewProcess.class);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -61,10 +61,10 @@ public class QuerySavedFilterProcess implements BackendStep
|
|||||||
public static QProcessMetaData getProcessMetaData()
|
public static QProcessMetaData getProcessMetaData()
|
||||||
{
|
{
|
||||||
return (new QProcessMetaData()
|
return (new QProcessMetaData()
|
||||||
.withName("querySavedFilter")
|
.withName("querySavedView")
|
||||||
.withStepList(List.of(
|
.withStepList(List.of(
|
||||||
new QBackendStepMetaData()
|
new QBackendStepMetaData()
|
||||||
.withCode(new QCodeReference(QuerySavedFilterProcess.class))
|
.withCode(new QCodeReference(QuerySavedViewProcess.class))
|
||||||
.withName("query")
|
.withName("query")
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
@ -81,36 +81,36 @@ public class QuerySavedFilterProcess implements BackendStep
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Integer savedFilterId = runBackendStepInput.getValueInteger("id");
|
Integer savedViewId = runBackendStepInput.getValueInteger("id");
|
||||||
if(savedFilterId != null)
|
if(savedViewId != null)
|
||||||
{
|
{
|
||||||
GetInput input = new GetInput();
|
GetInput input = new GetInput();
|
||||||
input.setTableName(SavedFilter.TABLE_NAME);
|
input.setTableName(SavedView.TABLE_NAME);
|
||||||
input.setPrimaryKey(savedFilterId);
|
input.setPrimaryKey(savedViewId);
|
||||||
|
|
||||||
GetOutput output = new GetAction().execute(input);
|
GetOutput output = new GetAction().execute(input);
|
||||||
runBackendStepOutput.addRecord(output.getRecord());
|
runBackendStepOutput.addRecord(output.getRecord());
|
||||||
runBackendStepOutput.addValue("savedFilter", output.getRecord());
|
runBackendStepOutput.addValue("savedView", output.getRecord());
|
||||||
runBackendStepOutput.addValue("savedFilterList", (Serializable) List.of(output.getRecord()));
|
runBackendStepOutput.addValue("savedViewList", (Serializable) List.of(output.getRecord()));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
String tableName = runBackendStepInput.getValueString("tableName");
|
String tableName = runBackendStepInput.getValueString("tableName");
|
||||||
|
|
||||||
QueryInput input = new QueryInput();
|
QueryInput input = new QueryInput();
|
||||||
input.setTableName(SavedFilter.TABLE_NAME);
|
input.setTableName(SavedView.TABLE_NAME);
|
||||||
input.setFilter(new QQueryFilter()
|
input.setFilter(new QQueryFilter()
|
||||||
.withCriteria(new QFilterCriteria("tableName", QCriteriaOperator.EQUALS, tableName))
|
.withCriteria(new QFilterCriteria("tableName", QCriteriaOperator.EQUALS, tableName))
|
||||||
.withOrderBy(new QFilterOrderBy("label")));
|
.withOrderBy(new QFilterOrderBy("label")));
|
||||||
|
|
||||||
QueryOutput output = new QueryAction().execute(input);
|
QueryOutput output = new QueryAction().execute(input);
|
||||||
runBackendStepOutput.setRecords(output.getRecords());
|
runBackendStepOutput.setRecords(output.getRecords());
|
||||||
runBackendStepOutput.addValue("savedFilterList", (Serializable) output.getRecords());
|
runBackendStepOutput.addValue("savedViewList", (Serializable) output.getRecords());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
LOG.warn("Error deleting saved filter", e);
|
LOG.warn("Error querying for saved views", e);
|
||||||
throw (e);
|
throw (e);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* QQQ - Low-code Application Framework for Engineers.
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
* contact@kingsrook.com
|
* contact@kingsrook.com
|
||||||
* https://github.com/Kingsrook/
|
* https://github.com/Kingsrook/
|
||||||
@ -19,37 +19,45 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.kingsrook.qqq.backend.core.processes.implementations.savedfilters;
|
package com.kingsrook.qqq.backend.core.processes.implementations.savedviews;
|
||||||
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||||
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.tables.InsertAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
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.processes.QBackendStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||||
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.savedfilters.SavedFilter;
|
import com.kingsrook.qqq.backend.core.model.savedviews.SavedView;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Process used by the saved filter dialog
|
** Process used by the saved view dialog
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class StoreSavedFilterProcess implements BackendStep
|
public class StoreSavedViewProcess implements BackendStep
|
||||||
{
|
{
|
||||||
private static final QLogger LOG = QLogger.getLogger(StoreSavedFilterProcess.class);
|
private static final QLogger LOG = QLogger.getLogger(StoreSavedViewProcess.class);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -59,10 +67,10 @@ public class StoreSavedFilterProcess implements BackendStep
|
|||||||
public static QProcessMetaData getProcessMetaData()
|
public static QProcessMetaData getProcessMetaData()
|
||||||
{
|
{
|
||||||
return (new QProcessMetaData()
|
return (new QProcessMetaData()
|
||||||
.withName("storeSavedFilter")
|
.withName("storeSavedView")
|
||||||
.withStepList(List.of(
|
.withStepList(List.of(
|
||||||
new QBackendStepMetaData()
|
new QBackendStepMetaData()
|
||||||
.withCode(new QCodeReference(StoreSavedFilterProcess.class))
|
.withCode(new QCodeReference(StoreSavedViewProcess.class))
|
||||||
.withName("store")
|
.withName("store")
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
@ -79,39 +87,73 @@ public class StoreSavedFilterProcess implements BackendStep
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
String userId = QContext.getQSession().getUser().getIdReference();
|
||||||
|
String tableName = runBackendStepInput.getValueString("tableName");
|
||||||
|
String label = runBackendStepInput.getValueString("label");
|
||||||
|
|
||||||
QRecord qRecord = new QRecord()
|
QRecord qRecord = new QRecord()
|
||||||
.withValue("id", runBackendStepInput.getValueInteger("id"))
|
.withValue("id", runBackendStepInput.getValueInteger("id"))
|
||||||
.withValue("label", runBackendStepInput.getValueString("label"))
|
.withValue("viewJson", runBackendStepInput.getValueString("viewJson"))
|
||||||
.withValue("tableName", runBackendStepInput.getValueString("tableName"))
|
.withValue("label", label)
|
||||||
.withValue("filterJson", runBackendStepInput.getValueString("filterJson"))
|
.withValue("tableName", tableName)
|
||||||
.withValue("userId", runBackendStepInput.getSession().getUser().getIdReference());
|
.withValue("userId", userId);
|
||||||
|
|
||||||
List<QRecord> savedFilterList = new ArrayList<>();
|
List<QRecord> savedViewList;
|
||||||
if(qRecord.getValueInteger("id") == null)
|
if(qRecord.getValueInteger("id") == null)
|
||||||
{
|
{
|
||||||
|
checkForDuplicates(userId, tableName, label, null);
|
||||||
|
|
||||||
InsertInput input = new InsertInput();
|
InsertInput input = new InsertInput();
|
||||||
input.setTableName(SavedFilter.TABLE_NAME);
|
input.setTableName(SavedView.TABLE_NAME);
|
||||||
input.setRecords(List.of(qRecord));
|
input.setRecords(List.of(qRecord));
|
||||||
|
|
||||||
InsertOutput output = new InsertAction().execute(input);
|
InsertOutput output = new InsertAction().execute(input);
|
||||||
savedFilterList = output.getRecords();
|
savedViewList = output.getRecords();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
checkForDuplicates(userId, tableName, label, qRecord.getValueInteger("id"));
|
||||||
|
|
||||||
UpdateInput input = new UpdateInput();
|
UpdateInput input = new UpdateInput();
|
||||||
input.setTableName(SavedFilter.TABLE_NAME);
|
input.setTableName(SavedView.TABLE_NAME);
|
||||||
input.setRecords(List.of(qRecord));
|
input.setRecords(List.of(qRecord));
|
||||||
|
|
||||||
UpdateOutput output = new UpdateAction().execute(input);
|
UpdateOutput output = new UpdateAction().execute(input);
|
||||||
savedFilterList = output.getRecords();
|
savedViewList = output.getRecords();
|
||||||
}
|
}
|
||||||
|
|
||||||
runBackendStepOutput.addValue("savedFilterList", (Serializable) savedFilterList);
|
runBackendStepOutput.addValue("savedViewList", (Serializable) savedViewList);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
LOG.warn("Error storing data saved filter", e);
|
LOG.warn("Error storing saved view", e);
|
||||||
throw (e);
|
throw (e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static void checkForDuplicates(String userId, String tableName, String label, Integer id) throws QException
|
||||||
|
{
|
||||||
|
QueryInput queryInput = new QueryInput();
|
||||||
|
queryInput.setTableName(SavedView.TABLE_NAME);
|
||||||
|
queryInput.setFilter(new QQueryFilter(
|
||||||
|
new QFilterCriteria("userId", QCriteriaOperator.EQUALS, userId),
|
||||||
|
new QFilterCriteria("tableName", QCriteriaOperator.EQUALS, tableName),
|
||||||
|
new QFilterCriteria("label", QCriteriaOperator.EQUALS, label)));
|
||||||
|
|
||||||
|
if(id != null)
|
||||||
|
{
|
||||||
|
queryInput.getFilter().addCriteria(new QFilterCriteria("id", QCriteriaOperator.NOT_EQUALS, id));
|
||||||
|
}
|
||||||
|
|
||||||
|
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||||
|
if(CollectionUtils.nullSafeHasContents(queryOutput.getRecords()))
|
||||||
|
{
|
||||||
|
throw (new QUserFacingException("You already have a saved view on this table with this name."));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* 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.utils.lambdas;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface UnsafeLambda
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
void run() throws Exception;
|
||||||
|
|
||||||
|
}
|
@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.processes.implementations.columnstats;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||||
@ -91,4 +92,50 @@ class ColumnStatsStepTest extends BaseTest
|
|||||||
.hasFieldOrPropertyWithValue("percent", new BigDecimal("16.67"));
|
.hasFieldOrPropertyWithValue("percent", new BigDecimal("16.67"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testDateTimesRollupByHour() throws QException
|
||||||
|
{
|
||||||
|
InsertInput insertInput = new InsertInput();
|
||||||
|
insertInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||||
|
insertInput.setRecords(List.of(
|
||||||
|
new QRecord().withValue("timestamp", Instant.parse("2024-01-31T09:59:01Z")),
|
||||||
|
new QRecord().withValue("timestamp", Instant.parse("2024-01-31T09:59:59Z")),
|
||||||
|
new QRecord().withValue("timestamp", Instant.parse("2024-01-31T10:00:00Z")),
|
||||||
|
new QRecord().withValue("timestamp", Instant.parse("2024-01-31T10:01:01Z")),
|
||||||
|
new QRecord().withValue("timestamp", Instant.parse("2024-01-31T10:59:59Z")),
|
||||||
|
new QRecord().withValue("timestamp", null)
|
||||||
|
));
|
||||||
|
new InsertAction().execute(insertInput);
|
||||||
|
|
||||||
|
RunBackendStepInput input = new RunBackendStepInput();
|
||||||
|
input.addValue("tableName", TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||||
|
input.addValue("fieldName", "timestamp");
|
||||||
|
input.addValue("orderBy", "count.desc");
|
||||||
|
|
||||||
|
RunBackendStepOutput output = new RunBackendStepOutput();
|
||||||
|
new ColumnStatsStep().run(input, output);
|
||||||
|
|
||||||
|
Map<String, Serializable> values = output.getValues();
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
List<QRecord> valueCounts = (List<QRecord>) values.get("valueCounts");
|
||||||
|
|
||||||
|
assertThat(valueCounts.get(0).getValues())
|
||||||
|
.hasFieldOrPropertyWithValue("timestamp", Instant.parse("2024-01-31T10:00:00Z"))
|
||||||
|
.hasFieldOrPropertyWithValue("count", 3);
|
||||||
|
|
||||||
|
assertThat(valueCounts.get(1).getValues())
|
||||||
|
.hasFieldOrPropertyWithValue("timestamp", Instant.parse("2024-01-31T09:00:00Z"))
|
||||||
|
.hasFieldOrPropertyWithValue("count", 2);
|
||||||
|
|
||||||
|
assertThat(valueCounts.get(2).getValues())
|
||||||
|
.hasFieldOrPropertyWithValue("timestamp", null)
|
||||||
|
.hasFieldOrPropertyWithValue("count", 1);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,143 +0,0 @@
|
|||||||
/*
|
|
||||||
* QQQ - Low-code Application Framework for Engineers.
|
|
||||||
* Copyright (C) 2021-2023. 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.savedfilters;
|
|
||||||
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
|
|
||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
|
||||||
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.actions.tables.query.QCriteriaOperator;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.savedfilters.SavedFiltersMetaDataProvider;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
|
||||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** Unit test for all saved filter processes
|
|
||||||
*******************************************************************************/
|
|
||||||
class SavedFilterProcessTests extends BaseTest
|
|
||||||
{
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
**
|
|
||||||
*******************************************************************************/
|
|
||||||
@Test
|
|
||||||
void test() throws QException
|
|
||||||
{
|
|
||||||
QInstance qInstance = QContext.getQInstance();
|
|
||||||
new SavedFiltersMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
|
||||||
String tableName = TestUtils.TABLE_NAME_PERSON_MEMORY;
|
|
||||||
|
|
||||||
{
|
|
||||||
///////////////////////////////////////////
|
|
||||||
// query - should be no filters to start //
|
|
||||||
///////////////////////////////////////////
|
|
||||||
RunProcessInput runProcessInput = new RunProcessInput();
|
|
||||||
runProcessInput.setProcessName(QuerySavedFilterProcess.getProcessMetaData().getName());
|
|
||||||
runProcessInput.addValue("tableName", tableName);
|
|
||||||
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
|
||||||
assertEquals(0, ((List<?>) runProcessOutput.getValues().get("savedFilterList")).size());
|
|
||||||
}
|
|
||||||
|
|
||||||
Integer savedFilterId;
|
|
||||||
{
|
|
||||||
////////////////////////
|
|
||||||
// store a new filter //
|
|
||||||
////////////////////////
|
|
||||||
RunProcessInput runProcessInput = new RunProcessInput();
|
|
||||||
runProcessInput.setProcessName(StoreSavedFilterProcess.getProcessMetaData().getName());
|
|
||||||
runProcessInput.addValue("label", "My Filter");
|
|
||||||
runProcessInput.addValue("tableName", tableName);
|
|
||||||
runProcessInput.addValue("filterJson", JsonUtils.toJson(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.EQUALS, 47))));
|
|
||||||
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
|
||||||
List<QRecord> savedFilterList = (List<QRecord>) runProcessOutput.getValues().get("savedFilterList");
|
|
||||||
assertEquals(1, savedFilterList.size());
|
|
||||||
savedFilterId = savedFilterList.get(0).getValueInteger("id");
|
|
||||||
assertNotNull(savedFilterId);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
////////////////////////////////////
|
|
||||||
// query - should find our filter //
|
|
||||||
////////////////////////////////////
|
|
||||||
RunProcessInput runProcessInput = new RunProcessInput();
|
|
||||||
runProcessInput.setProcessName(QuerySavedFilterProcess.getProcessMetaData().getName());
|
|
||||||
runProcessInput.addValue("tableName", tableName);
|
|
||||||
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
|
||||||
List<QRecord> savedFilterList = (List<QRecord>) runProcessOutput.getValues().get("savedFilterList");
|
|
||||||
assertEquals(1, savedFilterList.size());
|
|
||||||
assertEquals(1, savedFilterList.get(0).getValueInteger("id"));
|
|
||||||
assertEquals("My Filter", savedFilterList.get(0).getValueString("label"));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
///////////////////////
|
|
||||||
// update our filter //
|
|
||||||
///////////////////////
|
|
||||||
RunProcessInput runProcessInput = new RunProcessInput();
|
|
||||||
runProcessInput.setProcessName(StoreSavedFilterProcess.getProcessMetaData().getName());
|
|
||||||
runProcessInput.addValue("id", savedFilterId);
|
|
||||||
runProcessInput.addValue("label", "My Updated Filter");
|
|
||||||
runProcessInput.addValue("tableName", tableName);
|
|
||||||
runProcessInput.addValue("filterJson", JsonUtils.toJson(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.EQUALS, 47))));
|
|
||||||
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
|
||||||
List<QRecord> savedFilterList = (List<QRecord>) runProcessOutput.getValues().get("savedFilterList");
|
|
||||||
assertEquals(1, savedFilterList.size());
|
|
||||||
assertEquals(1, savedFilterList.get(0).getValueInteger("id"));
|
|
||||||
assertEquals("My Updated Filter", savedFilterList.get(0).getValueString("label"));
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
///////////////////////
|
|
||||||
// delete our filter //
|
|
||||||
///////////////////////
|
|
||||||
RunProcessInput runProcessInput = new RunProcessInput();
|
|
||||||
runProcessInput.setProcessName(DeleteSavedFilterProcess.getProcessMetaData().getName());
|
|
||||||
runProcessInput.addValue("id", savedFilterId);
|
|
||||||
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
////////////////////////////////////////
|
|
||||||
// query - should be no filters again //
|
|
||||||
////////////////////////////////////////
|
|
||||||
RunProcessInput runProcessInput = new RunProcessInput();
|
|
||||||
runProcessInput.setProcessName(QuerySavedFilterProcess.getProcessMetaData().getName());
|
|
||||||
runProcessInput.addValue("tableName", tableName);
|
|
||||||
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
|
||||||
assertEquals(0, ((List<?>) runProcessOutput.getValues().get("savedFilterList")).size());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,189 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||||
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
|
* contact@kingsrook.com
|
||||||
|
* https://github.com/Kingsrook/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.processes.implementations.savedviews;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.processes.RunProcessAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
|
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.RunProcessInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.savedviews.SavedViewsMetaDataProvider;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for all saved view processes
|
||||||
|
*******************************************************************************/
|
||||||
|
class SavedViewProcessTests extends BaseTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void test() throws QException
|
||||||
|
{
|
||||||
|
QInstance qInstance = QContext.getQInstance();
|
||||||
|
new SavedViewsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||||
|
String tableName = TestUtils.TABLE_NAME_PERSON_MEMORY;
|
||||||
|
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////
|
||||||
|
// query - should be no views to start //
|
||||||
|
/////////////////////////////////////////
|
||||||
|
RunProcessInput runProcessInput = new RunProcessInput();
|
||||||
|
runProcessInput.setProcessName(QuerySavedViewProcess.getProcessMetaData().getName());
|
||||||
|
runProcessInput.addValue("tableName", tableName);
|
||||||
|
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
||||||
|
assertEquals(0, ((List<?>) runProcessOutput.getValues().get("savedViewList")).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer savedViewId;
|
||||||
|
{
|
||||||
|
//////////////////////
|
||||||
|
// store a new view //
|
||||||
|
//////////////////////
|
||||||
|
RunProcessInput runProcessInput = new RunProcessInput();
|
||||||
|
runProcessInput.setProcessName(StoreSavedViewProcess.getProcessMetaData().getName());
|
||||||
|
runProcessInput.addValue("label", "My View");
|
||||||
|
runProcessInput.addValue("tableName", tableName);
|
||||||
|
runProcessInput.addValue("viewJson", JsonUtils.toJson(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.EQUALS, 47))));
|
||||||
|
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
||||||
|
List<QRecord> savedViewList = (List<QRecord>) runProcessOutput.getValues().get("savedViewList");
|
||||||
|
assertEquals(1, savedViewList.size());
|
||||||
|
savedViewId = savedViewList.get(0).getValueInteger("id");
|
||||||
|
assertNotNull(savedViewId);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
// try to store it again - should throw a "duplicate" exception //
|
||||||
|
//////////////////////////////////////////////////////////////////
|
||||||
|
assertThatThrownBy(() -> new RunProcessAction().execute(runProcessInput))
|
||||||
|
.isInstanceOf(QUserFacingException.class)
|
||||||
|
.hasMessageContaining("already have a saved view");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
///////////////////////////////////
|
||||||
|
// query - should find our views //
|
||||||
|
///////////////////////////////////
|
||||||
|
RunProcessInput runProcessInput = new RunProcessInput();
|
||||||
|
runProcessInput.setProcessName(QuerySavedViewProcess.getProcessMetaData().getName());
|
||||||
|
runProcessInput.addValue("tableName", tableName);
|
||||||
|
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
||||||
|
List<QRecord> savedViewList = (List<QRecord>) runProcessOutput.getValues().get("savedViewList");
|
||||||
|
assertEquals(1, savedViewList.size());
|
||||||
|
assertEquals(1, savedViewList.get(0).getValueInteger("id"));
|
||||||
|
assertEquals("My View", savedViewList.get(0).getValueString("label"));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
/////////////////////
|
||||||
|
// update our view //
|
||||||
|
/////////////////////
|
||||||
|
RunProcessInput runProcessInput = new RunProcessInput();
|
||||||
|
runProcessInput.setProcessName(StoreSavedViewProcess.getProcessMetaData().getName());
|
||||||
|
runProcessInput.addValue("id", savedViewId);
|
||||||
|
runProcessInput.addValue("label", "My Updated View");
|
||||||
|
runProcessInput.addValue("tableName", tableName);
|
||||||
|
runProcessInput.addValue("viewJson", JsonUtils.toJson(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.EQUALS, 47))));
|
||||||
|
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
||||||
|
List<QRecord> savedViewList = (List<QRecord>) runProcessOutput.getValues().get("savedViewList");
|
||||||
|
assertEquals(1, savedViewList.size());
|
||||||
|
assertEquals(1, savedViewList.get(0).getValueInteger("id"));
|
||||||
|
assertEquals("My Updated View", savedViewList.get(0).getValueString("label"));
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer anotherSavedViewId;
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// store a second one w/ different name (will be used below in update-dupe-check use-case) //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
RunProcessInput runProcessInput = new RunProcessInput();
|
||||||
|
runProcessInput.setProcessName(StoreSavedViewProcess.getProcessMetaData().getName());
|
||||||
|
runProcessInput.addValue("label", "My Second View");
|
||||||
|
runProcessInput.addValue("tableName", tableName);
|
||||||
|
runProcessInput.addValue("viewJson", JsonUtils.toJson(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.EQUALS, 47))));
|
||||||
|
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
||||||
|
List<QRecord> savedViewList = (List<QRecord>) runProcessOutput.getValues().get("savedViewList");
|
||||||
|
anotherSavedViewId = savedViewList.get(0).getValueInteger("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
// try to rename the second to match the first //
|
||||||
|
/////////////////////////////////////////////////
|
||||||
|
RunProcessInput runProcessInput = new RunProcessInput();
|
||||||
|
runProcessInput.setProcessName(StoreSavedViewProcess.getProcessMetaData().getName());
|
||||||
|
runProcessInput.addValue("id", anotherSavedViewId);
|
||||||
|
runProcessInput.addValue("label", "My Updated View");
|
||||||
|
runProcessInput.addValue("tableName", tableName);
|
||||||
|
runProcessInput.addValue("viewJson", JsonUtils.toJson(new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.EQUALS, 47))));
|
||||||
|
|
||||||
|
//////////////////////////////////////////
|
||||||
|
// should throw a "duplicate" exception //
|
||||||
|
//////////////////////////////////////////
|
||||||
|
assertThatThrownBy(() -> new RunProcessAction().execute(runProcessInput))
|
||||||
|
.isInstanceOf(QUserFacingException.class)
|
||||||
|
.hasMessageContaining("already have a saved view");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//////////////////////
|
||||||
|
// delete our views //
|
||||||
|
//////////////////////
|
||||||
|
RunProcessInput runProcessInput = new RunProcessInput();
|
||||||
|
runProcessInput.setProcessName(DeleteSavedViewProcess.getProcessMetaData().getName());
|
||||||
|
runProcessInput.addValue("id", savedViewId);
|
||||||
|
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
||||||
|
|
||||||
|
runProcessInput.addValue("id", anotherSavedViewId);
|
||||||
|
runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
//////////////////////////////////////
|
||||||
|
// query - should be no views again //
|
||||||
|
//////////////////////////////////////
|
||||||
|
RunProcessInput runProcessInput = new RunProcessInput();
|
||||||
|
runProcessInput.setProcessName(QuerySavedViewProcess.getProcessMetaData().getName());
|
||||||
|
runProcessInput.addValue("tableName", tableName);
|
||||||
|
RunProcessOutput runProcessOutput = new RunProcessAction().execute(runProcessInput);
|
||||||
|
assertEquals(0, ((List<?>) runProcessOutput.getValues().get("savedViewList")).size());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -66,7 +66,7 @@ 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.reporting.ReportType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.AssociatedScript;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.AssociatedScript;
|
||||||
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.savedfilters.SavedFiltersMetaDataProvider;
|
import com.kingsrook.qqq.backend.core.model.savedviews.SavedViewsMetaDataProvider;
|
||||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider;
|
import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider;
|
||||||
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.module.rdbms.jdbc.ConnectionManager;
|
import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
|
||||||
@ -157,7 +157,7 @@ public class TestUtils
|
|||||||
qInstance.addBackend(defineMemoryBackend());
|
qInstance.addBackend(defineMemoryBackend());
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
new SavedFiltersMetaDataProvider().defineAll(qInstance, defineMemoryBackend().getName(), null);
|
new SavedViewsMetaDataProvider().defineAll(qInstance, defineMemoryBackend().getName(), null);
|
||||||
new ScriptsMetaDataProvider().defineAll(qInstance, defineMemoryBackend().getName(), null);
|
new ScriptsMetaDataProvider().defineAll(qInstance, defineMemoryBackend().getName(), null);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
|
Reference in New Issue
Block a user