mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
Adding maxLength to fields, along with initial version of FieldBehviors and ValueBehaviorApplier, including ValueTooLongBehavior
This commit is contained in:
@ -22,11 +22,14 @@
|
||||
package com.kingsrook.qqq.backend.core.actions.dashboard;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.Charset;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.widgets.RenderWidgetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
|
||||
|
||||
@ -37,6 +40,7 @@ import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
public abstract class AbstractHTMLWidgetRenderer extends AbstractWidgetRenderer
|
||||
{
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -104,7 +108,7 @@ public abstract class AbstractHTMLWidgetRenderer extends AbstractWidgetRenderer
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected String linkTableBulkLoad(RenderWidgetInput input, String tableName) throws QException
|
||||
public static String linkTableBulkLoad(RenderWidgetInput input, String tableName) throws QException
|
||||
{
|
||||
String tablePath = input.getInstance().getTablePath(input, tableName);
|
||||
return (tablePath + "/" + tableName + ".bulkInsert");
|
||||
@ -115,10 +119,34 @@ public abstract class AbstractHTMLWidgetRenderer extends AbstractWidgetRenderer
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected String linkTableFilter(RenderWidgetInput input, String tableName, QQueryFilter filter) throws QException
|
||||
public static String linkTableFilter(RenderWidgetInput input, String tableName, QQueryFilter filter) throws QException
|
||||
{
|
||||
String tablePath = input.getInstance().getTablePath(input, tableName);
|
||||
return (tablePath + "?filter=" + URLEncoder.encode(JsonUtils.toJson(filter), Charset.defaultCharset()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static String linkRecordEdit(AbstractActionInput input, String tableName, Serializable recordId) throws QException
|
||||
{
|
||||
String tablePath = input.getInstance().getTablePath(input, tableName);
|
||||
return (tablePath + "/" + recordId + "/edit");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static String linkProcessForRecord(AbstractActionInput input, String processName, Serializable recordId) throws QException
|
||||
{
|
||||
QProcessMetaData process = input.getInstance().getProcess(processName);
|
||||
String tableName = process.getTableName();
|
||||
String tablePath = input.getInstance().getTablePath(input, tableName);
|
||||
return (tablePath + "/" + recordId + "/" + processName);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationStatusUpdater;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.ValueBehaviorApplier;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
|
||||
@ -55,6 +56,9 @@ public class InsertAction extends AbstractQActionFunction<InsertInput, InsertOut
|
||||
ActionHelper.validateSession(insertInput);
|
||||
setAutomationStatusField(insertInput);
|
||||
|
||||
ValueBehaviorApplier.applyFieldBehaviors(insertInput.getInstance(), insertInput.getTable(), insertInput.getRecords());
|
||||
// todo - need to handle records with errors coming out of here...
|
||||
|
||||
QBackendModuleInterface qModule = getBackendModuleInterface(insertInput);
|
||||
// todo pre-customization - just get to modify the request?
|
||||
InsertOutput insertOutput = qModule.getInsertInterface().execute(insertInput);
|
||||
|
@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.core.actions.tables;
|
||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.AutomationStatus;
|
||||
import com.kingsrook.qqq.backend.core.actions.automation.RecordAutomationStatusUpdater;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.ValueBehaviorApplier;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateOutput;
|
||||
@ -46,6 +47,9 @@ public class UpdateAction
|
||||
ActionHelper.validateSession(updateInput);
|
||||
setAutomationStatusField(updateInput);
|
||||
|
||||
ValueBehaviorApplier.applyFieldBehaviors(updateInput.getInstance(), updateInput.getTable(), updateInput.getRecords());
|
||||
// todo - need to handle records with errors coming out of here...
|
||||
|
||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(updateInput.getBackend());
|
||||
// todo pre-customization - just get to modify the request?
|
||||
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.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.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.utils.StringUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Utility class to apply value behaviors to records.
|
||||
*******************************************************************************/
|
||||
public class ValueBehaviorApplier
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void applyFieldBehaviors(QInstance instance, QTableMetaData table, List<QRecord> recordList)
|
||||
{
|
||||
for(QFieldMetaData field : table.getFields().values())
|
||||
{
|
||||
String fieldName = field.getName();
|
||||
if(field.getType().equals(QFieldType.STRING) && field.getMaxLength() != null)
|
||||
{
|
||||
applyValueTooLongBehavior(instance, recordList, field, fieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void applyValueTooLongBehavior(QInstance instance, List<QRecord> recordList, QFieldMetaData field, String fieldName)
|
||||
{
|
||||
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))
|
||||
{
|
||||
for(QRecord record : recordList)
|
||||
{
|
||||
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("The value for " + field.getLabel() + " is too long (max allowed length=" + field.getMaxLength() + ")");
|
||||
case PASS_THROUGH ->
|
||||
{
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected valueTooLongBehavior: " + valueTooLongBehavior);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -26,6 +26,7 @@ import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -67,6 +68,16 @@ public @interface QField
|
||||
*******************************************************************************/
|
||||
String possibleValueSourceName() default "";
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
int maxLength() default Integer.MAX_VALUE;
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
ValueTooLongBehavior valueTooLongBehavior() default ValueTooLongBehavior.PASS_THROUGH;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
// new attributes here likely need implementation in QFieldMetaData.constructFromGetter //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public interface FieldBehavior
|
||||
{
|
||||
|
||||
}
|
@ -25,12 +25,16 @@ package com.kingsrook.qqq.backend.core.model.metadata.fields;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import com.github.hervian.reflection.Fun;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QField;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
@ -56,6 +60,19 @@ public class QFieldMetaData implements Cloneable
|
||||
private Serializable defaultValue;
|
||||
private String possibleValueSourceName;
|
||||
|
||||
private Integer maxLength;
|
||||
private Set<FieldBehavior> behaviors;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// w/ longer-term vision: //
|
||||
// - more enums that implement ValueTooLongBehavior e.g., NumberOutsideRangeBehavior or DecimalPrecisionErrorBehavior //
|
||||
// - QInstance.Set<FieldBehavior> defaultFieldBehaviors //
|
||||
// - QBackendMetaData.Set<FieldBehavior> defaultFieldBehaviors //
|
||||
// - QTableMetaData.Set<FieldBehavior> defaultFieldBehaviors //
|
||||
// - inherit behaviors all the way down (up?) //
|
||||
// - instance validation to make sure you don’t specify more than 1 behavior of a given type at a given level. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private List<FieldAdornment> adornments;
|
||||
|
||||
|
||||
@ -176,6 +193,16 @@ public class QFieldMetaData implements Cloneable
|
||||
{
|
||||
setPossibleValueSourceName(fieldAnnotation.possibleValueSourceName());
|
||||
}
|
||||
|
||||
if(fieldAnnotation.maxLength() != Integer.MAX_VALUE)
|
||||
{
|
||||
setMaxLength(fieldAnnotation.maxLength());
|
||||
}
|
||||
|
||||
if(fieldAnnotation.valueTooLongBehavior() != ValueTooLongBehavior.PASS_THROUGH)
|
||||
{
|
||||
withBehavior(fieldAnnotation.valueTooLongBehavior());
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(QException qe)
|
||||
@ -532,4 +559,118 @@ public class QFieldMetaData implements Cloneable
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for maxLength
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Integer getMaxLength()
|
||||
{
|
||||
return maxLength;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for maxLength
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setMaxLength(Integer maxLength)
|
||||
{
|
||||
this.maxLength = maxLength;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for maxLength
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFieldMetaData withMaxLength(Integer maxLength)
|
||||
{
|
||||
this.maxLength = maxLength;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for behaviors
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Set<FieldBehavior> getBehaviors()
|
||||
{
|
||||
return behaviors;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public <T extends FieldBehavior> T getBehavior(QInstance instance, Class<T> behaviorType)
|
||||
{
|
||||
for(FieldBehavior fieldBehavior : CollectionUtils.nonNullCollection(behaviors))
|
||||
{
|
||||
if(behaviorType.isInstance(fieldBehavior))
|
||||
{
|
||||
return (behaviorType.cast(fieldBehavior));
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// todo - cascade/inherit behaviors down from table, backend, instance //
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////
|
||||
// return default behavior for this type //
|
||||
///////////////////////////////////////////
|
||||
if(behaviorType.equals(ValueTooLongBehavior.class))
|
||||
{
|
||||
return behaviorType.cast(ValueTooLongBehavior.getDefault());
|
||||
}
|
||||
|
||||
return (null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for behaviors
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setBehaviors(Set<FieldBehavior> behaviors)
|
||||
{
|
||||
this.behaviors = behaviors;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for behaviors
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFieldMetaData withBehaviors(Set<FieldBehavior> behaviors)
|
||||
{
|
||||
this.behaviors = behaviors;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for behaviors
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFieldMetaData withBehavior(FieldBehavior behavior)
|
||||
{
|
||||
if(behaviors == null)
|
||||
{
|
||||
behaviors = new HashSet<>();
|
||||
}
|
||||
this.behaviors.add(behavior);
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public enum ValueTooLongBehavior implements FieldBehavior
|
||||
{
|
||||
TRUNCATE,
|
||||
TRUNCATE_ELLIPSIS,
|
||||
ERROR,
|
||||
PASS_THROUGH;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static FieldBehavior getDefault()
|
||||
{
|
||||
return PASS_THROUGH;
|
||||
}
|
||||
}
|
@ -28,12 +28,15 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.CountAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||
@ -45,6 +48,7 @@ import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
|
||||
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -304,6 +308,41 @@ public class GeneralProcessUtils
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Load all rows from a table, into a map, keyed by the keyFieldName - typed as
|
||||
** the specified keyType.
|
||||
**
|
||||
** Note - null values from the key field are NOT put in the map.
|
||||
**
|
||||
** If multiple values are found for the key, they'll squash each other, and only
|
||||
** one random value will appear.
|
||||
**
|
||||
** Also, note, this is inherently unsafe, if you were to call it on a table with
|
||||
** too many rows... Caveat emptor.
|
||||
*******************************************************************************/
|
||||
public static <T extends Serializable> Map<T, QRecord> loadTableToMap(AbstractActionInput parentActionInput, String tableName, Class<T> keyType, String keyFieldName) throws QException
|
||||
{
|
||||
QueryInput queryInput = new QueryInput(parentActionInput.getInstance());
|
||||
queryInput.setSession(parentActionInput.getSession());
|
||||
queryInput.setTableName(tableName);
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
List<QRecord> records = queryOutput.getRecords();
|
||||
|
||||
Map<T, QRecord> map = new HashMap<>();
|
||||
for(QRecord record : records)
|
||||
{
|
||||
Serializable value = record.getValue(keyFieldName);
|
||||
if(value != null)
|
||||
{
|
||||
T valueAsT = ValueUtils.getValueAsType(keyType, value);
|
||||
map.put(valueAsT, record);
|
||||
}
|
||||
}
|
||||
return (map);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Note - null values from the key field are NOT put in the map.
|
||||
*******************************************************************************/
|
||||
@ -406,4 +445,19 @@ public class GeneralProcessUtils
|
||||
return (rs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static Integer count(AbstractActionInput input, String tableName, QQueryFilter filter) throws QException
|
||||
{
|
||||
CountInput countInput = new CountInput(input.getInstance());
|
||||
countInput.setSession(input.getSession());
|
||||
countInput.setTableName(tableName);
|
||||
countInput.setFilter(filter);
|
||||
CountOutput countOutput = new CountAction().execute(countInput);
|
||||
return (countOutput.getCount());
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -585,4 +585,51 @@ public class ValueUtils
|
||||
throw (new QValueException("Unsupported class " + value.getClass().getName() + " for converting to ByteArray."));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T extends Serializable> T getValueAsType(Class<T> type, Serializable value)
|
||||
{
|
||||
if(type.equals(Integer.class))
|
||||
{
|
||||
return (T) getValueAsInteger(value);
|
||||
}
|
||||
else if(type.equals(String.class))
|
||||
{
|
||||
return (T) getValueAsString(value);
|
||||
}
|
||||
else if(type.equals(Boolean.class))
|
||||
{
|
||||
return (T) getValueAsBoolean(value);
|
||||
}
|
||||
else if(type.equals(BigDecimal.class))
|
||||
{
|
||||
return (T) getValueAsBigDecimal(value);
|
||||
}
|
||||
else if(type.equals(LocalDateTime.class))
|
||||
{
|
||||
return (T) getValueAsLocalDateTime(value);
|
||||
}
|
||||
else if(type.equals(LocalDate.class))
|
||||
{
|
||||
return (T) getValueAsLocalDate(value);
|
||||
}
|
||||
else if(type.equals(Instant.class))
|
||||
{
|
||||
return (T) getValueAsInstant(value);
|
||||
}
|
||||
else if(type.equals(byte[].class))
|
||||
{
|
||||
return (T) getValueAsByteArray(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new QValueException("Unsupported type [" + type.getSimpleName() + "] in getValueAsType.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,117 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.values;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
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.ValueTooLongBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for ValueBehaviorApplier
|
||||
*******************************************************************************/
|
||||
class ValueBehaviorApplierTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testValueTooLongNormalCases()
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QTableMetaData table = qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
table.getField("firstName").withMaxLength(10).withBehavior(ValueTooLongBehavior.TRUNCATE);
|
||||
table.getField("lastName").withMaxLength(10).withBehavior(ValueTooLongBehavior.TRUNCATE_ELLIPSIS);
|
||||
table.getField("email").withMaxLength(20).withBehavior(ValueTooLongBehavior.ERROR);
|
||||
|
||||
List<QRecord> recordList = List.of(
|
||||
new QRecord().withValue("id", 1).withValue("firstName", "First name too long").withValue("lastName", "Smith").withValue("email", "john@smith.com"),
|
||||
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);
|
||||
|
||||
assertEquals("First name", getRecordById(recordList, 1).getValueString("firstName"));
|
||||
assertEquals("Last na...", getRecordById(recordList, 2).getValueString("lastName"));
|
||||
assertEquals("john.smith@emaildomainwayytolongtofit.com", getRecordById(recordList, 3).getValueString("email"));
|
||||
assertFalse(getRecordById(recordList, 3).getErrors().isEmpty());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testValueTooLongEdgeCases()
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
QTableMetaData table = qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// make sure PASS THROUGH actually does nothing, and that a maxLength w/ no behavior specified also does nothing (e.g., does PASS_THROUGH) //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
table.getField("firstName").withMaxLength(10).withBehavior(ValueTooLongBehavior.PASS_THROUGH);
|
||||
table.getField("lastName").withMaxLength(10);
|
||||
|
||||
List<QRecord> recordList = List.of(
|
||||
////////////////////////////////////////////////////////////////
|
||||
// make sure nulls and empty are okay, and don't get changed. //
|
||||
////////////////////////////////////////////////////////////////
|
||||
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);
|
||||
|
||||
assertEquals("First name too long", getRecordById(recordList, 1).getValueString("firstName"));
|
||||
assertNull(getRecordById(recordList, 1).getValueString("lastName"));
|
||||
assertEquals("Last name too long", getRecordById(recordList, 2).getValueString("lastName"));
|
||||
assertEquals("", getRecordById(recordList, 2).getValueString("firstName"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static QRecord getRecordById(List<QRecord> recordList, Integer id)
|
||||
{
|
||||
Optional<QRecord> recordOpt = recordList.stream().filter(r -> r.getValueInteger("id").equals(id)).findFirst();
|
||||
if(recordOpt.isEmpty())
|
||||
{
|
||||
fail("Didn't find record with id=" + id);
|
||||
}
|
||||
return (recordOpt.get());
|
||||
}
|
||||
|
||||
}
|
@ -22,8 +22,10 @@
|
||||
package com.kingsrook.qqq.backend.core.utils;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
@ -34,6 +36,7 @@ import java.util.GregorianCalendar;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QValueException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
@ -231,6 +234,7 @@ class ValueUtilsTest
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -251,4 +255,20 @@ class ValueUtilsTest
|
||||
assertThat(assertThrows(QValueException.class, () -> ValueUtils.getValueAsInstant(new Object())).getMessage()).contains("Unsupported class");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testGetValueAsType()
|
||||
{
|
||||
assertEquals(1, ValueUtils.getValueAsType(Integer.class, "1"));
|
||||
assertEquals("1", ValueUtils.getValueAsType(String.class, 1));
|
||||
assertEquals(BigDecimal.ONE, ValueUtils.getValueAsType(BigDecimal.class, 1));
|
||||
assertEquals(true, ValueUtils.getValueAsType(Boolean.class, "true"));
|
||||
assertArrayEquals("a".getBytes(StandardCharsets.UTF_8), ValueUtils.getValueAsType(byte[].class, "a"));
|
||||
assertThrows(QValueException.class, () -> ValueUtils.getValueAsType(Serializable.class, 1));
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user