mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 13:10:44 +00:00
CE-1072 Add DateTimeDisplayValueBehavior as first version of FieldDisplayBehavior, sub-interface of FieldBehavior, called through ValueBehaviorApplier by QValueFormatter;
This commit is contained in:
@ -28,7 +28,6 @@ import java.time.LocalDateTime;
|
|||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.time.format.DateTimeFormatter;
|
import java.time.format.DateTimeFormatter;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -364,7 +363,9 @@ public class QValueFormatter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setDisplayValuesInRecord(fieldMap, record);
|
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.FORMATTING, QContext.getQInstance(), table, records, null);
|
||||||
|
|
||||||
|
setDisplayValuesInRecord(table, fieldMap, record, true);
|
||||||
record.setRecordLabel(formatRecordLabel(table, record));
|
record.setRecordLabel(formatRecordLabel(table, record));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -374,61 +375,49 @@ public class QValueFormatter
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** For a list of records, set their recordLabels and display values
|
** For a list of records, set their recordLabels and display values
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static void setDisplayValuesInRecords(Collection<QFieldMetaData> fields, List<QRecord> records)
|
public static void setDisplayValuesInRecords(QTableMetaData table, Map<String, QFieldMetaData> fields, List<QRecord> records)
|
||||||
{
|
{
|
||||||
if(records == null)
|
if(records == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(table != null)
|
||||||
|
{
|
||||||
|
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.FORMATTING, QContext.getQInstance(), table, records, null);
|
||||||
|
}
|
||||||
|
|
||||||
for(QRecord record : records)
|
for(QRecord record : records)
|
||||||
{
|
{
|
||||||
setDisplayValuesInRecord(fields, record);
|
setDisplayValuesInRecord(table, fields, record, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** For a list of records, set their recordLabels and display values
|
** For a single record, set its display values - public version of this.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static void setDisplayValuesInRecords(Map<String, QFieldMetaData> fields, List<QRecord> records)
|
public static void setDisplayValuesInRecord(QTableMetaData table, Map<String, QFieldMetaData> fields, QRecord record)
|
||||||
{
|
{
|
||||||
if(records == null)
|
setDisplayValuesInRecord(table, fields, record, false);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(QRecord record : records)
|
|
||||||
{
|
|
||||||
setDisplayValuesInRecord(fields, record);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** For a list of records, set their display values
|
** For a single record, set its display values - where caller (meant to stay private)
|
||||||
|
** can specify if they've already done fieldBehaviors (to avoid re-doing).
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static void setDisplayValuesInRecord(Collection<QFieldMetaData> fields, QRecord record)
|
private static void setDisplayValuesInRecord(QTableMetaData table, Map<String, QFieldMetaData> fields, QRecord record, boolean alreadyAppliedFieldDisplayBehaviors)
|
||||||
{
|
{
|
||||||
for(QFieldMetaData field : fields)
|
if(!alreadyAppliedFieldDisplayBehaviors)
|
||||||
{
|
{
|
||||||
if(record.getDisplayValue(field.getName()) == null)
|
if(table != null)
|
||||||
{
|
{
|
||||||
String formattedValue = formatValue(field, record.getValue(field.getName()));
|
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.FORMATTING, QContext.getQInstance(), table, List.of(record), null);
|
||||||
record.setDisplayValue(field.getName(), formattedValue);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
** For a list of records, set their display values
|
|
||||||
*******************************************************************************/
|
|
||||||
public static void setDisplayValuesInRecord(Map<String, QFieldMetaData> fields, QRecord record)
|
|
||||||
{
|
|
||||||
for(Map.Entry<String, QFieldMetaData> entry : fields.entrySet())
|
for(Map.Entry<String, QFieldMetaData> entry : fields.entrySet())
|
||||||
{
|
{
|
||||||
String fieldName = entry.getKey();
|
String fieldName = entry.getKey();
|
||||||
|
@ -27,6 +27,7 @@ import java.util.Set;
|
|||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
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.FieldBehavior;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldDisplayBehavior;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
@ -44,7 +45,8 @@ public class ValueBehaviorApplier
|
|||||||
public enum Action
|
public enum Action
|
||||||
{
|
{
|
||||||
INSERT,
|
INSERT,
|
||||||
UPDATE
|
UPDATE,
|
||||||
|
FORMATTING
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -63,7 +65,34 @@ public class ValueBehaviorApplier
|
|||||||
{
|
{
|
||||||
for(FieldBehavior<?> fieldBehavior : CollectionUtils.nonNullCollection(field.getBehaviors()))
|
for(FieldBehavior<?> fieldBehavior : CollectionUtils.nonNullCollection(field.getBehaviors()))
|
||||||
{
|
{
|
||||||
fieldBehavior.apply(action, recordList, instance, table, field, behaviorsToOmit);
|
boolean applyBehavior = true;
|
||||||
|
if(behaviorsToOmit != null && behaviorsToOmit.contains(fieldBehavior))
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// if we're given a set of behaviors to omit, and this behavior is in there, then skip //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
applyBehavior = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Action.FORMATTING == action && !(fieldBehavior instanceof FieldDisplayBehavior<?>))
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// for the formatting action, do not apply the behavior unless it is a field-display-behavior //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
applyBehavior = false;
|
||||||
|
}
|
||||||
|
else if(Action.FORMATTING != action && fieldBehavior instanceof FieldDisplayBehavior<?>)
|
||||||
|
{
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// for non-formatting actions, do not apply the behavior IF it is a field-display-behavior //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
applyBehavior = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(applyBehavior)
|
||||||
|
{
|
||||||
|
fieldBehavior.apply(action, recordList, instance, table, field);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,6 +65,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.dashboard.QWidgetMetaDataIn
|
|||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.AdornmentType;
|
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.fields.FieldAdornment;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldBehavior;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.joins.QJoinMetaData;
|
||||||
@ -810,7 +811,7 @@ public class QInstanceValidator
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private void validateTableField(QInstance qInstance, String tableName, String fieldName, QTableMetaData table, QFieldMetaData field)
|
private <T extends FieldBehavior<T>> void validateTableField(QInstance qInstance, String tableName, String fieldName, QTableMetaData table, QFieldMetaData field)
|
||||||
{
|
{
|
||||||
assertCondition(Objects.equals(fieldName, field.getName()),
|
assertCondition(Objects.equals(fieldName, field.getName()),
|
||||||
"Inconsistent naming in table " + tableName + " for field " + fieldName + "/" + field.getName() + ".");
|
"Inconsistent naming in table " + tableName + " for field " + fieldName + "/" + field.getName() + ".");
|
||||||
@ -823,12 +824,32 @@ public class QInstanceValidator
|
|||||||
|
|
||||||
String prefix = "Field " + fieldName + " in table " + tableName + " ";
|
String prefix = "Field " + fieldName + " in table " + tableName + " ";
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////
|
||||||
|
// validate things we know about field behaviors //
|
||||||
|
///////////////////////////////////////////////////
|
||||||
ValueTooLongBehavior behavior = field.getBehaviorOrDefault(qInstance, ValueTooLongBehavior.class);
|
ValueTooLongBehavior behavior = field.getBehaviorOrDefault(qInstance, ValueTooLongBehavior.class);
|
||||||
if(behavior != null && !behavior.equals(ValueTooLongBehavior.PASS_THROUGH))
|
if(behavior != null && !behavior.equals(ValueTooLongBehavior.PASS_THROUGH))
|
||||||
{
|
{
|
||||||
assertCondition(field.getMaxLength() != null, prefix + "specifies a ValueTooLongBehavior, but not a maxLength.");
|
assertCondition(field.getMaxLength() != null, prefix + "specifies a ValueTooLongBehavior, but not a maxLength.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Set<Class<FieldBehavior<T>>> usedFieldBehaviorTypes = new HashSet<>();
|
||||||
|
if(field.getBehaviors() != null)
|
||||||
|
{
|
||||||
|
for(FieldBehavior<?> fieldBehavior : field.getBehaviors())
|
||||||
|
{
|
||||||
|
Class<FieldBehavior<T>> behaviorClass = (Class<FieldBehavior<T>>) fieldBehavior.getClass();
|
||||||
|
|
||||||
|
errors.addAll(fieldBehavior.validateBehaviorConfiguration(table, field));
|
||||||
|
|
||||||
|
if(!fieldBehavior.allowMultipleBehaviorsOfThisType())
|
||||||
|
{
|
||||||
|
assertCondition(!usedFieldBehaviorTypes.contains(behaviorClass), prefix + "has more than 1 fieldBehavior of type " + behaviorClass.getSimpleName() + ", which is not allowed for this type");
|
||||||
|
}
|
||||||
|
usedFieldBehaviorTypes.add(behaviorClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(field.getMaxLength() != null)
|
if(field.getMaxLength() != null)
|
||||||
{
|
{
|
||||||
assertCondition(field.getMaxLength() > 0, prefix + "has an invalid maxLength (" + field.getMaxLength() + ") - must be greater than 0.");
|
assertCondition(field.getMaxLength() > 0, prefix + "has an invalid maxLength (" + field.getMaxLength() + ") - must be greater than 0.");
|
||||||
|
@ -27,6 +27,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
||||||
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
|
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
@ -147,7 +148,7 @@ public class FieldValueListData extends QWidgetData
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QValueFormatter.setDisplayValuesInRecord(fields, record);
|
QValueFormatter.setDisplayValuesInRecord(null, fields.stream().collect(Collectors.toMap(f -> f.getName(), f -> f)), record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -0,0 +1,224 @@
|
|||||||
|
/*
|
||||||
|
* 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.Instant;
|
||||||
|
import java.time.ZoneId;
|
||||||
|
import java.time.ZonedDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
||||||
|
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 com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
|
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class DateTimeDisplayValueBehavior implements FieldDisplayBehavior<DateTimeDisplayValueBehavior>
|
||||||
|
{
|
||||||
|
private static final QLogger LOG = QLogger.getLogger(DateTimeDisplayValueBehavior.class);
|
||||||
|
|
||||||
|
private String zoneIdFromFieldName;
|
||||||
|
private String defaultZoneId;
|
||||||
|
|
||||||
|
private static DateTimeDisplayValueBehavior NOOP = new DateTimeDisplayValueBehavior();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public DateTimeDisplayValueBehavior getDefault()
|
||||||
|
{
|
||||||
|
return NOOP;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void apply(ValueBehaviorApplier.Action action, List<QRecord> recordList, QInstance instance, QTableMetaData table, QFieldMetaData field)
|
||||||
|
{
|
||||||
|
if(StringUtils.hasContent(defaultZoneId))
|
||||||
|
{
|
||||||
|
applyDefaultZoneId(recordList, table, field);
|
||||||
|
}
|
||||||
|
else if(StringUtils.hasContent(zoneIdFromFieldName))
|
||||||
|
{
|
||||||
|
applyZoneIdFromFieldName(recordList, table, field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private void applyDefaultZoneId(List<QRecord> recordList, QTableMetaData table, QFieldMetaData field)
|
||||||
|
{
|
||||||
|
for(QRecord record : CollectionUtils.nonNullList(recordList))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Instant instant = record.getValueInstant(field.getName());
|
||||||
|
ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of(defaultZoneId));
|
||||||
|
record.setDisplayValue(field.getName(), QValueFormatter.formatDateTimeWithZone(zonedDateTime));
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
LOG.info("Error applying defaultZoneId DateTimeDisplayValueBehavior", logPair("table", table.getName()), logPair("field", field.getName()), logPair("id", record.getValue(table.getPrimaryKeyField())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private void applyZoneIdFromFieldName(List<QRecord> recordList, QTableMetaData table, QFieldMetaData field)
|
||||||
|
{
|
||||||
|
for(QRecord record : CollectionUtils.nonNullList(recordList))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Instant instant = record.getValueInstant(field.getName());
|
||||||
|
String zoneId = record.getValueString(zoneIdFromFieldName);
|
||||||
|
ZonedDateTime zonedDateTime = instant.atZone(ZoneId.of(zoneId));
|
||||||
|
record.setDisplayValue(field.getName(), QValueFormatter.formatDateTimeWithZone(zonedDateTime));
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
LOG.info("Error applying zoneIdFromFieldName DateTimeDisplayValueBehavior", logPair("table", table.getName()), logPair("field", field.getName()), logPair("id", record.getValue(table.getPrimaryKeyField())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public List<String> validateBehaviorConfiguration(QTableMetaData tableMetaData, QFieldMetaData fieldMetaData)
|
||||||
|
{
|
||||||
|
List<String> errors = new ArrayList<>();
|
||||||
|
String errorSuffix = " field [" + fieldMetaData.getName() + "] in table [" + tableMetaData.getName() + "]";
|
||||||
|
|
||||||
|
if(!QFieldType.DATE_TIME.equals(fieldMetaData.getType()))
|
||||||
|
{
|
||||||
|
errors.add("A DateTimeDisplayValueBehavior was a applied to a non-DATE_TIME" + errorSuffix);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(StringUtils.hasContent(zoneIdFromFieldName))
|
||||||
|
{
|
||||||
|
if(!tableMetaData.getFields().containsKey(zoneIdFromFieldName))
|
||||||
|
{
|
||||||
|
errors.add("Unrecognized field name [" + zoneIdFromFieldName + "] for [zoneIdFromFieldName] in DateTimeDisplayValueBehavior on" + errorSuffix);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
QFieldMetaData zoneIdField = tableMetaData.getFields().get(zoneIdFromFieldName);
|
||||||
|
if(!QFieldType.STRING.equals(zoneIdField.getType()))
|
||||||
|
{
|
||||||
|
errors.add("A non-STRING type [" + zoneIdField.getType() + "] was specified as the zoneIdFromFieldName field [" + zoneIdFromFieldName + "] in DateTimeDisplayValueBehavior on" + errorSuffix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (errors);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for zoneIdFromFieldName
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getZoneIdFromFieldName()
|
||||||
|
{
|
||||||
|
return (this.zoneIdFromFieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for zoneIdFromFieldName
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setZoneIdFromFieldName(String zoneIdFromFieldName)
|
||||||
|
{
|
||||||
|
this.zoneIdFromFieldName = zoneIdFromFieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for zoneIdFromFieldName
|
||||||
|
*******************************************************************************/
|
||||||
|
public DateTimeDisplayValueBehavior withZoneIdFromFieldName(String zoneIdFromFieldName)
|
||||||
|
{
|
||||||
|
this.zoneIdFromFieldName = zoneIdFromFieldName;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for defaultZoneId
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getDefaultZoneId()
|
||||||
|
{
|
||||||
|
return (this.defaultZoneId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for defaultZoneId
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setDefaultZoneId(String defaultZoneId)
|
||||||
|
{
|
||||||
|
this.defaultZoneId = defaultZoneId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for defaultZoneId
|
||||||
|
*******************************************************************************/
|
||||||
|
public DateTimeDisplayValueBehavior withDefaultZoneId(String defaultZoneId)
|
||||||
|
{
|
||||||
|
this.defaultZoneId = defaultZoneId;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* QQQ - Low-code Application Framework for Engineers.
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
* contact@kingsrook.com
|
* contact@kingsrook.com
|
||||||
* https://github.com/Kingsrook/
|
* https://github.com/Kingsrook/
|
||||||
@ -26,7 +26,6 @@ import java.io.Serializable;
|
|||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.values.ValueBehaviorApplier;
|
import com.kingsrook.qqq.backend.core.actions.values.ValueBehaviorApplier;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
@ -66,16 +65,12 @@ public enum DynamicDefaultValueBehavior implements FieldBehavior<DynamicDefaultV
|
|||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Override
|
@Override
|
||||||
public void apply(ValueBehaviorApplier.Action action, List<QRecord> recordList, QInstance instance, QTableMetaData table, QFieldMetaData field, Set<FieldBehavior<?>> behaviorsToOmit)
|
public void apply(ValueBehaviorApplier.Action action, List<QRecord> recordList, QInstance instance, QTableMetaData table, QFieldMetaData field)
|
||||||
{
|
{
|
||||||
if(this.equals(NONE))
|
if(this.equals(NONE))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(behaviorsToOmit != null && behaviorsToOmit.contains(this))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(this)
|
switch(this)
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* QQQ - Low-code Application Framework for Engineers.
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
* contact@kingsrook.com
|
* contact@kingsrook.com
|
||||||
* https://github.com/Kingsrook/
|
* https://github.com/Kingsrook/
|
||||||
@ -22,8 +22,8 @@
|
|||||||
package com.kingsrook.qqq.backend.core.model.metadata.fields;
|
package com.kingsrook.qqq.backend.core.model.metadata.fields;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.values.ValueBehaviorApplier;
|
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.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
@ -34,8 +34,10 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
|||||||
** Interface for (expected to be?) enums which define behaviors that get applied
|
** Interface for (expected to be?) enums which define behaviors that get applied
|
||||||
** to fields.
|
** to fields.
|
||||||
**
|
**
|
||||||
** At the present, these behaviors get applied before a field is stored (insert
|
** Some of these behaviors get applied before a field is stored (insert
|
||||||
** or update), through the ValueBehaviorApplier class.
|
** or update), through the ValueBehaviorApplier class. Others can be used to
|
||||||
|
** do more advanced display formatting than the displayFormat string alone can
|
||||||
|
** do (see QValueFormatter).
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public interface FieldBehavior<T extends FieldBehavior<T>>
|
public interface FieldBehavior<T extends FieldBehavior<T>>
|
||||||
@ -50,7 +52,7 @@ public interface FieldBehavior<T extends FieldBehavior<T>>
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Apply this behavior to a list of records
|
** Apply this behavior to a list of records
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
void apply(ValueBehaviorApplier.Action action, List<QRecord> recordList, QInstance instance, QTableMetaData table, QFieldMetaData field, Set<FieldBehavior<?>> behaviorsToOmit);
|
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.
|
** control if multiple behaviors of this type should be allowed together on a field.
|
||||||
@ -60,4 +62,14 @@ public interface FieldBehavior<T extends FieldBehavior<T>>
|
|||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** allow this behavior to be validated during QInstance validation.
|
||||||
|
**
|
||||||
|
** return a list of validation errors, if there are any.
|
||||||
|
*******************************************************************************/
|
||||||
|
default List<String> validateBehaviorConfiguration(QTableMetaData tableMetaData, QFieldMetaData fieldMetaData)
|
||||||
|
{
|
||||||
|
return (Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public interface FieldDisplayBehavior<T extends FieldDisplayBehavior<T>> extends FieldBehavior<T>
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
@ -716,6 +716,17 @@ public class QFieldMetaData implements Cloneable
|
|||||||
{
|
{
|
||||||
return (behaviorType.getEnumConstants()[0].getDefault());
|
return (behaviorType.getEnumConstants()[0].getDefault());
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return behaviorType.getConstructor().newInstance().getDefault();
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
LOG.warn("Error getting default behaviorType for [" + behaviorType.getSimpleName() + "]", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (null);
|
return (null);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* QQQ - Low-code Application Framework for Engineers.
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
* Copyright (C) 2021-2024. Kingsrook, LLC
|
||||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
* contact@kingsrook.com
|
* contact@kingsrook.com
|
||||||
* https://github.com/Kingsrook/
|
* https://github.com/Kingsrook/
|
||||||
@ -23,7 +23,6 @@ package com.kingsrook.qqq.backend.core.model.metadata.fields;
|
|||||||
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.values.ValueBehaviorApplier;
|
import com.kingsrook.qqq.backend.core.actions.values.ValueBehaviorApplier;
|
||||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
@ -66,16 +65,12 @@ public enum ValueTooLongBehavior implements FieldBehavior<ValueTooLongBehavior>
|
|||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Override
|
@Override
|
||||||
public void apply(ValueBehaviorApplier.Action action, List<QRecord> recordList, QInstance instance, QTableMetaData table, QFieldMetaData field, Set<FieldBehavior<?>> behaviorsToOmit)
|
public void apply(ValueBehaviorApplier.Action action, List<QRecord> recordList, QInstance instance, QTableMetaData table, QFieldMetaData field)
|
||||||
{
|
{
|
||||||
if(this.equals(PASS_THROUGH))
|
if(this.equals(PASS_THROUGH))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(behaviorsToOmit != null && behaviorsToOmit.contains(this))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String fieldName = field.getName();
|
String fieldName = field.getName();
|
||||||
if(!QFieldType.STRING.equals(field.getType()))
|
if(!QFieldType.STRING.equals(field.getType()))
|
||||||
|
@ -34,6 +34,7 @@ import java.util.Iterator;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.DateTimeGroupBy;
|
import com.kingsrook.qqq.backend.core.actions.dashboard.widgets.DateTimeGroupBy;
|
||||||
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
|
import com.kingsrook.qqq.backend.core.actions.permissions.PermissionsHelper;
|
||||||
import com.kingsrook.qqq.backend.core.actions.permissions.TablePermissionSubType;
|
import com.kingsrook.qqq.backend.core.actions.permissions.TablePermissionSubType;
|
||||||
@ -252,7 +253,7 @@ public class ColumnStatsStep implements BackendStep
|
|||||||
|
|
||||||
QPossibleValueTranslator qPossibleValueTranslator = new QPossibleValueTranslator();
|
QPossibleValueTranslator qPossibleValueTranslator = new QPossibleValueTranslator();
|
||||||
qPossibleValueTranslator.translatePossibleValuesInRecords(table, valueCounts, queryJoin == null ? null : List.of(queryJoin), null);
|
qPossibleValueTranslator.translatePossibleValuesInRecords(table, valueCounts, queryJoin == null ? null : List.of(queryJoin), null);
|
||||||
QValueFormatter.setDisplayValuesInRecords(Map.of(fieldName, field, "count", countField), valueCounts);
|
QValueFormatter.setDisplayValuesInRecords(table, Map.of(fieldName, field, "count", countField), valueCounts);
|
||||||
|
|
||||||
runBackendStepOutput.addValue("valueCounts", valueCounts);
|
runBackendStepOutput.addValue("valueCounts", valueCounts);
|
||||||
|
|
||||||
@ -442,13 +443,13 @@ public class ColumnStatsStep implements BackendStep
|
|||||||
}
|
}
|
||||||
|
|
||||||
QFieldMetaData percentField = new QFieldMetaData("percent", QFieldType.DECIMAL).withDisplayFormat(DisplayFormat.PERCENT_POINT2).withLabel("Percent");
|
QFieldMetaData percentField = new QFieldMetaData("percent", QFieldType.DECIMAL).withDisplayFormat(DisplayFormat.PERCENT_POINT2).withLabel("Percent");
|
||||||
QValueFormatter.setDisplayValuesInRecords(Map.of(fieldName, field, "percent", percentField), valueCounts);
|
QValueFormatter.setDisplayValuesInRecords(table, Map.of(fieldName, field, "percent", percentField), valueCounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
QInstanceEnricher qInstanceEnricher = new QInstanceEnricher(null);
|
QInstanceEnricher qInstanceEnricher = new QInstanceEnricher(null);
|
||||||
fields.forEach(qInstanceEnricher::enrichField);
|
fields.forEach(qInstanceEnricher::enrichField);
|
||||||
|
|
||||||
QValueFormatter.setDisplayValuesInRecord(fields, statsRecord);
|
QValueFormatter.setDisplayValuesInRecord(table, fields.stream().collect(Collectors.toMap(f -> f.getName(), f -> f)), statsRecord);
|
||||||
|
|
||||||
runBackendStepOutput.addValue("statsFields", fields);
|
runBackendStepOutput.addValue("statsFields", fields);
|
||||||
runBackendStepOutput.addValue("statsRecord", statsRecord);
|
runBackendStepOutput.addValue("statsRecord", statsRecord);
|
||||||
|
@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.actions.values;
|
|||||||
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.time.Instant;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
@ -32,10 +33,13 @@ import java.time.ZonedDateTime;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||||
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.DisplayFormat;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.DisplayFormat;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.DateTimeDisplayValueBehavior;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -210,4 +214,23 @@ class QValueFormatterTest extends BaseTest
|
|||||||
assertEquals("2023-02-01 07:15:47 PM CST", QValueFormatter.formatDateTimeWithZone(ZonedDateTime.of(LocalDateTime.of(2023, Month.FEBRUARY, 1, 19, 15, 47), ZoneId.of("US/Central"))));
|
assertEquals("2023-02-01 07:15:47 PM CST", QValueFormatter.formatDateTimeWithZone(ZonedDateTime.of(LocalDateTime.of(2023, Month.FEBRUARY, 1, 19, 15, 47), ZoneId.of("US/Central"))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testFieldDisplayBehaviors()
|
||||||
|
{
|
||||||
|
QInstance qInstance = QContext.getQInstance();
|
||||||
|
QTableMetaData table = qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||||
|
|
||||||
|
table.withField(new QFieldMetaData("timeZone", QFieldType.STRING));
|
||||||
|
table.getField("createDate").withBehavior(new DateTimeDisplayValueBehavior().withZoneIdFromFieldName("timeZone"));
|
||||||
|
|
||||||
|
QRecord record = new QRecord().withValue("createDate", Instant.parse("2024-04-04T19:12:00Z")).withValue("timeZone", "America/Chicago");
|
||||||
|
QValueFormatter.setDisplayValuesInRecords(table, List.of(record));
|
||||||
|
assertEquals("2024-04-04 02:12:00 PM CDT", record.getDisplayValue("createDate"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -29,9 +29,12 @@ import com.kingsrook.qqq.backend.core.BaseTest;
|
|||||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
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.FieldBehavior;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldBehavior;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.FieldDisplayBehavior;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
|
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.metadata.tables.QTableMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
@ -140,6 +143,36 @@ class ValueBehaviorApplierTest extends BaseTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testApplyFormattingBehaviors()
|
||||||
|
{
|
||||||
|
QInstance qInstance = QContext.getQInstance();
|
||||||
|
QTableMetaData table = qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||||
|
|
||||||
|
table.getField("firstName").withBehavior(ToUpperCaseBehavior.getInstance());
|
||||||
|
table.getField("lastName").withBehavior(ToUpperCaseBehavior.NOOP);
|
||||||
|
table.getField("ssn").withBehavior(ValueTooLongBehavior.TRUNCATE).withMaxLength(1);
|
||||||
|
|
||||||
|
QRecord record = new QRecord().withValue("firstName", "Homer").withValue("lastName", "Simpson").withValue("ssn", "0123456789");
|
||||||
|
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.FORMATTING, qInstance, table, List.of(record), null);
|
||||||
|
|
||||||
|
assertEquals("HOMER", record.getDisplayValue("firstName"));
|
||||||
|
assertNull(record.getDisplayValue("lastName")); // noop will literally do nothing, not even pass value through.
|
||||||
|
assertEquals("0123456789", record.getValueString("ssn")); // formatting action should not run the too-long truncate behavior
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// now put to-upper-case behavior on lastName, but run INSERT actions - and make sure it doesn't get applied. //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
table.getField("lastName").withBehavior(ToUpperCaseBehavior.getInstance());
|
||||||
|
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.INSERT, qInstance, table, List.of(record), null);
|
||||||
|
assertNull(record.getDisplayValue("lastName"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -153,4 +186,73 @@ class ValueBehaviorApplierTest extends BaseTest
|
|||||||
return (recordOpt.get());
|
return (recordOpt.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static class ToUpperCaseBehavior implements FieldDisplayBehavior<ToUpperCaseBehavior>
|
||||||
|
{
|
||||||
|
private final boolean enabled;
|
||||||
|
|
||||||
|
private static ToUpperCaseBehavior NOOP = new ToUpperCaseBehavior(false);
|
||||||
|
private static ToUpperCaseBehavior instance = new ToUpperCaseBehavior(true);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Constructor
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private ToUpperCaseBehavior(boolean enabled)
|
||||||
|
{
|
||||||
|
this.enabled = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public ToUpperCaseBehavior getDefault()
|
||||||
|
{
|
||||||
|
return (NOOP);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static ToUpperCaseBehavior getInstance()
|
||||||
|
{
|
||||||
|
return (instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public void apply(ValueBehaviorApplier.Action action, List<QRecord> recordList, QInstance instance, QTableMetaData table, QFieldMetaData field)
|
||||||
|
{
|
||||||
|
if(!enabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(QRecord record : CollectionUtils.nonNullList(recordList))
|
||||||
|
{
|
||||||
|
String displayValue = record.getValueString(field.getName());
|
||||||
|
if(displayValue != null)
|
||||||
|
{
|
||||||
|
displayValue = displayValue.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
record.setDisplayValue(field.getName(), displayValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
@ -56,6 +58,7 @@ 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.fields.FieldAdornment;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.DateTimeDisplayValueBehavior;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.ValueTooLongBehavior;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType;
|
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinType;
|
||||||
@ -1739,6 +1742,26 @@ public class QInstanceValidatorTest extends BaseTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testFieldBehaviors()
|
||||||
|
{
|
||||||
|
BiFunction<QInstance, String, QFieldMetaData> fieldExtractor = (QInstance qInstance, String fieldName) -> qInstance.getTable(TestUtils.TABLE_NAME_PERSON).getField(fieldName);
|
||||||
|
assertValidationFailureReasons((qInstance -> fieldExtractor.apply(qInstance, "firstName").withBehaviors(Set.of(ValueTooLongBehavior.ERROR, ValueTooLongBehavior.TRUNCATE)).withMaxLength(1)),
|
||||||
|
"more than 1 fieldBehavior of type ValueTooLongBehavior, which is not allowed");
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// make sure a custom validation method in a field behavior gets applied //
|
||||||
|
// more tests for this particular behavior are in its own test class //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
assertValidationFailureReasons((qInstance -> fieldExtractor.apply(qInstance, "firstName").withBehavior(new DateTimeDisplayValueBehavior())),
|
||||||
|
"DateTimeDisplayValueBehavior was a applied to a non-DATE_TIME field");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* 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.Instant;
|
||||||
|
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.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Unit test for DateTimeDisplayValueBehavior
|
||||||
|
*******************************************************************************/
|
||||||
|
class DateTimeDisplayValueBehaviorTest extends BaseTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void test()
|
||||||
|
{
|
||||||
|
QInstance qInstance = QContext.getQInstance();
|
||||||
|
QTableMetaData table = qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||||
|
|
||||||
|
table.withField(new QFieldMetaData("timeZone", QFieldType.STRING));
|
||||||
|
table.getField("createDate").withBehavior(new DateTimeDisplayValueBehavior().withZoneIdFromFieldName("timeZone"));
|
||||||
|
|
||||||
|
QRecord record = new QRecord().withValue("createDate", Instant.parse("2024-04-04T19:12:00Z")).withValue("timeZone", "America/Chicago");
|
||||||
|
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.FORMATTING, qInstance, table, List.of(record), null);
|
||||||
|
assertEquals("2024-04-04 02:12:00 PM CDT", record.getDisplayValue("createDate"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user