mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-20 14:10:44 +00:00
Merged dev into feature/CE-881-create-basic-saved-reports
This commit is contained in:
@ -526,7 +526,7 @@ public class PollingAutomationPerTableRunner implements Runnable
|
||||
// note - this method - will re-query the objects, so we should have confidence that their data is fresh... //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
List<QRecord> matchingQRecords = getRecordsMatchingActionFilter(table, records, action);
|
||||
LOG.debug("Of the {} records that were pending automations, {} of them match the filter on the action {}", records.size(), matchingQRecords.size(), action);
|
||||
LOG.debug("Of the [" + records.size() + "] records that were pending automations, [" + matchingQRecords.size() + "] of them match the filter on the action:" + action);
|
||||
if(CollectionUtils.nullSafeHasContents(matchingQRecords))
|
||||
{
|
||||
LOG.debug(" Processing " + matchingQRecords.size() + " records in " + table + " for action " + action);
|
||||
@ -601,7 +601,7 @@ public class PollingAutomationPerTableRunner implements Runnable
|
||||
|
||||
/*******************************************************************************
|
||||
** Finally, actually run action code against a list of known matching records.
|
||||
** todo not commit - move to somewhere genericer
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void applyActionToMatchingRecords(QTableMetaData table, List<QRecord> records, TableAutomationAction action) throws Exception
|
||||
{
|
||||
|
@ -96,7 +96,7 @@ public class QCodeLoader
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.error("Error initializing customizer", logPair("codeReference", codeReference), e);
|
||||
LOG.error("Error initializing customizer", e, logPair("codeReference", codeReference));
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// return null here - under the assumption that during normal run-time operations, we'll never hit here //
|
||||
@ -135,7 +135,7 @@ public class QCodeLoader
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.error("Error initializing customizer", logPair("codeReference", codeReference), e);
|
||||
LOG.error("Error initializing customizer", e, logPair("codeReference", codeReference));
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// return null here - under the assumption that during normal run-time operations, we'll never hit here //
|
||||
@ -187,7 +187,7 @@ public class QCodeLoader
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.error("Error initializing customizer", logPair("codeReference", codeReference), e);
|
||||
LOG.error("Error initializing customizer", e, logPair("codeReference", codeReference));
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// return null here - under the assumption that during normal run-time operations, we'll never hit here //
|
||||
|
@ -138,7 +138,7 @@ public class RecordPipe
|
||||
{
|
||||
if(now - sleepLoopStartTime > MAX_SLEEP_LOOP_MILLIS)
|
||||
{
|
||||
LOG.warn("Giving up adding record to pipe, due to pipe being full for more than {} millis", MAX_SLEEP_LOOP_MILLIS);
|
||||
LOG.warn("Giving up adding record to pipe, due to pipe being full for more than " + MAX_SLEEP_LOOP_MILLIS + " millis");
|
||||
throw (new IllegalStateException("Giving up adding record to pipe, due to pipe staying full too long."));
|
||||
}
|
||||
LOG.trace("Record pipe.add failed (due to full pipe). Blocking.");
|
||||
|
@ -28,7 +28,6 @@ import java.time.LocalDateTime;
|
||||
import java.time.LocalTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
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));
|
||||
}
|
||||
}
|
||||
@ -374,61 +375,49 @@ public class QValueFormatter
|
||||
/*******************************************************************************
|
||||
** 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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(table != null)
|
||||
{
|
||||
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.FORMATTING, QContext.getQInstance(), table, records, null);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for(QRecord record : records)
|
||||
{
|
||||
setDisplayValuesInRecord(fields, record);
|
||||
}
|
||||
setDisplayValuesInRecord(table, fields, record, false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** 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()));
|
||||
record.setDisplayValue(field.getName(), formattedValue);
|
||||
ValueBehaviorApplier.applyFieldBehaviors(ValueBehaviorApplier.Action.FORMATTING, QContext.getQInstance(), table, List.of(record), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** 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())
|
||||
{
|
||||
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.metadata.QInstance;
|
||||
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.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
@ -44,7 +45,8 @@ public class ValueBehaviorApplier
|
||||
public enum Action
|
||||
{
|
||||
INSERT,
|
||||
UPDATE
|
||||
UPDATE,
|
||||
FORMATTING
|
||||
}
|
||||
|
||||
|
||||
@ -63,7 +65,34 @@ public class ValueBehaviorApplier
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.dashboard.ParentWidgetMetaD
|
||||
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.FieldAdornment;
|
||||
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.ValueTooLongBehavior;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.joins.JoinOn;
|
||||
@ -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()),
|
||||
"Inconsistent naming in table " + tableName + " for field " + fieldName + "/" + field.getName() + ".");
|
||||
@ -823,12 +824,32 @@ public class QInstanceValidator
|
||||
|
||||
String prefix = "Field " + fieldName + " in table " + tableName + " ";
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// validate things we know about field behaviors //
|
||||
///////////////////////////////////////////////////
|
||||
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.");
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
assertCondition(field.getMaxLength() > 0, prefix + "has an invalid maxLength (" + field.getMaxLength() + ") - must be greater than 0.");
|
||||
@ -1449,7 +1470,7 @@ public class QInstanceValidator
|
||||
private void validateScheduleMetaData(QScheduleMetaData schedule, QInstance qInstance, String prefix)
|
||||
{
|
||||
boolean isRepeat = schedule.getRepeatMillis() != null || schedule.getRepeatSeconds() != null;
|
||||
boolean isCron = StringUtils.hasContent(schedule.getCronExpression());
|
||||
boolean isCron = StringUtils.hasContent(schedule.getCronExpression());
|
||||
assertCondition(isRepeat || isCron, prefix + " either repeatMillis or repeatSeconds or cronExpression must be set");
|
||||
assertCondition(!(isRepeat && isCron), prefix + " both a repeat time and cronExpression may not be set");
|
||||
|
||||
@ -1469,8 +1490,8 @@ public class QInstanceValidator
|
||||
|
||||
if(assertCondition(StringUtils.hasContent(schedule.getCronTimeZoneId()), prefix + " a cron schedule must specify a cronTimeZoneId"))
|
||||
{
|
||||
String[] availableIDs = TimeZone.getAvailableIDs();
|
||||
Optional<String> first = Arrays.stream(availableIDs).filter(id -> id.equals(schedule.getCronTimeZoneId())).findFirst();
|
||||
String[] availableIDs = TimeZone.getAvailableIDs();
|
||||
Optional<String> first = Arrays.stream(availableIDs).filter(id -> id.equals(schedule.getCronTimeZoneId())).findFirst();
|
||||
assertCondition(first.isPresent(), prefix + " unrecognized cronTimeZoneId: " + schedule.getCronTimeZoneId());
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -150,7 +150,7 @@ public class MetaDataProducerHelper
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("error executing metaDataProducer", logPair("producer", producer.getClass().getSimpleName()), e);
|
||||
LOG.warn("error executing metaDataProducer", e, logPair("producer", producer.getClass().getSimpleName()));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -0,0 +1,331 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Field Display Behavior class for customizing the display values used
|
||||
** in date-time fields
|
||||
*******************************************************************************/
|
||||
public class DateTimeDisplayValueBehavior implements FieldDisplayBehavior<DateTimeDisplayValueBehavior>
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(DateTimeDisplayValueBehavior.class);
|
||||
|
||||
private String zoneIdFromFieldName;
|
||||
private String fallbackZoneId;
|
||||
|
||||
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 zoneString = record.getValueString(zoneIdFromFieldName);
|
||||
|
||||
ZoneId zoneId;
|
||||
try
|
||||
{
|
||||
zoneId = ZoneId.of(zoneString);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if the zone string from the other field isn't valid, and we have a fallback, try to use it //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(StringUtils.hasContent(fallbackZoneId))
|
||||
{
|
||||
zoneId = ZoneId.of(fallbackZoneId);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw (e);
|
||||
}
|
||||
}
|
||||
|
||||
ZonedDateTime zonedDateTime = instant.atZone(zoneId);
|
||||
record.setDisplayValue(field.getName(), QValueFormatter.formatDateTimeWithZone(zonedDateTime));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.info("Error applying zoneIdFromFieldName DateTimeDisplayValueBehavior", e, 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);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////
|
||||
// validate rules if zoneIdFromFieldName is set //
|
||||
//////////////////////////////////////////////////
|
||||
if(StringUtils.hasContent(zoneIdFromFieldName))
|
||||
{
|
||||
if(StringUtils.hasContent(defaultZoneId))
|
||||
{
|
||||
errors.add("You may not specify both zoneIdFromFieldName and defaultZoneId in DateTimeDisplayValueBehavior on" + errorSuffix);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////
|
||||
// validate rules if defaultZoneId is set //
|
||||
////////////////////////////////////////////
|
||||
if(StringUtils.hasContent(defaultZoneId))
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// would check that you didn't specify from zoneIdFromFieldName - but that's covered above //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
if(StringUtils.hasContent(fallbackZoneId))
|
||||
{
|
||||
errors.add("You may not specify both defaultZoneId and fallbackZoneId in DateTimeDisplayValueBehavior on" + errorSuffix);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ZoneId.of(defaultZoneId);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
errors.add("Invalid ZoneId [" + defaultZoneId + "] for [defaultZoneId] in DateTimeDisplayValueBehavior on" + errorSuffix + "; " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////
|
||||
// validate rules if fallbackZoneId is set //
|
||||
/////////////////////////////////////////////
|
||||
if(StringUtils.hasContent(fallbackZoneId))
|
||||
{
|
||||
if(!StringUtils.hasContent(zoneIdFromFieldName))
|
||||
{
|
||||
errors.add("You may only set fallbackZoneId if using zoneIdFromFieldName in DateTimeDisplayValueBehavior on" + errorSuffix);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ZoneId.of(fallbackZoneId);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
errors.add("Invalid ZoneId [" + fallbackZoneId + "] for [fallbackZoneId] in DateTimeDisplayValueBehavior on" + errorSuffix + "; " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fallbackZoneId
|
||||
*******************************************************************************/
|
||||
public String getFallbackZoneId()
|
||||
{
|
||||
return (this.fallbackZoneId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for fallbackZoneId
|
||||
*******************************************************************************/
|
||||
public void setFallbackZoneId(String fallbackZoneId)
|
||||
{
|
||||
this.fallbackZoneId = fallbackZoneId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for fallbackZoneId
|
||||
*******************************************************************************/
|
||||
public DateTimeDisplayValueBehavior withFallbackZoneId(String fallbackZoneId)
|
||||
{
|
||||
this.fallbackZoneId = fallbackZoneId;
|
||||
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/
|
||||
@ -26,7 +26,6 @@ import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import com.kingsrook.qqq.backend.core.actions.values.ValueBehaviorApplier;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
@ -70,16 +69,12 @@ public enum DynamicDefaultValueBehavior implements FieldBehavior<DynamicDefaultV
|
||||
**
|
||||
*******************************************************************************/
|
||||
@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))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(behaviorsToOmit != null && behaviorsToOmit.contains(this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch(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/
|
||||
@ -22,8 +22,9 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.fields;
|
||||
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
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;
|
||||
@ -34,8 +35,10 @@ 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.
|
||||
** Some of these behaviors get applied before a field is stored (insert
|
||||
** 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>>
|
||||
@ -45,12 +48,13 @@ 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?
|
||||
*******************************************************************************/
|
||||
@JsonIgnore
|
||||
T getDefault();
|
||||
|
||||
/*******************************************************************************
|
||||
** 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.
|
||||
@ -60,4 +64,14 @@ public interface FieldBehavior<T extends FieldBehavior<T>>
|
||||
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>
|
||||
{
|
||||
|
||||
}
|
@ -721,6 +721,17 @@ public class QFieldMetaData implements Cloneable
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
@ -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/
|
||||
@ -23,7 +23,6 @@ package com.kingsrook.qqq.backend.core.model.metadata.fields;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
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;
|
||||
@ -66,16 +65,12 @@ public enum ValueTooLongBehavior implements FieldBehavior<ValueTooLongBehavior>
|
||||
**
|
||||
*******************************************************************************/
|
||||
@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))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(behaviorsToOmit != null && behaviorsToOmit.contains(this))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
String fieldName = field.getName();
|
||||
if(!QFieldType.STRING.equals(field.getType()))
|
||||
|
@ -27,6 +27,7 @@ import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QModuleDispatchException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -89,7 +90,7 @@ public class QBackendModuleDispatcher
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.debug("Backend module [{}] could not be loaded: {}", moduleClassName, e.getMessage());
|
||||
LOG.debug("Backend module could not be loaded", e, logPair("moduleClassName", moduleClassName));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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.permissions.PermissionsHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.permissions.TablePermissionSubType;
|
||||
@ -252,7 +253,7 @@ public class ColumnStatsStep implements BackendStep
|
||||
|
||||
QPossibleValueTranslator qPossibleValueTranslator = new QPossibleValueTranslator();
|
||||
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);
|
||||
|
||||
@ -442,13 +443,13 @@ public class ColumnStatsStep implements BackendStep
|
||||
}
|
||||
|
||||
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);
|
||||
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("statsRecord", statsRecord);
|
||||
|
@ -56,7 +56,7 @@ public class MockBackendStep implements BackendStep
|
||||
|
||||
runBackendStepInput.getRecords().forEach(r ->
|
||||
{
|
||||
LOG.info("We are mocking {}: {}", r.getValueString("firstName"), r.getValue(FIELD_MOCK_VALUE));
|
||||
LOG.info("We are mocking " + r.getValueString("firstName") + ": " + r.getValue(FIELD_MOCK_VALUE));
|
||||
r.setValue(FIELD_MOCK_VALUE, "Ha ha!");
|
||||
r.setValue("greetingMessage", runBackendStepInput.getValueString(FIELD_GREETING_PREFIX) + " " + r.getValueString("firstName") + " " + runBackendStepInput.getValueString(FIELD_GREETING_SUFFIX));
|
||||
});
|
||||
|
@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.state;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -57,4 +58,10 @@ public abstract class AbstractStateKey implements Serializable
|
||||
@Override
|
||||
public abstract String toString();
|
||||
|
||||
/*******************************************************************************
|
||||
** Require all state keys to implement the getStartTime method
|
||||
*
|
||||
*******************************************************************************/
|
||||
public abstract Instant getStartTime();
|
||||
|
||||
}
|
||||
|
@ -23,9 +23,16 @@ package com.kingsrook.qqq.backend.core.state;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -33,10 +40,16 @@ import java.util.Optional;
|
||||
*******************************************************************************/
|
||||
public class InMemoryStateProvider implements StateProviderInterface
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(InMemoryStateProvider.class);
|
||||
|
||||
private static InMemoryStateProvider instance;
|
||||
|
||||
private final Map<AbstractStateKey, Object> map;
|
||||
|
||||
private static int jobPeriodSeconds = 60 * 60; // 1 hour
|
||||
private static int cleanHours = 6;
|
||||
private static int jobInitialDelay = 60 * 60 * cleanHours;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -45,6 +58,41 @@ public class InMemoryStateProvider implements StateProviderInterface
|
||||
private InMemoryStateProvider()
|
||||
{
|
||||
this.map = new HashMap<>();
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
// Start a single thread executor to handle the cleaning //
|
||||
///////////////////////////////////////////////////////////
|
||||
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
|
||||
executorService.scheduleAtFixedRate(new InMemoryStateProvider.InMemoryStateProviderCleanJob(), jobInitialDelay, jobPeriodSeconds, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Runnable that gets scheduled to periodically clean the InMemoryStateProvider
|
||||
*******************************************************************************/
|
||||
private static class InMemoryStateProviderCleanJob implements Runnable
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(InMemoryStateProvider.InMemoryStateProviderCleanJob.class);
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** run
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
Instant cleanTime = Instant.now().minus(cleanHours, ChronoUnit.HOURS);
|
||||
getInstance().clean(cleanTime);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error cleaning InMemoryStateProvider entries.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -101,4 +149,24 @@ public class InMemoryStateProvider implements StateProviderInterface
|
||||
map.remove(key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Clean entries that started before the given Instant
|
||||
*
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void clean(Instant cleanBeforeInstant)
|
||||
{
|
||||
long jobStartTime = System.currentTimeMillis();
|
||||
Integer beforeSize = map.size();
|
||||
LOG.info("Starting clean for InMemoryStateProvider.", logPair("beforeSize", beforeSize));
|
||||
|
||||
map.entrySet().removeIf(e -> e.getKey().getStartTime().isBefore(cleanBeforeInstant));
|
||||
|
||||
Integer afterSize = map.size();
|
||||
long endTime = System.currentTimeMillis();
|
||||
LOG.info("Completed clean for InMemoryStateProvider.", logPair("beforeSize", beforeSize), logPair("afterSize", afterSize), logPair("amountCleaned", (beforeSize - afterSize)), logPair("runTimeMillis", (endTime - jobStartTime)));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,6 +22,9 @@
|
||||
package com.kingsrook.qqq.backend.core.state;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -93,4 +96,17 @@ public class SimpleStateKey<T> extends AbstractStateKey
|
||||
{
|
||||
return key.hashCode();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for startTime
|
||||
*******************************************************************************/
|
||||
public Instant getStartTime()
|
||||
{
|
||||
//////////////////////////////////////////
|
||||
// For now these will never get cleaned //
|
||||
//////////////////////////////////////////
|
||||
return (Instant.now());
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.state;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
@ -58,4 +59,8 @@ public interface StateProviderInterface
|
||||
*******************************************************************************/
|
||||
void remove(AbstractStateKey key);
|
||||
|
||||
/*******************************************************************************
|
||||
** Clean entries that started before the given Instant
|
||||
*******************************************************************************/
|
||||
void clean(Instant startTime);
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.nio.file.NoSuchFileException;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
@ -127,6 +128,19 @@ public class TempFileStateProvider implements StateProviderInterface
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Clean entries that started before the given Instant
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void clean(Instant startTime)
|
||||
{
|
||||
////////////////////////////////
|
||||
// Not supported at this time //
|
||||
////////////////////////////////
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Get the file referenced by a key
|
||||
*******************************************************************************/
|
||||
|
@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.state;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.Instant;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
@ -34,6 +35,7 @@ public class UUIDAndTypeStateKey extends AbstractStateKey implements Serializabl
|
||||
{
|
||||
private final UUID uuid;
|
||||
private final StateType stateType;
|
||||
private final Instant startTime;
|
||||
|
||||
|
||||
|
||||
@ -43,7 +45,7 @@ public class UUIDAndTypeStateKey extends AbstractStateKey implements Serializabl
|
||||
*******************************************************************************/
|
||||
public UUIDAndTypeStateKey(StateType stateType)
|
||||
{
|
||||
this(UUID.randomUUID(), stateType);
|
||||
this(UUID.randomUUID(), stateType, Instant.now());
|
||||
}
|
||||
|
||||
|
||||
@ -53,9 +55,21 @@ public class UUIDAndTypeStateKey extends AbstractStateKey implements Serializabl
|
||||
**
|
||||
*******************************************************************************/
|
||||
public UUIDAndTypeStateKey(UUID uuid, StateType stateType)
|
||||
{
|
||||
this(uuid, stateType, Instant.now());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor where user can supply the UUID.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public UUIDAndTypeStateKey(UUID uuid, StateType stateType, Instant startTime)
|
||||
{
|
||||
this.uuid = uuid;
|
||||
this.stateType = stateType;
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
|
||||
@ -133,4 +147,15 @@ public class UUIDAndTypeStateKey extends AbstractStateKey implements Serializabl
|
||||
{
|
||||
return "{uuid=" + uuid + ", stateType=" + stateType + '}';
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for startTime
|
||||
*******************************************************************************/
|
||||
public Instant getStartTime()
|
||||
{
|
||||
return (this.startTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -31,6 +31,8 @@
|
||||
</Logger>
|
||||
<Logger name="org.quartz" level="INFO">
|
||||
</Logger>
|
||||
<Logger name="liquibase" level="INFO">
|
||||
</Logger>
|
||||
<Root level="all">
|
||||
<AppenderRef ref="SystemOutAppender"/>
|
||||
<AppenderRef ref="SyslogAppender"/>
|
||||
|
Reference in New Issue
Block a user