mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-22 06:58:45 +00:00
Compare commits
15 Commits
snapshot-f
...
snapshot-i
Author | SHA1 | Date | |
---|---|---|---|
5e04eba94d | |||
c77e37d6dc | |||
aef42a4a5e | |||
c0b5d11a09 | |||
8e8d3b5d2b | |||
2f20861a27 | |||
4ca9c9dcaf | |||
c6a58ac68f | |||
bb69dddb81 | |||
459510bba4 | |||
18e1852ce4 | |||
0dd7f5e1d2 | |||
601c66ddff | |||
e1ca85c746 | |||
1baade0449 |
@ -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.TableAutomationAction;
|
||||
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.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||
import org.apache.commons.lang.NotImplementedException;
|
||||
import org.json.JSONObject;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
@ -388,13 +389,15 @@ public class PollingAutomationPerTableRunner implements Runnable
|
||||
if(filterId != null)
|
||||
{
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(SavedFilter.TABLE_NAME);
|
||||
getInput.setTableName(SavedView.TABLE_NAME);
|
||||
getInput.setPrimaryKey(filterId);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
if(getOutput.getRecord() != null)
|
||||
{
|
||||
SavedFilter savedFilter = new SavedFilter(getOutput.getRecord());
|
||||
filter = JsonUtils.toObject(savedFilter.getFilterJson(), QQueryFilter.class);
|
||||
SavedView savedView = new SavedView(getOutput.getRecord());
|
||||
JSONObject viewJson = new JSONObject(savedView.getViewJson());
|
||||
JSONObject queryFilter = viewJson.getJSONObject("queryFilter");
|
||||
filter = JsonUtils.toObject(queryFilter.toString(), QQueryFilter.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,6 @@ package com.kingsrook.qqq.backend.core.actions.tables;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -207,17 +206,6 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
|
||||
return (rs);
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// set values in create date & modify date //
|
||||
// todo .. better (not hard-coded names) //
|
||||
/////////////////////////////////////////////
|
||||
Instant now = Instant.now();
|
||||
for(QRecord record : insertInput.getRecords())
|
||||
{
|
||||
setValueIfTableHasField(record, insertInput.getTable(), "createDate", now);
|
||||
setValueIfTableHasField(record, insertInput.getTable(), "modifyDate", now);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////
|
||||
// load the backend module and its insert interface //
|
||||
//////////////////////////////////////////////////////
|
||||
@ -233,29 +221,6 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** If the table has a field with the given name, then set the given value in the
|
||||
** given record.
|
||||
*******************************************************************************/
|
||||
private static void setValueIfTableHasField(QRecord record, QTableMetaData table, String fieldName, Serializable value)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(table.getFields().containsKey(fieldName))
|
||||
{
|
||||
record.setValue(fieldName, value);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
/////////////////////////////////////////////////
|
||||
// this means field doesn't exist, so, ignore. //
|
||||
/////////////////////////////////////////////////
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -277,7 +242,7 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
|
||||
|
||||
setDefaultValuesInRecords(table, insertInput.getRecords());
|
||||
|
||||
ValueBehaviorApplier.applyFieldBehaviors(insertInput.getInstance(), table, insertInput.getRecords());
|
||||
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.INSERT, insertInput.getInstance(), table, insertInput.getRecords());
|
||||
|
||||
runPreInsertCustomizerIfItIsTime(insertInput, preInsertCustomizer, AbstractPreInsertCustomizer.WhenToRun.BEFORE_UNIQUE_KEY_CHECKS);
|
||||
setErrorsIfUniqueKeyErrors(insertInput, table);
|
||||
|
@ -235,7 +235,7 @@ public class UpdateAction
|
||||
/////////////////////////////
|
||||
// run standard validators //
|
||||
/////////////////////////////
|
||||
ValueBehaviorApplier.applyFieldBehaviors(updateInput.getInstance(), table, updateInput.getRecords());
|
||||
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.UPDATE, updateInput.getInstance(), table, updateInput.getRecords());
|
||||
validatePrimaryKeysAreGiven(updateInput);
|
||||
|
||||
if(oldRecordList.isPresent())
|
||||
|
@ -22,7 +22,6 @@
|
||||
package com.kingsrook.qqq.backend.core.actions.tables.helpers;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@ -61,11 +60,6 @@ public class UpdateActionRecordSplitHelper
|
||||
|
||||
for(QRecord record : updateInput.getRecords())
|
||||
{
|
||||
////////////////////////////////////////////
|
||||
// todo .. better (not a hard-coded name) //
|
||||
////////////////////////////////////////////
|
||||
setValueIfTableHasField(record, table, "modifyDate", now);
|
||||
|
||||
List<String> updatableFields = table.getFields().values().stream()
|
||||
.map(QFieldMetaData::getName)
|
||||
// todo - intent here is to avoid non-updateable fields - but this
|
||||
@ -147,29 +141,6 @@ public class UpdateActionRecordSplitHelper
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** If the table has a field with the given name, then set the given value in the
|
||||
** given record.
|
||||
*******************************************************************************/
|
||||
protected void setValueIfTableHasField(QRecord record, QTableMetaData table, String fieldName, Serializable value)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(table.getFields().containsKey(fieldName))
|
||||
{
|
||||
record.setValue(fieldName, value);
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
/////////////////////////////////////////////////
|
||||
// this means field doesn't exist, so, ignore. //
|
||||
/////////////////////////////////////////////////
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for haveAnyWithoutErrors
|
||||
**
|
||||
|
@ -25,12 +25,10 @@ package com.kingsrook.qqq.backend.core.actions.values;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.statusmessages.BadInputStatusMessage;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -42,16 +40,10 @@ public class ValueBehaviorApplier
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void applyFieldBehaviors(QInstance instance, QTableMetaData table, List<QRecord> recordList)
|
||||
public enum Action
|
||||
{
|
||||
for(QFieldMetaData field : table.getFields().values())
|
||||
{
|
||||
String fieldName = field.getName();
|
||||
if(field.getType().equals(QFieldType.STRING) && field.getMaxLength() != null)
|
||||
{
|
||||
applyValueTooLongBehavior(instance, recordList, field, fieldName);
|
||||
}
|
||||
}
|
||||
INSERT,
|
||||
UPDATE
|
||||
}
|
||||
|
||||
|
||||
@ -59,31 +51,18 @@ public class ValueBehaviorApplier
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void applyValueTooLongBehavior(QInstance instance, List<QRecord> recordList, QFieldMetaData field, String fieldName)
|
||||
public static void applyFieldBehaviors(Action action, QInstance instance, QTableMetaData table, List<QRecord> recordList)
|
||||
{
|
||||
ValueTooLongBehavior valueTooLongBehavior = field.getBehavior(instance, ValueTooLongBehavior.class);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// don't process PASS_THROUGH - so we don't have to iterate over the whole record list to do noop //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(valueTooLongBehavior != null && !valueTooLongBehavior.equals(ValueTooLongBehavior.PASS_THROUGH))
|
||||
if(CollectionUtils.nullSafeIsEmpty(recordList))
|
||||
{
|
||||
for(QRecord record : recordList)
|
||||
return;
|
||||
}
|
||||
|
||||
for(QFieldMetaData field : table.getFields().values())
|
||||
{
|
||||
for(FieldBehavior<?> fieldBehavior : CollectionUtils.nonNullCollection(field.getBehaviors()))
|
||||
{
|
||||
String value = record.getValueString(fieldName);
|
||||
if(value != null && value.length() > field.getMaxLength())
|
||||
{
|
||||
switch(valueTooLongBehavior)
|
||||
{
|
||||
case TRUNCATE -> record.setValue(fieldName, StringUtils.safeTruncate(value, field.getMaxLength()));
|
||||
case TRUNCATE_ELLIPSIS -> record.setValue(fieldName, StringUtils.safeTruncate(value, field.getMaxLength(), "..."));
|
||||
case ERROR -> record.addError(new BadInputStatusMessage("The value for " + field.getLabel() + " is too long (max allowed length=" + field.getMaxLength() + ")"));
|
||||
case PASS_THROUGH ->
|
||||
{
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected valueTooLongBehavior: " + valueTooLongBehavior);
|
||||
}
|
||||
}
|
||||
fieldBehavior.apply(action, recordList, instance, table, field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.DynamicDefaultValueBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
@ -94,10 +95,8 @@ public class QInstanceEnricher
|
||||
|
||||
private JoinGraph joinGraph;
|
||||
|
||||
//////////////////////////////////////////////////////////
|
||||
// todo - come up w/ a way for app devs to set configs! //
|
||||
//////////////////////////////////////////////////////////
|
||||
private boolean configRemoveIdFromNameWhenCreatingPossibleValueFieldLabels = true;
|
||||
private boolean configRemoveIdFromNameWhenCreatingPossibleValueFieldLabels = true;
|
||||
private boolean configAddDynamicDefaultValuesToFieldsNamedCreateDateAndModifyDate = true;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// let an instance define mappings to be applied during name-to-label enrichments, //
|
||||
@ -464,6 +463,22 @@ public class QInstanceEnricher
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// add field behaviors for create date & modify date, if so configured //
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
if(configAddDynamicDefaultValuesToFieldsNamedCreateDateAndModifyDate)
|
||||
{
|
||||
if("createDate".equals(field.getName()) && field.getBehaviorOnlyIfSet(DynamicDefaultValueBehavior.class) == null)
|
||||
{
|
||||
field.withBehavior(DynamicDefaultValueBehavior.CREATE_DATE);
|
||||
}
|
||||
|
||||
if("modifyDate".equals(field.getName()) && field.getBehaviorOnlyIfSet(DynamicDefaultValueBehavior.class) == null)
|
||||
{
|
||||
field.withBehavior(DynamicDefaultValueBehavior.MODIFY_DATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1220,4 +1235,66 @@ public class QInstanceEnricher
|
||||
labelMappings.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for configRemoveIdFromNameWhenCreatingPossibleValueFieldLabels
|
||||
*******************************************************************************/
|
||||
public boolean getConfigRemoveIdFromNameWhenCreatingPossibleValueFieldLabels()
|
||||
{
|
||||
return (this.configRemoveIdFromNameWhenCreatingPossibleValueFieldLabels);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for configRemoveIdFromNameWhenCreatingPossibleValueFieldLabels
|
||||
*******************************************************************************/
|
||||
public void setConfigRemoveIdFromNameWhenCreatingPossibleValueFieldLabels(boolean configRemoveIdFromNameWhenCreatingPossibleValueFieldLabels)
|
||||
{
|
||||
this.configRemoveIdFromNameWhenCreatingPossibleValueFieldLabels = configRemoveIdFromNameWhenCreatingPossibleValueFieldLabels;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for configRemoveIdFromNameWhenCreatingPossibleValueFieldLabels
|
||||
*******************************************************************************/
|
||||
public QInstanceEnricher withConfigRemoveIdFromNameWhenCreatingPossibleValueFieldLabels(boolean configRemoveIdFromNameWhenCreatingPossibleValueFieldLabels)
|
||||
{
|
||||
this.configRemoveIdFromNameWhenCreatingPossibleValueFieldLabels = configRemoveIdFromNameWhenCreatingPossibleValueFieldLabels;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for configAddDynamicDefaultValuesToFieldsNamedCreateDateAndModifyDate
|
||||
*******************************************************************************/
|
||||
public boolean getConfigAddDynamicDefaultValuesToFieldsNamedCreateDateAndModifyDate()
|
||||
{
|
||||
return (this.configAddDynamicDefaultValuesToFieldsNamedCreateDateAndModifyDate);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for configAddDynamicDefaultValuesToFieldsNamedCreateDateAndModifyDate
|
||||
*******************************************************************************/
|
||||
public void setConfigAddDynamicDefaultValuesToFieldsNamedCreateDateAndModifyDate(boolean configAddDynamicDefaultValuesToFieldsNamedCreateDateAndModifyDate)
|
||||
{
|
||||
this.configAddDynamicDefaultValuesToFieldsNamedCreateDateAndModifyDate = configAddDynamicDefaultValuesToFieldsNamedCreateDateAndModifyDate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for configAddDynamicDefaultValuesToFieldsNamedCreateDateAndModifyDate
|
||||
*******************************************************************************/
|
||||
public QInstanceEnricher withConfigAddDynamicDefaultValuesToFieldsNamedCreateDateAndModifyDate(boolean configAddDynamicDefaultValuesToFieldsNamedCreateDateAndModifyDate)
|
||||
{
|
||||
this.configAddDynamicDefaultValuesToFieldsNamedCreateDateAndModifyDate = configAddDynamicDefaultValuesToFieldsNamedCreateDateAndModifyDate;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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.ExposedJoin;
|
||||
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.Tier;
|
||||
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.StringUtils;
|
||||
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);
|
||||
validateTableAssociations(qInstance, table);
|
||||
validateExposedJoins(qInstance, joinGraph, table);
|
||||
|
||||
for(QSupplementalTableMetaData supplementalTableMetaData : CollectionUtils.nonNullMap(table.getSupplementalMetaData()).values())
|
||||
{
|
||||
supplementalTableMetaData.validate(qInstance, table, this);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -692,7 +699,7 @@ public class QInstanceValidator
|
||||
|
||||
String prefix = "Field " + fieldName + " in table " + tableName + " ";
|
||||
|
||||
ValueTooLongBehavior behavior = field.getBehavior(qInstance, ValueTooLongBehavior.class);
|
||||
ValueTooLongBehavior behavior = field.getBehaviorOrDefault(qInstance, ValueTooLongBehavior.class);
|
||||
if(behavior != null && !behavior.equals(ValueTooLongBehavior.PASS_THROUGH))
|
||||
{
|
||||
assertCondition(field.getMaxLength() != null, prefix + "specifies a ValueTooLongBehavior, but not a maxLength.");
|
||||
@ -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.QRecordEntity;
|
||||
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;
|
||||
|
||||
|
||||
@ -51,7 +51,7 @@ public class TableTrigger extends QRecordEntity
|
||||
@QField(possibleValueSourceName = TablesPossibleValueSourceMetaDataProvider.NAME)
|
||||
private String tableName;
|
||||
|
||||
@QField(possibleValueSourceName = SavedFilter.TABLE_NAME)
|
||||
@QField(possibleValueSourceName = SavedView.TABLE_NAME)
|
||||
private Integer filterId;
|
||||
|
||||
@QField(possibleValueSourceName = Script.TABLE_NAME)
|
||||
|
@ -186,7 +186,7 @@ public class QRecord implements Serializable
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// we know entry is serializable at this point, based on type param's bound //
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
LOG.info("Non-primitive serializable value in QRecord - calling SerializationUtils.clone...", logPair("key", entry.getKey()), logPair("type", value.getClass()));
|
||||
LOG.debug("Non-primitive serializable value in QRecord - calling SerializationUtils.clone...", logPair("key", entry.getKey()), logPair("type", value.getClass()));
|
||||
clone.put(entry.getKey(), (V) SerializationUtils.clone(entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,164 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.fields;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.ValueBehaviorApplier;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Field behavior that sets a default value for a field dynamically.
|
||||
** e.g., create-date fields get set to 'now' on insert.
|
||||
** e.g., modify-date fields get set to 'now' on insert and on update.
|
||||
*******************************************************************************/
|
||||
public enum DynamicDefaultValueBehavior implements FieldBehavior<DynamicDefaultValueBehavior>
|
||||
{
|
||||
CREATE_DATE,
|
||||
MODIFY_DATE,
|
||||
NONE;
|
||||
|
||||
private static final QLogger LOG = QLogger.getLogger(ValueTooLongBehavior.class);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public DynamicDefaultValueBehavior getDefault()
|
||||
{
|
||||
return (NONE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void apply(ValueBehaviorApplier.Action action, List<QRecord> recordList, QInstance instance, QTableMetaData table, QFieldMetaData field)
|
||||
{
|
||||
if(this.equals(NONE))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch(this)
|
||||
{
|
||||
case CREATE_DATE -> applyCreateDate(action, recordList, table, field);
|
||||
case MODIFY_DATE -> applyModifyDate(action, recordList, table, field);
|
||||
default -> throw new IllegalStateException("Unexpected enum value: " + this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void applyCreateDate(ValueBehaviorApplier.Action action, List<QRecord> recordList, QTableMetaData table, QFieldMetaData field)
|
||||
{
|
||||
if(!ValueBehaviorApplier.Action.INSERT.equals(action))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
setCreateDateOrModifyDateOnList(recordList, table, field);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void applyModifyDate(ValueBehaviorApplier.Action action, List<QRecord> recordList, QTableMetaData table, QFieldMetaData field)
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// check both of these (even though they're the only 2 values at the time of this writing), just in case more enum values are added in the future //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(!ValueBehaviorApplier.Action.INSERT.equals(action) && !ValueBehaviorApplier.Action.UPDATE.equals(action))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
setCreateDateOrModifyDateOnList(recordList, table, field);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void setCreateDateOrModifyDateOnList(List<QRecord> recordList, QTableMetaData table, QFieldMetaData field)
|
||||
{
|
||||
String fieldName = field.getName();
|
||||
Serializable value = getNow(table, field);
|
||||
|
||||
for(QRecord record : CollectionUtils.nonNullList(recordList))
|
||||
{
|
||||
record.setValue(fieldName, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private Serializable getNow(QTableMetaData table, QFieldMetaData field)
|
||||
{
|
||||
if(QFieldType.DATE_TIME.equals(field.getType()))
|
||||
{
|
||||
return (Instant.now());
|
||||
}
|
||||
else if(QFieldType.DATE.equals(field.getType()))
|
||||
{
|
||||
return (LocalDate.now());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.debug("Request to apply a " + this.name() + " DynamicDefaultValueBehavior to a non-date or date-time field", logPair("table", table.getName()), logPair("field", field.getName()));
|
||||
return (null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void noop()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -22,10 +22,41 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.fields;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.ValueBehaviorApplier;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Interface for (expected to be?) enums which define behaviors that get applied
|
||||
** to fields.
|
||||
**
|
||||
** At the present, these behaviors get applied before a field is stored (insert
|
||||
** or update), through the ValueBehaviorApplier class.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface FieldBehavior
|
||||
public interface FieldBehavior<T extends FieldBehavior<T>>
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** In case a behavior of this type wasn't set on the field, what should the
|
||||
** default of this type be?
|
||||
*******************************************************************************/
|
||||
T getDefault();
|
||||
|
||||
/*******************************************************************************
|
||||
** Apply this behavior to a list of records
|
||||
*******************************************************************************/
|
||||
void apply(ValueBehaviorApplier.Action action, List<QRecord> recordList, QInstance instance, QTableMetaData table, QFieldMetaData field);
|
||||
|
||||
/*******************************************************************************
|
||||
** control if multiple behaviors of this type should be allowed together on a field.
|
||||
*******************************************************************************/
|
||||
default boolean allowMultipleBehaviorsOfThisType()
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.github.hervian.reflection.Fun;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceHelpContentManager;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
@ -44,6 +45,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.help.QHelpContent;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.FieldSecurityLock;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -52,6 +54,8 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
*******************************************************************************/
|
||||
public class QFieldMetaData implements Cloneable
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(QFieldMetaData.class);
|
||||
|
||||
private String name;
|
||||
private String label;
|
||||
private String backendName;
|
||||
@ -73,8 +77,8 @@ public class QFieldMetaData implements Cloneable
|
||||
private String possibleValueSourceName;
|
||||
private QQueryFilter possibleValueSourceFilter;
|
||||
|
||||
private Integer maxLength;
|
||||
private Set<FieldBehavior> behaviors;
|
||||
private Integer maxLength;
|
||||
private Set<FieldBehavior<?>> behaviors;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// w/ longer-term vision for FieldBehaviors //
|
||||
@ -674,7 +678,7 @@ public class QFieldMetaData implements Cloneable
|
||||
** Getter for behaviors
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Set<FieldBehavior> getBehaviors()
|
||||
public Set<FieldBehavior<?>> getBehaviors()
|
||||
{
|
||||
return behaviors;
|
||||
}
|
||||
@ -682,11 +686,12 @@ public class QFieldMetaData implements Cloneable
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
** Get the FieldBehavior object of a given behaviorType (class) - but - if one
|
||||
** isn't set, then use the default from that type.
|
||||
*******************************************************************************/
|
||||
public <T extends FieldBehavior> T getBehavior(QInstance instance, Class<T> behaviorType)
|
||||
public <T extends FieldBehavior<T>> T getBehaviorOrDefault(QInstance instance, Class<T> behaviorType)
|
||||
{
|
||||
for(FieldBehavior fieldBehavior : CollectionUtils.nonNullCollection(behaviors))
|
||||
for(FieldBehavior<?> fieldBehavior : CollectionUtils.nonNullCollection(behaviors))
|
||||
{
|
||||
if(behaviorType.isInstance(fieldBehavior))
|
||||
{
|
||||
@ -701,9 +706,33 @@ public class QFieldMetaData implements Cloneable
|
||||
///////////////////////////////////////////
|
||||
// return default behavior for this type //
|
||||
///////////////////////////////////////////
|
||||
if(behaviorType.equals(ValueTooLongBehavior.class))
|
||||
if(behaviorType.isEnum())
|
||||
{
|
||||
return behaviorType.cast(ValueTooLongBehavior.getDefault());
|
||||
return (behaviorType.getEnumConstants()[0].getDefault());
|
||||
}
|
||||
|
||||
return (null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Get the FieldBehavior object of a given behaviorType (class) - and if one
|
||||
** isn't set, then return null.
|
||||
*******************************************************************************/
|
||||
public <T extends FieldBehavior<T>> T getBehaviorOnlyIfSet(Class<T> behaviorType)
|
||||
{
|
||||
if(behaviors == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
for(FieldBehavior<?> fieldBehavior : CollectionUtils.nonNullCollection(behaviors))
|
||||
{
|
||||
if(behaviorType.isInstance(fieldBehavior))
|
||||
{
|
||||
return (behaviorType.cast(fieldBehavior));
|
||||
}
|
||||
}
|
||||
|
||||
return (null);
|
||||
@ -715,7 +744,7 @@ public class QFieldMetaData implements Cloneable
|
||||
** Setter for behaviors
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setBehaviors(Set<FieldBehavior> behaviors)
|
||||
public void setBehaviors(Set<FieldBehavior<?>> behaviors)
|
||||
{
|
||||
this.behaviors = behaviors;
|
||||
}
|
||||
@ -726,7 +755,7 @@ public class QFieldMetaData implements Cloneable
|
||||
** Fluent setter for behaviors
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFieldMetaData withBehaviors(Set<FieldBehavior> behaviors)
|
||||
public QFieldMetaData withBehaviors(Set<FieldBehavior<?>> behaviors)
|
||||
{
|
||||
this.behaviors = behaviors;
|
||||
return (this);
|
||||
@ -738,12 +767,30 @@ public class QFieldMetaData implements Cloneable
|
||||
** Fluent setter for behaviors
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFieldMetaData withBehavior(FieldBehavior behavior)
|
||||
public QFieldMetaData withBehavior(FieldBehavior<?> behavior)
|
||||
{
|
||||
if(behavior == null)
|
||||
{
|
||||
LOG.debug("Skipping request to add null behavior", logPair("fieldName", getName()));
|
||||
return (this);
|
||||
}
|
||||
|
||||
if(behaviors == null)
|
||||
{
|
||||
behaviors = new HashSet<>();
|
||||
}
|
||||
|
||||
if(!behavior.allowMultipleBehaviorsOfThisType())
|
||||
{
|
||||
@SuppressWarnings("unchecked")
|
||||
FieldBehavior<?> existingBehaviorOfThisType = getBehaviorOnlyIfSet(behavior.getClass());
|
||||
if(existingBehaviorOfThisType != null)
|
||||
{
|
||||
LOG.debug("Replacing a field behavior", logPair("fieldName", getName()), logPair("oldBehavior", existingBehaviorOfThisType), logPair("newBehavior", behavior));
|
||||
this.behaviors.remove(existingBehaviorOfThisType);
|
||||
}
|
||||
}
|
||||
|
||||
this.behaviors.add(behavior);
|
||||
return (this);
|
||||
}
|
||||
|
@ -22,23 +22,85 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.fields;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.ValueBehaviorApplier;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.statusmessages.BadInputStatusMessage;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Behaviors for string fields, if their value is too long.
|
||||
**
|
||||
** Note: This was the first implementation of a FieldBehavior, so its test
|
||||
** coverage is provided in ValueBehaviorApplierTest.
|
||||
*******************************************************************************/
|
||||
public enum ValueTooLongBehavior implements FieldBehavior
|
||||
public enum ValueTooLongBehavior implements FieldBehavior<ValueTooLongBehavior>
|
||||
{
|
||||
TRUNCATE,
|
||||
TRUNCATE_ELLIPSIS,
|
||||
ERROR,
|
||||
PASS_THROUGH;
|
||||
|
||||
private static final QLogger LOG = QLogger.getLogger(ValueTooLongBehavior.class);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static FieldBehavior getDefault()
|
||||
@Override
|
||||
public ValueTooLongBehavior getDefault()
|
||||
{
|
||||
return PASS_THROUGH;
|
||||
return (PASS_THROUGH);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void apply(ValueBehaviorApplier.Action action, List<QRecord> recordList, QInstance instance, QTableMetaData table, QFieldMetaData field)
|
||||
{
|
||||
if(this.equals(PASS_THROUGH))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
String fieldName = field.getName();
|
||||
if(!QFieldType.STRING.equals(field.getType()))
|
||||
{
|
||||
LOG.debug("Request to apply a ValueTooLongBehavior to a non-string field", logPair("table", table.getName()), logPair("field", fieldName));
|
||||
return;
|
||||
}
|
||||
|
||||
if(field.getMaxLength() == null)
|
||||
{
|
||||
LOG.debug("Request to apply a ValueTooLongBehavior to string field without a maxLength", logPair("table", table.getName()), logPair("field", fieldName));
|
||||
return;
|
||||
}
|
||||
|
||||
for(QRecord record : recordList)
|
||||
{
|
||||
String value = record.getValueString(fieldName);
|
||||
if(value != null && value.length() > field.getMaxLength())
|
||||
{
|
||||
switch(this)
|
||||
{
|
||||
case TRUNCATE -> record.setValue(fieldName, StringUtils.safeTruncate(value, field.getMaxLength()));
|
||||
case TRUNCATE_ELLIPSIS -> record.setValue(fieldName, StringUtils.safeTruncate(value, field.getMaxLength(), "..."));
|
||||
case ERROR -> record.addError(new BadInputStatusMessage("The value for " + field.getLabel() + " is too long (max allowed length=" + field.getMaxLength() + ")"));
|
||||
///////////////////////////////////
|
||||
// PASS_THROUGH is handled above //
|
||||
///////////////////////////////////
|
||||
default -> throw new IllegalStateException("Unexpected enum value: " + this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
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;
|
||||
|
||||
|
||||
@ -69,4 +70,16 @@ public abstract class QSupplementalTableMetaData
|
||||
// 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.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 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/
|
||||
@ -19,7 +19,7 @@
|
||||
* 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;
|
||||
@ -32,9 +32,9 @@ import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
/*******************************************************************************
|
||||
** 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)
|
||||
private Integer id;
|
||||
@ -55,7 +55,7 @@ public class SavedFilter extends QRecordEntity
|
||||
private String userId;
|
||||
|
||||
@QField(isEditable = false)
|
||||
private String filterJson;
|
||||
private String viewJson;
|
||||
|
||||
|
||||
|
||||
@ -63,7 +63,7 @@ public class SavedFilter extends QRecordEntity
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public SavedFilter()
|
||||
public SavedView()
|
||||
{
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@ public class SavedFilter extends QRecordEntity
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public SavedFilter(QRecord qRecord) throws QException
|
||||
public SavedView(QRecord qRecord) throws QException
|
||||
{
|
||||
populateFromQRecord(qRecord);
|
||||
}
|
||||
@ -172,7 +172,7 @@ public class SavedFilter extends QRecordEntity
|
||||
** Fluent setter for label
|
||||
**
|
||||
*******************************************************************************/
|
||||
public SavedFilter withLabel(String label)
|
||||
public SavedView withLabel(String label)
|
||||
{
|
||||
this.label = label;
|
||||
return (this);
|
||||
@ -206,7 +206,7 @@ public class SavedFilter extends QRecordEntity
|
||||
** Fluent setter for tableName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public SavedFilter withTableName(String tableName)
|
||||
public SavedView withTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
return (this);
|
||||
@ -240,7 +240,7 @@ public class SavedFilter extends QRecordEntity
|
||||
** Fluent setter for userId
|
||||
**
|
||||
*******************************************************************************/
|
||||
public SavedFilter withUserId(String userId)
|
||||
public SavedView withUserId(String userId)
|
||||
{
|
||||
this.userId = userId;
|
||||
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);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* 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
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
@ -19,25 +19,31 @@
|
||||
* 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 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.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.QPossibleValueSource;
|
||||
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.processes.implementations.savedfilters.DeleteSavedFilterProcess;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.savedfilters.QuerySavedFilterProcess;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.savedfilters.StoreSavedFilterProcess;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.savedviews.DeleteSavedViewProcess;
|
||||
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
|
||||
{
|
||||
instance.addTable(defineSavedFilterTable(backendName, backendDetailEnricher));
|
||||
instance.addPossibleValueSource(defineSavedFilterPossibleValueSource());
|
||||
instance.addProcess(QuerySavedFilterProcess.getProcessMetaData());
|
||||
instance.addProcess(StoreSavedFilterProcess.getProcessMetaData());
|
||||
instance.addProcess(DeleteSavedFilterProcess.getProcessMetaData());
|
||||
instance.addTable(defineSavedViewTable(backendName, backendDetailEnricher));
|
||||
instance.addPossibleValueSource(defineSavedViewPossibleValueSource());
|
||||
instance.addProcess(QuerySavedViewProcess.getProcessMetaData());
|
||||
instance.addProcess(StoreSavedViewProcess.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()
|
||||
.withName(SavedFilter.TABLE_NAME)
|
||||
.withLabel("Saved Filter")
|
||||
.withName(SavedView.TABLE_NAME)
|
||||
.withLabel("Saved View")
|
||||
.withRecordLabelFormat("%s")
|
||||
.withRecordLabelFields("label")
|
||||
.withBackendName(backendName)
|
||||
.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)
|
||||
{
|
||||
@ -82,12 +93,12 @@ public class SavedFiltersMetaDataProvider
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QPossibleValueSource defineSavedFilterPossibleValueSource()
|
||||
private QPossibleValueSource defineSavedViewPossibleValueSource()
|
||||
{
|
||||
return new QPossibleValueSource()
|
||||
.withName(SavedFilter.TABLE_NAME)
|
||||
.withName(SavedView.TABLE_NAME)
|
||||
.withType(QPossibleValueSourceType.TABLE)
|
||||
.withTableName(SavedFilter.TABLE_NAME)
|
||||
.withTableName(SavedView.TABLE_NAME)
|
||||
.withValueFormatAndFields(PVSValueFormatAndFields.LABEL_ONLY)
|
||||
.withOrderByField("label");
|
||||
}
|
@ -176,7 +176,7 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
|
||||
// process a sessionUUID - looks up userSession record - cannot create token this way. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////
|
||||
String sessionUUID = context.get(SESSION_UUID_KEY);
|
||||
LOG.info("Creating session from sessionUUID (userSession)", logPair("sessionUUID", maskForLog(sessionUUID)));
|
||||
LOG.debug("Creating session from sessionUUID (userSession)", logPair("sessionUUID", maskForLog(sessionUUID)));
|
||||
if(sessionUUID != null)
|
||||
{
|
||||
accessToken = getAccessTokenFromSessionUUID(metaData, sessionUUID);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* 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
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
@ -19,7 +19,7 @@
|
||||
* 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;
|
||||
@ -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.processes.QBackendStepMetaData;
|
||||
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()
|
||||
{
|
||||
return (new QProcessMetaData()
|
||||
.withName("deleteSavedFilter")
|
||||
.withName("deleteSavedView")
|
||||
.withStepList(List.of(
|
||||
new QBackendStepMetaData()
|
||||
.withCode(new QCodeReference(DeleteSavedFilterProcess.class))
|
||||
.withCode(new QCodeReference(DeleteSavedViewProcess.class))
|
||||
.withName("delete")
|
||||
)));
|
||||
}
|
||||
@ -72,16 +72,16 @@ public class DeleteSavedFilterProcess implements BackendStep
|
||||
|
||||
try
|
||||
{
|
||||
Integer savedFilterId = runBackendStepInput.getValueInteger("id");
|
||||
Integer savedViewId = runBackendStepInput.getValueInteger("id");
|
||||
|
||||
DeleteInput input = new DeleteInput();
|
||||
input.setTableName(SavedFilter.TABLE_NAME);
|
||||
input.setPrimaryKeys(List.of(savedFilterId));
|
||||
input.setTableName(SavedView.TABLE_NAME);
|
||||
input.setPrimaryKeys(List.of(savedViewId));
|
||||
new DeleteAction().execute(input);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error deleting saved filter", e);
|
||||
LOG.warn("Error deleting saved view", e);
|
||||
throw (e);
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* 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
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
@ -19,7 +19,7 @@
|
||||
* 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;
|
||||
@ -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.processes.QBackendStepMetaData;
|
||||
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()
|
||||
{
|
||||
return (new QProcessMetaData()
|
||||
.withName("querySavedFilter")
|
||||
.withName("querySavedView")
|
||||
.withStepList(List.of(
|
||||
new QBackendStepMetaData()
|
||||
.withCode(new QCodeReference(QuerySavedFilterProcess.class))
|
||||
.withCode(new QCodeReference(QuerySavedViewProcess.class))
|
||||
.withName("query")
|
||||
)));
|
||||
}
|
||||
@ -81,36 +81,36 @@ public class QuerySavedFilterProcess implements BackendStep
|
||||
|
||||
try
|
||||
{
|
||||
Integer savedFilterId = runBackendStepInput.getValueInteger("id");
|
||||
if(savedFilterId != null)
|
||||
Integer savedViewId = runBackendStepInput.getValueInteger("id");
|
||||
if(savedViewId != null)
|
||||
{
|
||||
GetInput input = new GetInput();
|
||||
input.setTableName(SavedFilter.TABLE_NAME);
|
||||
input.setPrimaryKey(savedFilterId);
|
||||
input.setTableName(SavedView.TABLE_NAME);
|
||||
input.setPrimaryKey(savedViewId);
|
||||
|
||||
GetOutput output = new GetAction().execute(input);
|
||||
runBackendStepOutput.addRecord(output.getRecord());
|
||||
runBackendStepOutput.addValue("savedFilter", output.getRecord());
|
||||
runBackendStepOutput.addValue("savedFilterList", (Serializable) List.of(output.getRecord()));
|
||||
runBackendStepOutput.addValue("savedView", output.getRecord());
|
||||
runBackendStepOutput.addValue("savedViewList", (Serializable) List.of(output.getRecord()));
|
||||
}
|
||||
else
|
||||
{
|
||||
String tableName = runBackendStepInput.getValueString("tableName");
|
||||
|
||||
QueryInput input = new QueryInput();
|
||||
input.setTableName(SavedFilter.TABLE_NAME);
|
||||
input.setTableName(SavedView.TABLE_NAME);
|
||||
input.setFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("tableName", QCriteriaOperator.EQUALS, tableName))
|
||||
.withOrderBy(new QFilterOrderBy("label")));
|
||||
|
||||
QueryOutput output = new QueryAction().execute(input);
|
||||
runBackendStepOutput.setRecords(output.getRecords());
|
||||
runBackendStepOutput.addValue("savedFilterList", (Serializable) output.getRecords());
|
||||
runBackendStepOutput.addValue("savedViewList", (Serializable) output.getRecords());
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error deleting saved filter", e);
|
||||
LOG.warn("Error querying for saved views", e);
|
||||
throw (e);
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
* 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
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
@ -19,37 +19,45 @@
|
||||
* 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.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
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.QueryAction;
|
||||
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.QUserFacingException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
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.UpdateOutput;
|
||||
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.processes.QBackendStepMetaData;
|
||||
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()
|
||||
{
|
||||
return (new QProcessMetaData()
|
||||
.withName("storeSavedFilter")
|
||||
.withName("storeSavedView")
|
||||
.withStepList(List.of(
|
||||
new QBackendStepMetaData()
|
||||
.withCode(new QCodeReference(StoreSavedFilterProcess.class))
|
||||
.withCode(new QCodeReference(StoreSavedViewProcess.class))
|
||||
.withName("store")
|
||||
)));
|
||||
}
|
||||
@ -79,39 +87,73 @@ public class StoreSavedFilterProcess implements BackendStep
|
||||
|
||||
try
|
||||
{
|
||||
String userId = QContext.getQSession().getUser().getIdReference();
|
||||
String tableName = runBackendStepInput.getValueString("tableName");
|
||||
String label = runBackendStepInput.getValueString("label");
|
||||
|
||||
QRecord qRecord = new QRecord()
|
||||
.withValue("id", runBackendStepInput.getValueInteger("id"))
|
||||
.withValue("label", runBackendStepInput.getValueString("label"))
|
||||
.withValue("tableName", runBackendStepInput.getValueString("tableName"))
|
||||
.withValue("filterJson", runBackendStepInput.getValueString("filterJson"))
|
||||
.withValue("userId", runBackendStepInput.getSession().getUser().getIdReference());
|
||||
.withValue("viewJson", runBackendStepInput.getValueString("viewJson"))
|
||||
.withValue("label", label)
|
||||
.withValue("tableName", tableName)
|
||||
.withValue("userId", userId);
|
||||
|
||||
List<QRecord> savedFilterList = new ArrayList<>();
|
||||
List<QRecord> savedViewList;
|
||||
if(qRecord.getValueInteger("id") == null)
|
||||
{
|
||||
checkForDuplicates(userId, tableName, label, null);
|
||||
|
||||
InsertInput input = new InsertInput();
|
||||
input.setTableName(SavedFilter.TABLE_NAME);
|
||||
input.setTableName(SavedView.TABLE_NAME);
|
||||
input.setRecords(List.of(qRecord));
|
||||
|
||||
InsertOutput output = new InsertAction().execute(input);
|
||||
savedFilterList = output.getRecords();
|
||||
savedViewList = output.getRecords();
|
||||
}
|
||||
else
|
||||
{
|
||||
checkForDuplicates(userId, tableName, label, qRecord.getValueInteger("id"));
|
||||
|
||||
UpdateInput input = new UpdateInput();
|
||||
input.setTableName(SavedFilter.TABLE_NAME);
|
||||
input.setTableName(SavedView.TABLE_NAME);
|
||||
input.setRecords(List.of(qRecord));
|
||||
|
||||
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)
|
||||
{
|
||||
LOG.warn("Error storing data saved filter", e);
|
||||
LOG.warn("Error storing saved view", 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;
|
||||
|
||||
}
|
@ -38,7 +38,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.statusmessages.SystemErrorStatusMessage;
|
||||
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
@ -64,6 +63,7 @@ class UpdateActionRecordSplitHelperTest extends BaseTest
|
||||
.withField(new QFieldMetaData("B", QFieldType.INTEGER))
|
||||
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME)));
|
||||
|
||||
Instant now = Instant.now();
|
||||
UpdateInput updateInput = new UpdateInput(tableName)
|
||||
.withRecord(new QRecord().withValue("id", 1).withValue("A", 1))
|
||||
.withRecord(new QRecord().withValue("id", 2).withValue("A", 2))
|
||||
@ -71,6 +71,7 @@ class UpdateActionRecordSplitHelperTest extends BaseTest
|
||||
.withRecord(new QRecord().withValue("id", 4).withValue("B", 3))
|
||||
.withRecord(new QRecord().withValue("id", 5).withValue("B", 3))
|
||||
.withRecord(new QRecord().withValue("id", 6).withValue("A", 4).withValue("B", 5));
|
||||
updateInput.getRecords().forEach(r -> r.setValue("modifyDate", now));
|
||||
UpdateActionRecordSplitHelper updateActionRecordSplitHelper = new UpdateActionRecordSplitHelper();
|
||||
updateActionRecordSplitHelper.init(updateInput);
|
||||
ListingHash<List<String>, QRecord> recordsByFieldBeingUpdated = updateActionRecordSplitHelper.getRecordsByFieldBeingUpdated();
|
||||
@ -78,12 +79,6 @@ class UpdateActionRecordSplitHelperTest extends BaseTest
|
||||
Function<Collection<QRecord>, Set<Integer>> extractIds = (records) ->
|
||||
records.stream().map(r -> r.getValueInteger("id")).collect(Collectors.toSet());
|
||||
|
||||
////////////////////////////////////////
|
||||
// validate that modify dates got set //
|
||||
////////////////////////////////////////
|
||||
updateInput.getRecords().forEach(r ->
|
||||
assertThat(r.getValue("modifyDate")).isInstanceOf(Instant.class));
|
||||
|
||||
//////////////////////////////////////////////////////////////
|
||||
// validate the grouping of records by fields-being-updated //
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
@ -39,7 +39,9 @@ import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for ValueBehaviorApplier
|
||||
** Unit test for ValueBehaviorApplier - and also providing coverage for
|
||||
** ValueTooLongBehavior (the first implementation, which was previously in the
|
||||
** class under test).
|
||||
*******************************************************************************/
|
||||
class ValueBehaviorApplierTest extends BaseTest
|
||||
{
|
||||
@ -61,7 +63,7 @@ class ValueBehaviorApplierTest extends BaseTest
|
||||
new QRecord().withValue("id", 2).withValue("firstName", "John").withValue("lastName", "Last name too long").withValue("email", "john@smith.com"),
|
||||
new QRecord().withValue("id", 3).withValue("firstName", "First name too long").withValue("lastName", "Smith").withValue("email", "john.smith@emaildomainwayytolongtofit.com")
|
||||
);
|
||||
ValueBehaviorApplier.applyFieldBehaviors(qInstance, table, recordList);
|
||||
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.INSERT, qInstance, table, recordList);
|
||||
|
||||
assertEquals("First name", getRecordById(recordList, 1).getValueString("firstName"));
|
||||
assertEquals("Last na...", getRecordById(recordList, 2).getValueString("lastName"));
|
||||
@ -93,7 +95,7 @@ class ValueBehaviorApplierTest extends BaseTest
|
||||
new QRecord().withValue("id", 1).withValue("firstName", "First name too long").withValue("lastName", null).withValue("email", "john@smith.com"),
|
||||
new QRecord().withValue("id", 2).withValue("firstName", "").withValue("lastName", "Last name too long").withValue("email", "john@smith.com")
|
||||
);
|
||||
ValueBehaviorApplier.applyFieldBehaviors(qInstance, table, recordList);
|
||||
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.INSERT, qInstance, table, recordList);
|
||||
|
||||
assertEquals("First name too long", getRecordById(recordList, 1).getValueString("firstName"));
|
||||
assertNull(getRecordById(recordList, 1).getValueString("lastName"));
|
||||
|
@ -29,6 +29,7 @@ import java.util.Optional;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
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.DynamicDefaultValueBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldAdornment;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
@ -493,4 +494,39 @@ class QInstanceEnricherTest extends BaseTest
|
||||
return (tableMetaData);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testCreateDateAndModifyDateBehaviors()
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
qInstance.addTable(newTable("A", "id", "createDate", "modifyDate"));
|
||||
QTableMetaData table = qInstance.getTable("A");
|
||||
|
||||
////////////////////////////////////////////////
|
||||
// make sure behavior wasn't there by default //
|
||||
////////////////////////////////////////////////
|
||||
assertNull(table.getField("createDate").getBehaviorOnlyIfSet(DynamicDefaultValueBehavior.class));
|
||||
assertNull(table.getField("modifyDate").getBehaviorOnlyIfSet(DynamicDefaultValueBehavior.class));
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// make sure if config'ing off the adding of the behavior works //
|
||||
//////////////////////////////////////////////////////////////////
|
||||
new QInstanceEnricher(qInstance)
|
||||
.withConfigAddDynamicDefaultValuesToFieldsNamedCreateDateAndModifyDate(false)
|
||||
.enrich();
|
||||
assertNull(table.getField("createDate").getBehaviorOnlyIfSet(DynamicDefaultValueBehavior.class));
|
||||
assertNull(table.getField("modifyDate").getBehaviorOnlyIfSet(DynamicDefaultValueBehavior.class));
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// make sure default value for the config (e.g., in a new enricher) is to add the behavior //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
new QInstanceEnricher(qInstance).enrich();
|
||||
assertEquals(DynamicDefaultValueBehavior.CREATE_DATE, table.getField("createDate").getBehaviorOnlyIfSet(DynamicDefaultValueBehavior.class));
|
||||
assertEquals(DynamicDefaultValueBehavior.MODIFY_DATE, table.getField("modifyDate").getBehaviorOnlyIfSet(DynamicDefaultValueBehavior.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.model.metadata.fields;
|
||||
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.ValueBehaviorApplier;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for DynamicDefaultValueBehavior
|
||||
*******************************************************************************/
|
||||
class DynamicDefaultValueBehaviorTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testCreateDateHappyPath()
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QTableMetaData table = qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
|
||||
QRecord record = new QRecord().withValue("id", 1);
|
||||
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.INSERT, qInstance, table, List.of(record));
|
||||
|
||||
assertNotNull(record.getValue("createDate"));
|
||||
assertNotNull(record.getValue("modifyDate"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testModifyDateHappyPath()
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QTableMetaData table = qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
|
||||
QRecord record = new QRecord().withValue("id", 1);
|
||||
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.UPDATE, qInstance, table, List.of(record));
|
||||
|
||||
assertNull(record.getValue("createDate"));
|
||||
assertNotNull(record.getValue("modifyDate"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testNone()
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QTableMetaData table = qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
table.getField("createDate").withBehavior(DynamicDefaultValueBehavior.NONE);
|
||||
table.getField("modifyDate").withBehavior(DynamicDefaultValueBehavior.NONE);
|
||||
|
||||
QRecord record = new QRecord().withValue("id", 1);
|
||||
|
||||
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.INSERT, qInstance, table, List.of(record));
|
||||
assertNull(record.getValue("createDate"));
|
||||
assertNull(record.getValue("modifyDate"));
|
||||
|
||||
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.UPDATE, qInstance, table, List.of(record));
|
||||
assertNull(record.getValue("createDate"));
|
||||
assertNull(record.getValue("modifyDate"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testDateInsteadOfDateTimeField()
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QTableMetaData table = qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
table.getField("createDate").withType(QFieldType.DATE);
|
||||
|
||||
QRecord record = new QRecord().withValue("id", 1);
|
||||
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.INSERT, qInstance, table, List.of(record));
|
||||
assertNotNull(record.getValue("createDate"));
|
||||
assertThat(record.getValue("createDate")).isInstanceOf(LocalDate.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testNonDateField()
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
QTableMetaData table = qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
table.getField("firstName").withBehavior(DynamicDefaultValueBehavior.CREATE_DATE);
|
||||
|
||||
QRecord record = new QRecord().withValue("id", 1);
|
||||
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.INSERT, qInstance, table, List.of(record));
|
||||
assertNull(record.getValue("firstName"));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.model.metadata.fields;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for QFieldMetaData
|
||||
*******************************************************************************/
|
||||
class QFieldMetaDataTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testFieldBehaviors()
|
||||
{
|
||||
/////////////////////////////////////////
|
||||
// create field - assert default state //
|
||||
/////////////////////////////////////////
|
||||
QFieldMetaData field = new QFieldMetaData("createDate", QFieldType.DATE_TIME);
|
||||
assertTrue(CollectionUtils.nullSafeIsEmpty(field.getBehaviors()));
|
||||
assertNull(field.getBehaviorOnlyIfSet(DynamicDefaultValueBehavior.class));
|
||||
assertEquals(DynamicDefaultValueBehavior.NONE, field.getBehaviorOrDefault(new QInstance(), DynamicDefaultValueBehavior.class));
|
||||
|
||||
//////////////////////////////////////
|
||||
// add NONE behavior - assert state //
|
||||
//////////////////////////////////////
|
||||
field.withBehavior(DynamicDefaultValueBehavior.NONE);
|
||||
assertEquals(1, field.getBehaviors().size());
|
||||
assertEquals(DynamicDefaultValueBehavior.NONE, field.getBehaviorOnlyIfSet(DynamicDefaultValueBehavior.class));
|
||||
assertEquals(DynamicDefaultValueBehavior.NONE, field.getBehaviorOrDefault(new QInstance(), DynamicDefaultValueBehavior.class));
|
||||
|
||||
/////////////////////////////////////////////////////////
|
||||
// replace behavior - assert it got rid of the old one //
|
||||
/////////////////////////////////////////////////////////
|
||||
field.withBehavior(DynamicDefaultValueBehavior.CREATE_DATE);
|
||||
assertEquals(1, field.getBehaviors().size());
|
||||
assertEquals(DynamicDefaultValueBehavior.CREATE_DATE, field.getBehaviorOnlyIfSet(DynamicDefaultValueBehavior.class));
|
||||
assertEquals(DynamicDefaultValueBehavior.CREATE_DATE, field.getBehaviorOrDefault(new QInstance(), DynamicDefaultValueBehavior.class));
|
||||
}
|
||||
|
||||
}
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -78,6 +78,20 @@ class StringUtilsTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void test_safeAppend()
|
||||
{
|
||||
assertEquals("Foo", StringUtils.safeAppend("Foo", null));
|
||||
assertEquals("Foo", StringUtils.safeAppend(null, "Foo"));
|
||||
assertEquals("FooBar", StringUtils.safeAppend("Foo", "Bar"));
|
||||
assertEquals("", StringUtils.safeAppend(null, null));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -96,6 +96,7 @@ import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
@ -1081,7 +1082,7 @@ public class BaseAPIActionUtil
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// trim response body (just to keep logs smaller, or, in case someone consuming logs doesn't want such long lines) //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
LOG.info("Received successful response with code [" + qResponse.getStatusCode() + "] and content [" + StringUtils.safeTruncate(qResponse.getContent(), getMaxResponseMessageLengthForLog(), "...") + "].");
|
||||
LOG.log(getAPIResponseLogLevel(), "Received successful response with code [" + qResponse.getStatusCode() + "] and content [" + StringUtils.safeTruncate(qResponse.getContent(), getMaxResponseMessageLengthForLog(), "...") + "].");
|
||||
return (qResponse);
|
||||
}
|
||||
}
|
||||
@ -1507,4 +1508,14 @@ public class BaseAPIActionUtil
|
||||
// nothing to do at this layer, meant to be overridden by subclasses //
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected Level getAPIResponseLogLevel() throws QException
|
||||
{
|
||||
return (Level.DEBUG);
|
||||
}
|
||||
}
|
||||
|
@ -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.tables.AssociatedScript;
|
||||
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.processes.implementations.mock.MockBackendStep;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
|
||||
@ -157,7 +157,7 @@ public class TestUtils
|
||||
qInstance.addBackend(defineMemoryBackend());
|
||||
try
|
||||
{
|
||||
new SavedFiltersMetaDataProvider().defineAll(qInstance, defineMemoryBackend().getName(), null);
|
||||
new SavedViewsMetaDataProvider().defineAll(qInstance, defineMemoryBackend().getName(), null);
|
||||
new ScriptsMetaDataProvider().defineAll(qInstance, defineMemoryBackend().getName(), null);
|
||||
}
|
||||
catch(Exception e)
|
||||
|
Reference in New Issue
Block a user