mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 21:20:45 +00:00
Initial passable version of possible values
This commit is contained in:
@ -215,7 +215,7 @@ public class RunBackendStepAction
|
|||||||
Object codeObject = codeClass.getConstructor().newInstance();
|
Object codeObject = codeClass.getConstructor().newInstance();
|
||||||
if(!(codeObject instanceof BackendStep backendStepCodeObject))
|
if(!(codeObject instanceof BackendStep backendStepCodeObject))
|
||||||
{
|
{
|
||||||
throw (new QException("The supplied code [" + codeClass.getName() + "] is not an instance of FunctionBody"));
|
throw (new QException("The supplied code [" + codeClass.getName() + "] is not an instance of BackendStep"));
|
||||||
}
|
}
|
||||||
|
|
||||||
backendStepCodeObject.run(runBackendStepInput, runBackendStepOutput);
|
backendStepCodeObject.run(runBackendStepInput, runBackendStepOutput);
|
||||||
|
@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.actions.tables;
|
|||||||
|
|
||||||
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.values.QPossibleValueTranslator;
|
||||||
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
import com.kingsrook.qqq.backend.core.actions.values.QValueFormatter;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||||
@ -52,7 +53,17 @@ public class QueryAction
|
|||||||
|
|
||||||
if(queryInput.getRecordPipe() == null)
|
if(queryInput.getRecordPipe() == null)
|
||||||
{
|
{
|
||||||
QValueFormatter.setDisplayValuesInRecords(queryInput.getTable(), queryOutput.getRecords());
|
if(queryInput.getShouldGenerateDisplayValues())
|
||||||
|
{
|
||||||
|
QValueFormatter qValueFormatter = new QValueFormatter();
|
||||||
|
qValueFormatter.setDisplayValuesInRecords(queryInput.getTable(), queryOutput.getRecords());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(queryInput.getShouldTranslatePossibleValues())
|
||||||
|
{
|
||||||
|
QPossibleValueTranslator qPossibleValueTranslator = new QPossibleValueTranslator(queryInput.getInstance(), queryInput.getSession());
|
||||||
|
qPossibleValueTranslator.translatePossibleValuesInRecords(queryInput.getTable(), queryOutput.getRecords());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return queryOutput;
|
return queryOutput;
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||||
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
|
* contact@kingsrook.com
|
||||||
|
* https://github.com/Kingsrook/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.actions.values;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Interface to be implemented by user-defined code that serves as the backing
|
||||||
|
** for a CUSTOM type possibleValueSource
|
||||||
|
*******************************************************************************/
|
||||||
|
public interface QCustomPossibleValueProvider
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
QPossibleValue<?> getPossibleValue(Serializable idValue);
|
||||||
|
|
||||||
|
// todo - get/search list of possible values
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,359 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||||
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
|
* contact@kingsrook.com
|
||||||
|
* https://github.com/Kingsrook/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.actions.values;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.ListingHash;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Class responsible for looking up possible-values for fields/records and
|
||||||
|
** make them into display values.
|
||||||
|
*******************************************************************************/
|
||||||
|
public class QPossibleValueTranslator
|
||||||
|
{
|
||||||
|
private static final Logger LOG = LogManager.getLogger(QPossibleValueTranslator.class);
|
||||||
|
|
||||||
|
private final QInstance qInstance;
|
||||||
|
private final QSession session;
|
||||||
|
|
||||||
|
// top-level keys are pvsNames (not table names)
|
||||||
|
// 2nd-level keys are pkey values from the PVS table
|
||||||
|
private Map<String, Map<Serializable, String>> possibleValueCache;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QPossibleValueTranslator(QInstance qInstance, QSession session)
|
||||||
|
{
|
||||||
|
this.qInstance = qInstance;
|
||||||
|
this.session = session;
|
||||||
|
|
||||||
|
this.possibleValueCache = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** For a list of records, translate their possible values (populating their display values)
|
||||||
|
*******************************************************************************/
|
||||||
|
public void translatePossibleValuesInRecords(QTableMetaData table, List<QRecord> records)
|
||||||
|
{
|
||||||
|
if(records == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
primePvsCache(table, records);
|
||||||
|
|
||||||
|
for(QRecord record : records)
|
||||||
|
{
|
||||||
|
for(QFieldMetaData field : table.getFields().values())
|
||||||
|
{
|
||||||
|
if(field.getPossibleValueSourceName() != null)
|
||||||
|
{
|
||||||
|
record.setDisplayValue(field.getName(), translatePossibleValue(field, record.getValue(field.getName())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
String translatePossibleValue(QFieldMetaData field, Serializable value)
|
||||||
|
{
|
||||||
|
QPossibleValueSource possibleValueSource = qInstance.getPossibleValueSource(field.getPossibleValueSourceName());
|
||||||
|
if(possibleValueSource == null)
|
||||||
|
{
|
||||||
|
LOG.error("Missing possible value source named [" + field.getPossibleValueSourceName() + "] when formatting value for field [" + field.getName() + "]");
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo - memoize!!!
|
||||||
|
// todo - bulk!!!
|
||||||
|
|
||||||
|
String resultValue = null;
|
||||||
|
if(possibleValueSource.getType().equals(QPossibleValueSourceType.ENUM))
|
||||||
|
{
|
||||||
|
resultValue = translatePossibleValueEnum(value, possibleValueSource);
|
||||||
|
}
|
||||||
|
else if(possibleValueSource.getType().equals(QPossibleValueSourceType.TABLE))
|
||||||
|
{
|
||||||
|
resultValue = translatePossibleValueTable(field, value, possibleValueSource);
|
||||||
|
}
|
||||||
|
else if(possibleValueSource.getType().equals(QPossibleValueSourceType.CUSTOM))
|
||||||
|
{
|
||||||
|
resultValue = translatePossibleValueCustom(field, value, possibleValueSource);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG.error("Unrecognized possibleValueSourceType [" + possibleValueSource.getType() + "] in PVS named [" + possibleValueSource.getName() + "] on field [" + field.getName() + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(resultValue == null)
|
||||||
|
{
|
||||||
|
resultValue = getDefaultForPossibleValue(possibleValueSource, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (resultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private String translatePossibleValueCustom(QFieldMetaData field, Serializable value, QPossibleValueSource possibleValueSource)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Class<?> codeClass = Class.forName(possibleValueSource.getCustomCodeReference().getName());
|
||||||
|
Object codeObject = codeClass.getConstructor().newInstance();
|
||||||
|
if(!(codeObject instanceof QCustomPossibleValueProvider customPossibleValueProvider))
|
||||||
|
{
|
||||||
|
throw (new QException("The supplied code [" + codeClass.getName() + "] is not an instance of QCustomPossibleValueProvider"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (formatPossibleValue(possibleValueSource, customPossibleValueProvider.getPossibleValue(value)));
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
LOG.warn("Error sending [" + value + "] for field [" + field + "] through custom code for PVS [" + field.getPossibleValueSourceName() + "]", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private String translatePossibleValueTable(QFieldMetaData field, Serializable value, QPossibleValueSource possibleValueSource)
|
||||||
|
{
|
||||||
|
/////////////////////////////////
|
||||||
|
// null input gets null output //
|
||||||
|
/////////////////////////////////
|
||||||
|
if(value == null)
|
||||||
|
{
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
// look for cached value - if it's missing, call the primer //
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
possibleValueCache.putIfAbsent(possibleValueSource.getName(), new HashMap<>());
|
||||||
|
Map<Serializable, String> cacheForPvs = possibleValueCache.get(possibleValueSource.getName());
|
||||||
|
if(!cacheForPvs.containsKey(value))
|
||||||
|
{
|
||||||
|
primePvsCache(possibleValueSource.getTableName(), List.of(possibleValueSource), List.of(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (cacheForPvs.get(value));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private String formatPossibleValue(QPossibleValueSource possibleValueSource, QPossibleValue<?> possibleValue)
|
||||||
|
{
|
||||||
|
return (doFormatPossibleValue(possibleValueSource.getValueFormat(), possibleValueSource.getValueFields(), possibleValue.getId(), possibleValue.getLabel()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private String getDefaultForPossibleValue(QPossibleValueSource possibleValueSource, Serializable value)
|
||||||
|
{
|
||||||
|
if(possibleValueSource.getValueFormatIfNotFound() == null)
|
||||||
|
{
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (doFormatPossibleValue(possibleValueSource.getValueFormatIfNotFound(), possibleValueSource.getValueFieldsIfNotFound(), value, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@SuppressWarnings("checkstyle:Indentation")
|
||||||
|
private String doFormatPossibleValue(String formatString, List<String> valueFields, Object id, String label)
|
||||||
|
{
|
||||||
|
List<Object> values = new ArrayList<>();
|
||||||
|
if(valueFields != null)
|
||||||
|
{
|
||||||
|
for(String valueField : valueFields)
|
||||||
|
{
|
||||||
|
Object value = switch(valueField)
|
||||||
|
{
|
||||||
|
case "id" -> id;
|
||||||
|
case "label" -> label;
|
||||||
|
default -> throw new IllegalArgumentException("Unexpected value field: " + valueField);
|
||||||
|
};
|
||||||
|
values.add(Objects.requireNonNullElse(value, ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (formatString.formatted(values.toArray()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private String translatePossibleValueEnum(Serializable value, QPossibleValueSource possibleValueSource)
|
||||||
|
{
|
||||||
|
for(QPossibleValue<?> possibleValue : possibleValueSource.getEnumValues())
|
||||||
|
{
|
||||||
|
if(possibleValue.getId().equals(value))
|
||||||
|
{
|
||||||
|
return (formatPossibleValue(possibleValueSource, possibleValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** prime the cache (e.g., by doing bulk-queries) for table-based PVS's
|
||||||
|
*******************************************************************************/
|
||||||
|
void primePvsCache(QTableMetaData table, List<QRecord> records)
|
||||||
|
{
|
||||||
|
ListingHash<String, QFieldMetaData> fieldsByPvsTable = new ListingHash<>();
|
||||||
|
ListingHash<String, QPossibleValueSource> pvsesByTable = new ListingHash<>();
|
||||||
|
for(QFieldMetaData field : table.getFields().values())
|
||||||
|
{
|
||||||
|
QPossibleValueSource possibleValueSource = qInstance.getPossibleValueSource(field.getPossibleValueSourceName());
|
||||||
|
if(possibleValueSource != null && possibleValueSource.getType().equals(QPossibleValueSourceType.TABLE))
|
||||||
|
{
|
||||||
|
fieldsByPvsTable.add(possibleValueSource.getTableName(), field);
|
||||||
|
pvsesByTable.add(possibleValueSource.getTableName(), possibleValueSource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(String tableName : fieldsByPvsTable.keySet())
|
||||||
|
{
|
||||||
|
Set<Serializable> values = new HashSet<>();
|
||||||
|
for(QRecord record : records)
|
||||||
|
{
|
||||||
|
for(QFieldMetaData field : fieldsByPvsTable.get(tableName))
|
||||||
|
{
|
||||||
|
values.add(record.getValue(field.getName()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
primePvsCache(tableName, pvsesByTable.get(tableName), values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** For a given table, and a list of pkey-values in that table, AND a list of
|
||||||
|
** possible value sources based on that table (maybe usually 1, but could be more,
|
||||||
|
** e.g., if they had different formatting, or different filters (todo, would that work?)
|
||||||
|
** - query for the values in the table, and populate the possibleValueCache.
|
||||||
|
*******************************************************************************/
|
||||||
|
private void primePvsCache(String tableName, List<QPossibleValueSource> possibleValueSources, Collection<Serializable> values)
|
||||||
|
{
|
||||||
|
for(QPossibleValueSource possibleValueSource : possibleValueSources)
|
||||||
|
{
|
||||||
|
possibleValueCache.putIfAbsent(possibleValueSource.getName(), new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
String primaryKeyField = qInstance.getTable(tableName).getPrimaryKeyField();
|
||||||
|
|
||||||
|
for(List<Serializable> page : CollectionUtils.getPages(values, 1000))
|
||||||
|
{
|
||||||
|
QueryInput queryInput = new QueryInput(qInstance);
|
||||||
|
queryInput.setSession(session);
|
||||||
|
queryInput.setTableName(tableName);
|
||||||
|
queryInput.setFilter(new QQueryFilter().withCriteria(new QFilterCriteria(primaryKeyField, QCriteriaOperator.IN, page)));
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// this is needed to get record labels, which are what we use here... unclear if best! //
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
queryInput.setShouldGenerateDisplayValues(true);
|
||||||
|
|
||||||
|
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||||
|
|
||||||
|
for(QRecord record : queryOutput.getRecords())
|
||||||
|
{
|
||||||
|
Serializable pkeyValue = record.getValue(primaryKeyField);
|
||||||
|
for(QPossibleValueSource possibleValueSource : possibleValueSources)
|
||||||
|
{
|
||||||
|
QPossibleValue<?> possibleValue = new QPossibleValue<>(pkeyValue, record.getRecordLabel());
|
||||||
|
possibleValueCache.get(possibleValueSource.getName()).put(pkeyValue, formatPossibleValue(possibleValueSource, possibleValue));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
LOG.warn("Error looking up possible values for table [" + tableName + "]", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -25,8 +25,10 @@ package com.kingsrook.qqq.backend.core.actions.values;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
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.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.model.session.QSession;
|
||||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
@ -34,7 +36,8 @@ import org.apache.logging.log4j.Logger;
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Utility to apply display formats to values for fields
|
** Utility to apply display formats to values for records and fields.
|
||||||
|
** Note that this includes handling PossibleValues.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class QValueFormatter
|
public class QValueFormatter
|
||||||
{
|
{
|
||||||
@ -45,7 +48,16 @@ public class QValueFormatter
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static String formatValue(QFieldMetaData field, Serializable value)
|
public QValueFormatter()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String formatValue(QFieldMetaData field, Serializable value)
|
||||||
{
|
{
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
// null values get null results //
|
// null values get null results //
|
||||||
@ -55,6 +67,16 @@ public class QValueFormatter
|
|||||||
return (null);
|
return (null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// todo - is this appropriate, with this class and possibleValueTransaltor being decoupled - to still do standard formatting here?
|
||||||
|
// alternatively, shold we return null here?
|
||||||
|
// ///////////////////////////////////////////////
|
||||||
|
// // if the field has a possible value, use it //
|
||||||
|
// ///////////////////////////////////////////////
|
||||||
|
// if(field.getPossibleValueSourceName() != null)
|
||||||
|
// {
|
||||||
|
// return (this.possibleValueTranslator.translatePossibleValue(field, value));
|
||||||
|
// }
|
||||||
|
|
||||||
////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////
|
||||||
// if the field has a display format, try to apply it //
|
// if the field has a display format, try to apply it //
|
||||||
////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////
|
||||||
@ -68,6 +90,7 @@ public class QValueFormatter
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// todo - revisit if we actually want this - or - if you should get an error if you mis-configure your table this way (ideally during validation!)
|
||||||
if(e.getMessage().equals("f != java.lang.Integer"))
|
if(e.getMessage().equals("f != java.lang.Integer"))
|
||||||
{
|
{
|
||||||
return formatValue(field, ValueUtils.getValueAsBigDecimal(value));
|
return formatValue(field, ValueUtils.getValueAsBigDecimal(value));
|
||||||
@ -99,7 +122,7 @@ public class QValueFormatter
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Make a string from a table's recordLabelFormat and fields, for a given record.
|
** Make a string from a table's recordLabelFormat and fields, for a given record.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public static String formatRecordLabel(QTableMetaData table, QRecord record)
|
public String formatRecordLabel(QTableMetaData table, QRecord record)
|
||||||
{
|
{
|
||||||
if(!StringUtils.hasContent(table.getRecordLabelFormat()))
|
if(!StringUtils.hasContent(table.getRecordLabelFormat()))
|
||||||
{
|
{
|
||||||
@ -128,7 +151,7 @@ public class QValueFormatter
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Deal with non-happy-path cases for making a record label.
|
** Deal with non-happy-path cases for making a record label.
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private static String formatRecordLabelExceptionalCases(QTableMetaData table, QRecord record)
|
private String formatRecordLabelExceptionalCases(QTableMetaData table, QRecord record)
|
||||||
{
|
{
|
||||||
///////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////
|
||||||
// if there's no record label format, then just return the primary key display value //
|
// if there's no record label format, then just return the primary key display value //
|
||||||
@ -156,7 +179,7 @@ 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(QTableMetaData table, List<QRecord> records)
|
public void setDisplayValuesInRecords(QTableMetaData table, List<QRecord> records)
|
||||||
{
|
{
|
||||||
if(records == null)
|
if(records == null)
|
||||||
{
|
{
|
||||||
@ -167,12 +190,13 @@ public class QValueFormatter
|
|||||||
{
|
{
|
||||||
for(QFieldMetaData field : table.getFields().values())
|
for(QFieldMetaData field : table.getFields().values())
|
||||||
{
|
{
|
||||||
String formattedValue = QValueFormatter.formatValue(field, record.getValue(field.getName()));
|
String formattedValue = formatValue(field, record.getValue(field.getName()));
|
||||||
record.setDisplayValue(field.getName(), formattedValue);
|
record.setDisplayValue(field.getName(), formattedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
record.setRecordLabel(QValueFormatter.formatRecordLabel(table, record));
|
record.setRecordLabel(formatRecordLabel(table, record));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -95,6 +95,15 @@ public class CsvToQRecordAdapter
|
|||||||
throw (new IllegalArgumentException("Empty csv value was provided."));
|
throw (new IllegalArgumentException("Empty csv value was provided."));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// once, from a DOS csv file (that had come from Excel), we had a "" character (FEFF, Byte-order marker) at the start of a //
|
||||||
|
// CSV, which caused our first header to not match... So, let us strip away any FEFF or FFFE's at the start of CSV strings. //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
if(csv.length() > 1 && (csv.charAt(0) == 0xfeff || csv.charAt(0) == 0xfffe))
|
||||||
|
{
|
||||||
|
csv = csv.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -118,7 +127,7 @@ public class CsvToQRecordAdapter
|
|||||||
// put values from the CSV record into a map of header -> value //
|
// put values from the CSV record into a map of header -> value //
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
Map<String, String> csvValues = new HashMap<>();
|
Map<String, String> csvValues = new HashMap<>();
|
||||||
for(int i = 0; i < headers.size(); i++)
|
for(int i = 0; i < headers.size() && i < csvRecord.size(); i++)
|
||||||
{
|
{
|
||||||
csvValues.put(headers.get(i), csvRecord.get(i));
|
csvValues.put(headers.get(i), csvRecord.get(i));
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.instances;
|
|||||||
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@ -128,6 +129,11 @@ public class QInstanceEnricher
|
|||||||
{
|
{
|
||||||
generateTableFieldSections(table);
|
generateTableFieldSections(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(CollectionUtils.nullSafeHasContents(table.getRecordLabelFields()) && !StringUtils.hasContent(table.getRecordLabelFormat()))
|
||||||
|
{
|
||||||
|
table.setRecordLabelFormat(String.join(" ", Collections.nCopies(table.getRecordLabelFields().size(), "%s")));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -211,7 +217,7 @@ public class QInstanceEnricher
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private String nameToLabel(String name)
|
static String nameToLabel(String name)
|
||||||
{
|
{
|
||||||
if(!StringUtils.hasContent(name))
|
if(!StringUtils.hasContent(name))
|
||||||
{
|
{
|
||||||
@ -223,7 +229,7 @@ public class QInstanceEnricher
|
|||||||
return (name.substring(0, 1).toUpperCase(Locale.ROOT));
|
return (name.substring(0, 1).toUpperCase(Locale.ROOT));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (name.substring(0, 1).toUpperCase(Locale.ROOT) + name.substring(1).replaceAll("([A-Z])", " $1"));
|
return (name.substring(0, 1).toUpperCase(Locale.ROOT) + name.substring(1).replaceAll("([A-Z0-9]+)", " $1").replaceAll("([0-9])([A-Za-z])", "$1 $2"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -579,7 +585,7 @@ public class QInstanceEnricher
|
|||||||
{
|
{
|
||||||
for(String fieldName : table.getRecordLabelFields())
|
for(String fieldName : table.getRecordLabelFields())
|
||||||
{
|
{
|
||||||
if(!usedFieldNames.contains(fieldName))
|
if(!usedFieldNames.contains(fieldName) && table.getFields().containsKey(fieldName))
|
||||||
{
|
{
|
||||||
identitySection.getFieldNames().add(fieldName);
|
identitySection.getFieldNames().add(fieldName);
|
||||||
usedFieldNames.add(fieldName);
|
usedFieldNames.add(fieldName);
|
||||||
|
@ -29,6 +29,7 @@ import java.util.Objects;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
||||||
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.code.QCodeUsage;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QStepMetaData;
|
||||||
@ -88,6 +89,7 @@ public class QInstanceValidator
|
|||||||
validateTables(qInstance, errors);
|
validateTables(qInstance, errors);
|
||||||
validateProcesses(qInstance, errors);
|
validateProcesses(qInstance, errors);
|
||||||
validateApps(qInstance, errors);
|
validateApps(qInstance, errors);
|
||||||
|
validatePossibleValueSources(qInstance, errors);
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
@ -190,6 +192,16 @@ public class QInstanceValidator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////
|
||||||
|
// validate the record label //
|
||||||
|
///////////////////////////////
|
||||||
|
if(table.getRecordLabelFields() != null)
|
||||||
|
{
|
||||||
|
for(String recordLabelField : table.getRecordLabelFields())
|
||||||
|
{
|
||||||
|
assertCondition(errors, table.getFields().containsKey(recordLabelField), "Table " + tableName + " record label field " + recordLabelField + " is not a field on this table.");
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -225,7 +237,7 @@ public class QInstanceValidator
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private void validateProcesses(QInstance qInstance, List<String> errors)
|
private void validateProcesses(QInstance qInstance, List<String> errors)
|
||||||
{
|
{
|
||||||
if(!CollectionUtils.nullSafeIsEmpty(qInstance.getProcesses()))
|
if(CollectionUtils.nullSafeHasContents(qInstance.getProcesses()))
|
||||||
{
|
{
|
||||||
qInstance.getProcesses().forEach((processName, process) ->
|
qInstance.getProcesses().forEach((processName, process) ->
|
||||||
{
|
{
|
||||||
@ -264,7 +276,7 @@ public class QInstanceValidator
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private void validateApps(QInstance qInstance, List<String> errors)
|
private void validateApps(QInstance qInstance, List<String> errors)
|
||||||
{
|
{
|
||||||
if(!CollectionUtils.nullSafeIsEmpty(qInstance.getApps()))
|
if(CollectionUtils.nullSafeHasContents(qInstance.getApps()))
|
||||||
{
|
{
|
||||||
qInstance.getApps().forEach((appName, app) ->
|
qInstance.getApps().forEach((appName, app) ->
|
||||||
{
|
{
|
||||||
@ -291,6 +303,61 @@ public class QInstanceValidator
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private void validatePossibleValueSources(QInstance qInstance, List<String> errors)
|
||||||
|
{
|
||||||
|
if(CollectionUtils.nullSafeHasContents(qInstance.getPossibleValueSources()))
|
||||||
|
{
|
||||||
|
qInstance.getPossibleValueSources().forEach((pvsName, possibleValueSource) ->
|
||||||
|
{
|
||||||
|
assertCondition(errors, Objects.equals(pvsName, possibleValueSource.getName()), "Inconsistent naming for possibleValueSource: " + pvsName + "/" + possibleValueSource.getName() + ".");
|
||||||
|
assertCondition(errors, possibleValueSource.getIdType() != null, "Missing an idType for possibleValueSource: " + pvsName);
|
||||||
|
if(assertCondition(errors, possibleValueSource.getType() != null, "Missing type for possibleValueSource: " + pvsName))
|
||||||
|
{
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// assert about fields that should and should not be set, based on possible value source type //
|
||||||
|
// do additional type-specific validations as well //
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
switch(possibleValueSource.getType())
|
||||||
|
{
|
||||||
|
case ENUM ->
|
||||||
|
{
|
||||||
|
assertCondition(errors, !StringUtils.hasContent(possibleValueSource.getTableName()), "enum-type possibleValueSource " + pvsName + " should not have a tableName.");
|
||||||
|
assertCondition(errors, possibleValueSource.getCustomCodeReference() == null, "enum-type possibleValueSource " + pvsName + " should not have a customCodeReference.");
|
||||||
|
|
||||||
|
assertCondition(errors, CollectionUtils.nullSafeHasContents(possibleValueSource.getEnumValues()), "enum-type possibleValueSource " + pvsName + " is missing enum values");
|
||||||
|
}
|
||||||
|
case TABLE ->
|
||||||
|
{
|
||||||
|
assertCondition(errors, CollectionUtils.nullSafeIsEmpty(possibleValueSource.getEnumValues()), "table-type possibleValueSource " + pvsName + " should not have enum values.");
|
||||||
|
assertCondition(errors, possibleValueSource.getCustomCodeReference() == null, "table-type possibleValueSource " + pvsName + " should not have a customCodeReference.");
|
||||||
|
|
||||||
|
if(assertCondition(errors, StringUtils.hasContent(possibleValueSource.getTableName()), "table-type possibleValueSource " + pvsName + " is missing a tableName."))
|
||||||
|
{
|
||||||
|
assertCondition(errors, qInstance.getTable(possibleValueSource.getTableName()) != null, "Unrecognized table " + possibleValueSource.getTableName() + " for possibleValueSource " + pvsName + ".");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case CUSTOM ->
|
||||||
|
{
|
||||||
|
assertCondition(errors, CollectionUtils.nullSafeIsEmpty(possibleValueSource.getEnumValues()), "custom-type possibleValueSource " + pvsName + " should not have enum values.");
|
||||||
|
assertCondition(errors, !StringUtils.hasContent(possibleValueSource.getTableName()), "custom-type possibleValueSource " + pvsName + " should not have a tableName.");
|
||||||
|
|
||||||
|
if(assertCondition(errors, possibleValueSource.getCustomCodeReference() != null, "custom-type possibleValueSource " + pvsName + " is missing a customCodeReference."))
|
||||||
|
{
|
||||||
|
assertCondition(errors, QCodeUsage.POSSIBLE_VALUE_PROVIDER.equals(possibleValueSource.getCustomCodeReference().getCodeUsage()), "customCodeReference for possibleValueSource " + pvsName + " is not a possibleValueProvider.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default -> errors.add("Unexpected possibleValueSource type: " + possibleValueSource.getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Check if an app's child list can recursively be traversed without finding a
|
** Check if an app's child list can recursively be traversed without finding a
|
||||||
** duplicate, which would indicate a cycle (e.g., an error)
|
** duplicate, which would indicate a cycle (e.g., an error)
|
||||||
|
@ -40,6 +40,8 @@ public class QueryInput extends AbstractTableActionInput
|
|||||||
|
|
||||||
private RecordPipe recordPipe;
|
private RecordPipe recordPipe;
|
||||||
|
|
||||||
|
private boolean shouldTranslatePossibleValues = false;
|
||||||
|
private boolean shouldGenerateDisplayValues = false;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -158,4 +160,47 @@ public class QueryInput extends AbstractTableActionInput
|
|||||||
this.recordPipe = recordPipe;
|
this.recordPipe = recordPipe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for shouldTranslatePossibleValues
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public boolean getShouldTranslatePossibleValues()
|
||||||
|
{
|
||||||
|
return shouldTranslatePossibleValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for shouldTranslatePossibleValues
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setShouldTranslatePossibleValues(boolean shouldTranslatePossibleValues)
|
||||||
|
{
|
||||||
|
this.shouldTranslatePossibleValues = shouldTranslatePossibleValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for shouldGenerateDisplayValues
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public boolean getShouldGenerateDisplayValues()
|
||||||
|
{
|
||||||
|
return shouldGenerateDisplayValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for shouldGenerateDisplayValues
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setShouldGenerateDisplayValues(boolean shouldGenerateDisplayValues)
|
||||||
|
{
|
||||||
|
this.shouldGenerateDisplayValues = shouldGenerateDisplayValues;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,6 +62,11 @@ public @interface QField
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
String displayFormat() default "";
|
String displayFormat() default "";
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
String possibleValueSourceName() default "";
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// new attributes here likely need implementation in QFieldMetaData.constructFromGetter //
|
// new attributes here likely need implementation in QFieldMetaData.constructFromGetter //
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -55,7 +55,7 @@ public class QInstance
|
|||||||
// Important to use LinkedHashmap here, to preserve the order in which entries are added. //
|
// Important to use LinkedHashmap here, to preserve the order in which entries are added. //
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
private Map<String, QTableMetaData> tables = new LinkedHashMap<>();
|
private Map<String, QTableMetaData> tables = new LinkedHashMap<>();
|
||||||
private Map<String, QPossibleValueSource<?>> possibleValueSources = new LinkedHashMap<>();
|
private Map<String, QPossibleValueSource> possibleValueSources = new LinkedHashMap<>();
|
||||||
private Map<String, QProcessMetaData> processes = new LinkedHashMap<>();
|
private Map<String, QProcessMetaData> processes = new LinkedHashMap<>();
|
||||||
private Map<String, QAppMetaData> apps = new LinkedHashMap<>();
|
private Map<String, QAppMetaData> apps = new LinkedHashMap<>();
|
||||||
|
|
||||||
@ -190,7 +190,7 @@ public class QInstance
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void addPossibleValueSource(QPossibleValueSource<?> possibleValueSource)
|
public void addPossibleValueSource(QPossibleValueSource possibleValueSource)
|
||||||
{
|
{
|
||||||
this.addPossibleValueSource(possibleValueSource.getName(), possibleValueSource);
|
this.addPossibleValueSource(possibleValueSource.getName(), possibleValueSource);
|
||||||
}
|
}
|
||||||
@ -353,7 +353,7 @@ public class QInstance
|
|||||||
** Getter for possibleValueSources
|
** Getter for possibleValueSources
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public Map<String, QPossibleValueSource<?>> getPossibleValueSources()
|
public Map<String, QPossibleValueSource> getPossibleValueSources()
|
||||||
{
|
{
|
||||||
return possibleValueSources;
|
return possibleValueSources;
|
||||||
}
|
}
|
||||||
@ -364,7 +364,7 @@ public class QInstance
|
|||||||
** Setter for possibleValueSources
|
** Setter for possibleValueSources
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void setPossibleValueSources(Map<String, QPossibleValueSource<?>> possibleValueSources)
|
public void setPossibleValueSources(Map<String, QPossibleValueSource> possibleValueSources)
|
||||||
{
|
{
|
||||||
this.possibleValueSources = possibleValueSources;
|
this.possibleValueSources = possibleValueSources;
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.code;
|
|||||||
|
|
||||||
|
|
||||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.values.QCustomPossibleValueProvider;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -70,6 +71,10 @@ public class QCodeReference
|
|||||||
{
|
{
|
||||||
this.codeUsage = QCodeUsage.BACKEND_STEP;
|
this.codeUsage = QCodeUsage.BACKEND_STEP;
|
||||||
}
|
}
|
||||||
|
else if(QCustomPossibleValueProvider.class.isAssignableFrom(javaClass))
|
||||||
|
{
|
||||||
|
this.codeUsage = QCodeUsage.POSSIBLE_VALUE_PROVIDER;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw (new IllegalStateException("Unable to infer code usage type for class: " + javaClass.getName()));
|
throw (new IllegalStateException("Unable to infer code usage type for class: " + javaClass.getName()));
|
||||||
|
@ -29,5 +29,6 @@ package com.kingsrook.qqq.backend.core.model.metadata.code;
|
|||||||
public enum QCodeUsage
|
public enum QCodeUsage
|
||||||
{
|
{
|
||||||
BACKEND_STEP, // a backend-step in a process
|
BACKEND_STEP, // a backend-step in a process
|
||||||
CUSTOMIZER // a function to customize part of a QQQ table's behavior
|
CUSTOMIZER, // a function to customize part of a QQQ table's behavior
|
||||||
|
POSSIBLE_VALUE_PROVIDER // code that drives a custom possibleValueSource
|
||||||
}
|
}
|
||||||
|
@ -133,6 +133,11 @@ public class QFieldMetaData
|
|||||||
{
|
{
|
||||||
setDisplayFormat(fieldAnnotation.displayFormat());
|
setDisplayFormat(fieldAnnotation.displayFormat());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(StringUtils.hasContent(fieldAnnotation.possibleValueSourceName()))
|
||||||
|
{
|
||||||
|
setPossibleValueSourceName(fieldAnnotation.possibleValueSourceName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(QException qe)
|
catch(QException qe)
|
||||||
@ -406,6 +411,7 @@ public class QFieldMetaData
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for displayFormat
|
** Getter for displayFormat
|
||||||
**
|
**
|
||||||
@ -427,6 +433,7 @@ public class QFieldMetaData
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent setter for displayFormat
|
** Fluent setter for displayFormat
|
||||||
**
|
**
|
||||||
|
@ -41,6 +41,8 @@ public class QFrontendFieldMetaData
|
|||||||
private QFieldType type;
|
private QFieldType type;
|
||||||
private boolean isRequired;
|
private boolean isRequired;
|
||||||
private boolean isEditable;
|
private boolean isEditable;
|
||||||
|
private String possibleValueSourceName;
|
||||||
|
private String displayFormat;
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////
|
||||||
// do not add setters. take values from the source-object in the constructor!! //
|
// do not add setters. take values from the source-object in the constructor!! //
|
||||||
@ -58,6 +60,8 @@ public class QFrontendFieldMetaData
|
|||||||
this.type = fieldMetaData.getType();
|
this.type = fieldMetaData.getType();
|
||||||
this.isRequired = fieldMetaData.getIsRequired();
|
this.isRequired = fieldMetaData.getIsRequired();
|
||||||
this.isEditable = fieldMetaData.getIsEditable();
|
this.isEditable = fieldMetaData.getIsEditable();
|
||||||
|
this.possibleValueSourceName = fieldMetaData.getPossibleValueSourceName();
|
||||||
|
this.displayFormat = fieldMetaData.getDisplayFormat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -115,4 +119,26 @@ public class QFrontendFieldMetaData
|
|||||||
return isEditable;
|
return isEditable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for displayFormat
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getDisplayFormat()
|
||||||
|
{
|
||||||
|
return displayFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for possibleValueSourceName
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getPossibleValueSourceName()
|
||||||
|
{
|
||||||
|
return possibleValueSourceName;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||||
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
|
* contact@kingsrook.com
|
||||||
|
* https://github.com/Kingsrook/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.model.metadata.possiblevalues;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public interface PossibleValueEnum<T>
|
||||||
|
{
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
T getPossibleValueId();
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
String getPossibleValueLabel();
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||||
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
|
* contact@kingsrook.com
|
||||||
|
* https://github.com/Kingsrook/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.model.metadata.possiblevalues;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** An actual possible value - an id and label.
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class QPossibleValue<T>
|
||||||
|
{
|
||||||
|
private final T id;
|
||||||
|
private final String label;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public QPossibleValue(String value)
|
||||||
|
{
|
||||||
|
this.id = (T) value;
|
||||||
|
this.label = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QPossibleValue(T id, String label)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
this.label = label;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for id
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public T getId()
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for label
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getLabel()
|
||||||
|
{
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
}
|
@ -24,19 +24,62 @@ package com.kingsrook.qqq.backend.core.model.metadata.possiblevalues;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Meta-data to represent a single field in a table.
|
** Meta-data to represent a single field in a table.
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public class QPossibleValueSource<T>
|
public class QPossibleValueSource
|
||||||
{
|
{
|
||||||
private String name;
|
private String name;
|
||||||
private QPossibleValueSourceType type;
|
private QPossibleValueSourceType type;
|
||||||
|
private QFieldType idType = QFieldType.INTEGER;
|
||||||
|
|
||||||
// should these be in sub-types??
|
private String valueFormat = ValueFormat.DEFAULT;
|
||||||
private List<T> enumValues;
|
private List<String> valueFields = ValueFields.DEFAULT;
|
||||||
|
private String valueFormatIfNotFound = null;
|
||||||
|
private List<String> valueFieldsIfNotFound = null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public interface ValueFormat
|
||||||
|
{
|
||||||
|
String DEFAULT = "%s";
|
||||||
|
String LABEL_ONLY = "%s";
|
||||||
|
String LABEL_PARENS_ID = "%s (%s)";
|
||||||
|
String ID_COLON_LABEL = "%s: %s";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public interface ValueFields
|
||||||
|
{
|
||||||
|
List<String> DEFAULT = List.of("label");
|
||||||
|
List<String> LABEL_ONLY = List.of("label");
|
||||||
|
List<String> LABEL_PARENS_ID = List.of("label", "id");
|
||||||
|
List<String> ID_COLON_LABEL = List.of("id", "label");
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo - optimization hints, such as "table is static, fully cache" or "table is small, so we can pull the whole thing into memory?"
|
||||||
|
|
||||||
|
//////////////////////
|
||||||
|
// for type = TABLE //
|
||||||
|
//////////////////////
|
||||||
|
private String tableName;
|
||||||
|
// todo - override labelFormat & labelFields?
|
||||||
|
|
||||||
|
/////////////////////
|
||||||
|
// for type = ENUM //
|
||||||
|
/////////////////////
|
||||||
|
private List<QPossibleValue<?>> enumValues;
|
||||||
|
|
||||||
|
///////////////////////
|
||||||
|
// for type = CUSTOM //
|
||||||
|
///////////////////////
|
||||||
|
private QCodeReference customCodeReference;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -72,7 +115,7 @@ public class QPossibleValueSource<T>
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QPossibleValueSource<T> withName(String name)
|
public QPossibleValueSource withName(String name)
|
||||||
{
|
{
|
||||||
this.name = name;
|
this.name = name;
|
||||||
return (this);
|
return (this);
|
||||||
@ -103,7 +146,7 @@ public class QPossibleValueSource<T>
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QPossibleValueSource<T> withType(QPossibleValueSourceType type)
|
public QPossibleValueSource withType(QPossibleValueSourceType type)
|
||||||
{
|
{
|
||||||
this.type = type;
|
this.type = type;
|
||||||
return (this);
|
return (this);
|
||||||
@ -111,11 +154,215 @@ public class QPossibleValueSource<T>
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for idType
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QFieldType getIdType()
|
||||||
|
{
|
||||||
|
return idType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for idType
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setIdType(QFieldType idType)
|
||||||
|
{
|
||||||
|
this.idType = idType;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for idType
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QPossibleValueSource withIdType(QFieldType idType)
|
||||||
|
{
|
||||||
|
this.idType = idType;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for valueFormat
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getValueFormat()
|
||||||
|
{
|
||||||
|
return valueFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for valueFormat
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setValueFormat(String valueFormat)
|
||||||
|
{
|
||||||
|
this.valueFormat = valueFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for valueFormat
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QPossibleValueSource withValueFormat(String valueFormat)
|
||||||
|
{
|
||||||
|
this.valueFormat = valueFormat;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for valueFields
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public List<String> getValueFields()
|
||||||
|
{
|
||||||
|
return valueFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for valueFields
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setValueFields(List<String> valueFields)
|
||||||
|
{
|
||||||
|
this.valueFields = valueFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for valueFields
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QPossibleValueSource withValueFields(List<String> valueFields)
|
||||||
|
{
|
||||||
|
this.valueFields = valueFields;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for valueFormatIfNotFound
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getValueFormatIfNotFound()
|
||||||
|
{
|
||||||
|
return valueFormatIfNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for valueFormatIfNotFound
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setValueFormatIfNotFound(String valueFormatIfNotFound)
|
||||||
|
{
|
||||||
|
this.valueFormatIfNotFound = valueFormatIfNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for valueFormatIfNotFound
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QPossibleValueSource withValueFormatIfNotFound(String valueFormatIfNotFound)
|
||||||
|
{
|
||||||
|
this.valueFormatIfNotFound = valueFormatIfNotFound;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for valueFieldsIfNotFound
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public List<String> getValueFieldsIfNotFound()
|
||||||
|
{
|
||||||
|
return valueFieldsIfNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for valueFieldsIfNotFound
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setValueFieldsIfNotFound(List<String> valueFieldsIfNotFound)
|
||||||
|
{
|
||||||
|
this.valueFieldsIfNotFound = valueFieldsIfNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for valueFieldsIfNotFound
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QPossibleValueSource withValueFieldsIfNotFound(List<String> valueFieldsIfNotFound)
|
||||||
|
{
|
||||||
|
this.valueFieldsIfNotFound = valueFieldsIfNotFound;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for tableName
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public String getTableName()
|
||||||
|
{
|
||||||
|
return tableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for tableName
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setTableName(String tableName)
|
||||||
|
{
|
||||||
|
this.tableName = tableName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for tableName
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QPossibleValueSource withTableName(String tableName)
|
||||||
|
{
|
||||||
|
this.tableName = tableName;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for enumValues
|
** Getter for enumValues
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public List<T> getEnumValues()
|
public List<QPossibleValue<?>> getEnumValues()
|
||||||
{
|
{
|
||||||
return enumValues;
|
return enumValues;
|
||||||
}
|
}
|
||||||
@ -126,7 +373,7 @@ public class QPossibleValueSource<T>
|
|||||||
** Setter for enumValues
|
** Setter for enumValues
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public void setEnumValues(List<T> enumValues)
|
public void setEnumValues(List<QPossibleValue<?>> enumValues)
|
||||||
{
|
{
|
||||||
this.enumValues = enumValues;
|
this.enumValues = enumValues;
|
||||||
}
|
}
|
||||||
@ -137,7 +384,7 @@ public class QPossibleValueSource<T>
|
|||||||
** Fluent setter for enumValues
|
** Fluent setter for enumValues
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QPossibleValueSource<T> withEnumValues(List<T> enumValues)
|
public QPossibleValueSource withEnumValues(List<QPossibleValue<?>> enumValues)
|
||||||
{
|
{
|
||||||
this.enumValues = enumValues;
|
this.enumValues = enumValues;
|
||||||
return this;
|
return this;
|
||||||
@ -146,16 +393,64 @@ public class QPossibleValueSource<T>
|
|||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Fluent adder for enumValues
|
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public QPossibleValueSource<T> addEnumValue(T enumValue)
|
public void addEnumValue(QPossibleValue<?> possibleValue)
|
||||||
{
|
{
|
||||||
if(this.enumValues == null)
|
if(this.enumValues == null)
|
||||||
{
|
{
|
||||||
this.enumValues = new ArrayList<>();
|
this.enumValues = new ArrayList<>();
|
||||||
}
|
}
|
||||||
this.enumValues.add(enumValue);
|
this.enumValues.add(possibleValue);
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public <T extends PossibleValueEnum<?>> QPossibleValueSource withValuesFromEnum(T[] values)
|
||||||
|
{
|
||||||
|
for(T t : values)
|
||||||
|
{
|
||||||
|
addEnumValue(new QPossibleValue<>(t.getPossibleValueId(), t.getPossibleValueLabel()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for customCodeReference
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QCodeReference getCustomCodeReference()
|
||||||
|
{
|
||||||
|
return customCodeReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for customCodeReference
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public void setCustomCodeReference(QCodeReference customCodeReference)
|
||||||
|
{
|
||||||
|
this.customCodeReference = customCodeReference;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for customCodeReference
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QPossibleValueSource withCustomCodeReference(QCodeReference customCodeReference)
|
||||||
|
{
|
||||||
|
this.customCodeReference = customCodeReference;
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.tables;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -587,6 +588,18 @@ public class QTableMetaData implements QAppChildMetaData, Serializable
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fluent setter for recordLabelFields
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public QTableMetaData withRecordLabelFields(String... recordLabelFields)
|
||||||
|
{
|
||||||
|
this.recordLabelFields = Arrays.asList(recordLabelFields);
|
||||||
|
return (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Getter for sections
|
** Getter for sections
|
||||||
**
|
**
|
||||||
|
@ -167,6 +167,11 @@ public class Auth0AuthenticationModule implements QAuthenticationModuleInterface
|
|||||||
return (false);
|
return (false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(session.getIdReference() == null)
|
||||||
|
{
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
StateProviderInterface spi = getStateProvider();
|
StateProviderInterface spi = getStateProvider();
|
||||||
Auth0StateKey key = new Auth0StateKey(session.getIdReference());
|
Auth0StateKey key = new Auth0StateKey(session.getIdReference());
|
||||||
Optional<Instant> lastTimeCheckedOptional = spi.get(Instant.class, key);
|
Optional<Instant> lastTimeCheckedOptional = spi.get(Instant.class, key);
|
||||||
|
@ -24,18 +24,21 @@ package com.kingsrook.qqq.backend.core.modules.backend.implementations.memory;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
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 com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||||
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.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
|
import org.apache.commons.lang.NotImplementedException;
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -48,6 +51,12 @@ public class MemoryRecordStore
|
|||||||
private Map<String, Map<Serializable, QRecord>> data;
|
private Map<String, Map<Serializable, QRecord>> data;
|
||||||
private Map<String, Integer> nextSerials;
|
private Map<String, Integer> nextSerials;
|
||||||
|
|
||||||
|
private static boolean collectStatistics = false;
|
||||||
|
|
||||||
|
private static final Map<String, Integer> statistics = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
|
||||||
|
public static final String STAT_QUERIES_RAN = "queriesRan";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -105,9 +114,56 @@ public class MemoryRecordStore
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
public List<QRecord> query(QueryInput input)
|
public List<QRecord> query(QueryInput input)
|
||||||
{
|
{
|
||||||
|
incrementStatistic(STAT_QUERIES_RAN);
|
||||||
|
|
||||||
Map<Serializable, QRecord> tableData = getTableData(input.getTable());
|
Map<Serializable, QRecord> tableData = getTableData(input.getTable());
|
||||||
List<QRecord> records = new ArrayList<>(tableData.values());
|
List<QRecord> records = new ArrayList<>();
|
||||||
// todo - filtering
|
|
||||||
|
for(QRecord qRecord : tableData.values())
|
||||||
|
{
|
||||||
|
boolean recordMatches = true;
|
||||||
|
if(input.getFilter() != null && input.getFilter().getCriteria() != null)
|
||||||
|
{
|
||||||
|
for(QFilterCriteria criterion : input.getFilter().getCriteria())
|
||||||
|
{
|
||||||
|
String fieldName = criterion.getFieldName();
|
||||||
|
Serializable value = qRecord.getValue(fieldName);
|
||||||
|
switch(criterion.getOperator())
|
||||||
|
{
|
||||||
|
case EQUALS:
|
||||||
|
{
|
||||||
|
if(!value.equals(criterion.getValues().get(0)))
|
||||||
|
{
|
||||||
|
recordMatches = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case IN:
|
||||||
|
{
|
||||||
|
if(!criterion.getValues().contains(value))
|
||||||
|
{
|
||||||
|
recordMatches = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
throw new NotImplementedException("Operator [" + criterion.getOperator() + "] is not yet implemented in the Memory backend.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!recordMatches)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(recordMatches)
|
||||||
|
{
|
||||||
|
records.add(qRecord);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (records);
|
return (records);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +176,7 @@ public class MemoryRecordStore
|
|||||||
{
|
{
|
||||||
Map<Serializable, QRecord> tableData = getTableData(input.getTable());
|
Map<Serializable, QRecord> tableData = getTableData(input.getTable());
|
||||||
List<QRecord> records = new ArrayList<>(tableData.values());
|
List<QRecord> records = new ArrayList<>(tableData.values());
|
||||||
// todo - filtering
|
// todo - filtering (call query)
|
||||||
return (records.size());
|
return (records.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -235,4 +291,53 @@ public class MemoryRecordStore
|
|||||||
|
|
||||||
return (rowsDeleted);
|
return (rowsDeleted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for collectStatistics
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static void setCollectStatistics(boolean collectStatistics)
|
||||||
|
{
|
||||||
|
MemoryRecordStore.collectStatistics = collectStatistics;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Increment a statistic
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static void incrementStatistic(String statName)
|
||||||
|
{
|
||||||
|
if(collectStatistics)
|
||||||
|
{
|
||||||
|
statistics.putIfAbsent(statName, 0);
|
||||||
|
statistics.put(statName, statistics.get(statName) + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** clear the map of statistics
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static void resetStatistics()
|
||||||
|
{
|
||||||
|
statistics.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Getter for statistics
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static Map<String, Integer> getStatistics()
|
||||||
|
{
|
||||||
|
return statistics;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -47,18 +47,33 @@ class QueryActionTest
|
|||||||
@Test
|
@Test
|
||||||
public void test() throws QException
|
public void test() throws QException
|
||||||
{
|
{
|
||||||
QueryInput request = new QueryInput(TestUtils.defineInstance());
|
QueryInput queryInput = new QueryInput(TestUtils.defineInstance());
|
||||||
request.setSession(TestUtils.getMockSession());
|
queryInput.setSession(TestUtils.getMockSession());
|
||||||
request.setTableName("person");
|
queryInput.setTableName("person");
|
||||||
QueryOutput result = new QueryAction().execute(request);
|
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||||
assertNotNull(result);
|
assertNotNull(queryOutput);
|
||||||
|
|
||||||
assertThat(result.getRecords()).isNotEmpty();
|
assertThat(queryOutput.getRecords()).isNotEmpty();
|
||||||
for(QRecord record : result.getRecords())
|
for(QRecord record : queryOutput.getRecords())
|
||||||
{
|
{
|
||||||
assertThat(record.getValues()).isNotEmpty();
|
assertThat(record.getValues()).isNotEmpty();
|
||||||
assertThat(record.getDisplayValues()).isNotEmpty();
|
|
||||||
assertThat(record.getErrors()).isEmpty();
|
assertThat(record.getErrors()).isEmpty();
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
|
// this SHOULD be empty, based on the default for the should //
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
|
assertThat(record.getDisplayValues()).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////
|
||||||
|
// now flip that field and re-run //
|
||||||
|
////////////////////////////////////
|
||||||
|
queryInput.setShouldGenerateDisplayValues(true);
|
||||||
|
assertThat(queryOutput.getRecords()).isNotEmpty();
|
||||||
|
queryOutput = new QueryAction().execute(queryInput);
|
||||||
|
for(QRecord record : queryOutput.getRecords())
|
||||||
|
{
|
||||||
|
assertThat(record.getDisplayValues()).isNotEmpty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,241 @@
|
|||||||
|
/*
|
||||||
|
* QQQ - Low-code Application Framework for Engineers.
|
||||||
|
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||||
|
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||||
|
* contact@kingsrook.com
|
||||||
|
* https://github.com/Kingsrook/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.kingsrook.qqq.backend.core.actions.values;
|
||||||
|
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.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.QFieldMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||||
|
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryRecordStore;
|
||||||
|
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public class QPossibleValueTranslatorTest
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testPossibleValueEnum()
|
||||||
|
{
|
||||||
|
QInstance qInstance = TestUtils.defineInstance();
|
||||||
|
QPossibleValueTranslator possibleValueTranslator = new QPossibleValueTranslator(qInstance, new QSession());
|
||||||
|
QFieldMetaData stateField = qInstance.getTable("person").getField("homeStateId");
|
||||||
|
QPossibleValueSource possibleValueSource = qInstance.getPossibleValueSource(stateField.getPossibleValueSourceName());
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// assert the default formatting for a not-found value is a null string //
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
assertNull(possibleValueTranslator.translatePossibleValue(stateField, null));
|
||||||
|
assertNull(possibleValueTranslator.translatePossibleValue(stateField, -1));
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// let the not-found value be a simple string (no formatted values) //
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
possibleValueSource.setValueFormatIfNotFound("?");
|
||||||
|
assertEquals("?", possibleValueTranslator.translatePossibleValue(stateField, null));
|
||||||
|
assertEquals("?", possibleValueTranslator.translatePossibleValue(stateField, -1));
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
// let the not-found value be a string w/ formatted values //
|
||||||
|
/////////////////////////////////////////////////////////////
|
||||||
|
possibleValueSource.setValueFormatIfNotFound("? (%s)");
|
||||||
|
possibleValueSource.setValueFieldsIfNotFound(List.of("id"));
|
||||||
|
assertEquals("? ()", possibleValueTranslator.translatePossibleValue(stateField, null));
|
||||||
|
assertEquals("? (-1)", possibleValueTranslator.translatePossibleValue(stateField, -1));
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
// assert the default formatting is just the label //
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
assertEquals("MO", possibleValueTranslator.translatePossibleValue(stateField, 2));
|
||||||
|
assertEquals("IL", possibleValueTranslator.translatePossibleValue(stateField, 1));
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
// assert the LABEL_ONLY format (when called out specifically) //
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
possibleValueSource.setValueFormat(QPossibleValueSource.ValueFormat.LABEL_ONLY);
|
||||||
|
possibleValueSource.setValueFields(QPossibleValueSource.ValueFields.LABEL_ONLY);
|
||||||
|
assertEquals("IL", possibleValueTranslator.translatePossibleValue(stateField, 1));
|
||||||
|
|
||||||
|
///////////////////////////////////////
|
||||||
|
// assert the LABEL_PARAMS_ID format //
|
||||||
|
///////////////////////////////////////
|
||||||
|
possibleValueSource.setValueFormat(QPossibleValueSource.ValueFormat.LABEL_PARENS_ID);
|
||||||
|
possibleValueSource.setValueFields(QPossibleValueSource.ValueFields.LABEL_PARENS_ID);
|
||||||
|
assertEquals("IL (1)", possibleValueTranslator.translatePossibleValue(stateField, 1));
|
||||||
|
|
||||||
|
//////////////////////////////////////
|
||||||
|
// assert the ID_COLON_LABEL format //
|
||||||
|
//////////////////////////////////////
|
||||||
|
possibleValueSource.setValueFormat(QPossibleValueSource.ValueFormat.ID_COLON_LABEL);
|
||||||
|
possibleValueSource.setValueFields(QPossibleValueSource.ValueFields.ID_COLON_LABEL);
|
||||||
|
assertEquals("1: IL", possibleValueTranslator.translatePossibleValue(stateField, 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testPossibleValueTable() throws QException
|
||||||
|
{
|
||||||
|
QInstance qInstance = TestUtils.defineInstance();
|
||||||
|
QPossibleValueTranslator possibleValueTranslator = new QPossibleValueTranslator(qInstance, new QSession());
|
||||||
|
QTableMetaData shapeTable = qInstance.getTable(TestUtils.TABLE_NAME_SHAPE);
|
||||||
|
QFieldMetaData shapeField = qInstance.getTable(TestUtils.TABLE_NAME_PERSON).getField("favoriteShapeId");
|
||||||
|
QPossibleValueSource possibleValueSource = qInstance.getPossibleValueSource(shapeField.getPossibleValueSourceName());
|
||||||
|
|
||||||
|
List<QRecord> shapeRecords = List.of(
|
||||||
|
new QRecord().withTableName(shapeTable.getName()).withValue("id", 1).withValue("name", "Triangle"),
|
||||||
|
new QRecord().withTableName(shapeTable.getName()).withValue("id", 2).withValue("name", "Square"),
|
||||||
|
new QRecord().withTableName(shapeTable.getName()).withValue("id", 3).withValue("name", "Circle"));
|
||||||
|
|
||||||
|
InsertInput insertInput = new InsertInput(qInstance);
|
||||||
|
insertInput.setSession(new QSession());
|
||||||
|
insertInput.setTableName(shapeTable.getName());
|
||||||
|
insertInput.setRecords(shapeRecords);
|
||||||
|
new InsertAction().execute(insertInput);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
// assert the default formatting for a not-found value is a null string //
|
||||||
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
assertNull(possibleValueTranslator.translatePossibleValue(shapeField, null));
|
||||||
|
assertNull(possibleValueTranslator.translatePossibleValue(shapeField, -1));
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// let the not-found value be a simple string (no formatted values) //
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
possibleValueSource.setValueFormatIfNotFound("?");
|
||||||
|
assertEquals("?", possibleValueTranslator.translatePossibleValue(shapeField, null));
|
||||||
|
assertEquals("?", possibleValueTranslator.translatePossibleValue(shapeField, -1));
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
// assert the default formatting is just the label //
|
||||||
|
/////////////////////////////////////////////////////
|
||||||
|
assertEquals("Square", possibleValueTranslator.translatePossibleValue(shapeField, 2));
|
||||||
|
assertEquals("Triangle", possibleValueTranslator.translatePossibleValue(shapeField, 1));
|
||||||
|
|
||||||
|
///////////////////////////////////////
|
||||||
|
// assert the LABEL_PARAMS_ID format //
|
||||||
|
///////////////////////////////////////
|
||||||
|
possibleValueSource.setValueFormat(QPossibleValueSource.ValueFormat.LABEL_PARENS_ID);
|
||||||
|
possibleValueSource.setValueFields(QPossibleValueSource.ValueFields.LABEL_PARENS_ID);
|
||||||
|
assertEquals("Circle (3)", possibleValueTranslator.translatePossibleValue(shapeField, 3));
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
// assert that we don't re-run queries for cached values //
|
||||||
|
///////////////////////////////////////////////////////////
|
||||||
|
possibleValueTranslator = new QPossibleValueTranslator(qInstance, new QSession());
|
||||||
|
MemoryRecordStore.setCollectStatistics(true);
|
||||||
|
possibleValueTranslator.translatePossibleValue(shapeField, 1);
|
||||||
|
possibleValueTranslator.translatePossibleValue(shapeField, 2);
|
||||||
|
assertEquals(2, MemoryRecordStore.getStatistics().get(MemoryRecordStore.STAT_QUERIES_RAN), "Should have ran 2 queries so far");
|
||||||
|
possibleValueTranslator.translatePossibleValue(shapeField, 2);
|
||||||
|
possibleValueTranslator.translatePossibleValue(shapeField, 3);
|
||||||
|
possibleValueTranslator.translatePossibleValue(shapeField, 3);
|
||||||
|
assertEquals(3, MemoryRecordStore.getStatistics().get(MemoryRecordStore.STAT_QUERIES_RAN), "Should have ran 3 queries in total");
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
|
// assert that if we prime the cache, we can do just 1 query //
|
||||||
|
///////////////////////////////////////////////////////////////
|
||||||
|
possibleValueTranslator = new QPossibleValueTranslator(qInstance, new QSession());
|
||||||
|
List<QRecord> personRecords = List.of(
|
||||||
|
new QRecord().withTableName(TestUtils.TABLE_NAME_PERSON).withValue("favoriteShapeId", 1),
|
||||||
|
new QRecord().withTableName(TestUtils.TABLE_NAME_PERSON).withValue("favoriteShapeId", 1),
|
||||||
|
new QRecord().withTableName(TestUtils.TABLE_NAME_PERSON).withValue("favoriteShapeId", 2),
|
||||||
|
new QRecord().withTableName(TestUtils.TABLE_NAME_PERSON).withValue("favoriteShapeId", 2),
|
||||||
|
new QRecord().withTableName(TestUtils.TABLE_NAME_PERSON).withValue("favoriteShapeId", 3)
|
||||||
|
);
|
||||||
|
QTableMetaData personTable = qInstance.getTable(TestUtils.TABLE_NAME_PERSON);
|
||||||
|
MemoryRecordStore.resetStatistics();
|
||||||
|
possibleValueTranslator.primePvsCache(personTable, personRecords);
|
||||||
|
assertEquals(1, MemoryRecordStore.getStatistics().get(MemoryRecordStore.STAT_QUERIES_RAN), "Should only run 1 query");
|
||||||
|
possibleValueTranslator.translatePossibleValue(shapeField, 1);
|
||||||
|
possibleValueTranslator.translatePossibleValue(shapeField, 2);
|
||||||
|
assertEquals(1, MemoryRecordStore.getStatistics().get(MemoryRecordStore.STAT_QUERIES_RAN), "Should only run 1 query");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testSetDisplayValuesInRecords()
|
||||||
|
{
|
||||||
|
QTableMetaData table = new QTableMetaData()
|
||||||
|
.withRecordLabelFormat("%s %s")
|
||||||
|
.withRecordLabelFields("firstName", "lastName")
|
||||||
|
.withField(new QFieldMetaData("firstName", QFieldType.STRING))
|
||||||
|
.withField(new QFieldMetaData("lastName", QFieldType.STRING))
|
||||||
|
.withField(new QFieldMetaData("price", QFieldType.DECIMAL).withDisplayFormat(DisplayFormat.CURRENCY))
|
||||||
|
.withField(new QFieldMetaData("homeStateId", QFieldType.INTEGER).withPossibleValueSourceName(TestUtils.POSSIBLE_VALUE_SOURCE_STATE));
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
// first, make sure it doesn't crash with null or empty inputs //
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
QPossibleValueTranslator possibleValueTranslator = new QPossibleValueTranslator(TestUtils.defineInstance(), new QSession());
|
||||||
|
possibleValueTranslator.translatePossibleValuesInRecords(table, null);
|
||||||
|
possibleValueTranslator.translatePossibleValuesInRecords(table, Collections.emptyList());
|
||||||
|
|
||||||
|
List<QRecord> records = List.of(
|
||||||
|
new QRecord()
|
||||||
|
.withValue("firstName", "Tim")
|
||||||
|
.withValue("lastName", "Chamberlain")
|
||||||
|
.withValue("price", new BigDecimal("3.50"))
|
||||||
|
.withValue("homeStateId", 1),
|
||||||
|
new QRecord()
|
||||||
|
.withValue("firstName", "Tyler")
|
||||||
|
.withValue("lastName", "Samples")
|
||||||
|
.withValue("price", new BigDecimal("174999.99"))
|
||||||
|
.withValue("homeStateId", 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
possibleValueTranslator.translatePossibleValuesInRecords(table, records);
|
||||||
|
|
||||||
|
assertNull(records.get(0).getRecordLabel()); // regular display stuff NOT done by PVS translator
|
||||||
|
assertNull(records.get(0).getDisplayValue("price"));
|
||||||
|
|
||||||
|
assertEquals("IL", records.get(0).getDisplayValue("homeStateId"));
|
||||||
|
assertEquals("MO", records.get(1).getDisplayValue("homeStateId"));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -30,6 +30,7 @@ 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.tables.QTableMetaData;
|
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 org.junit.jupiter.api.Test;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
@ -47,24 +48,26 @@ class QValueFormatterTest
|
|||||||
@Test
|
@Test
|
||||||
void testFormatValue()
|
void testFormatValue()
|
||||||
{
|
{
|
||||||
assertNull(QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), null));
|
QValueFormatter qValueFormatter = new QValueFormatter();
|
||||||
|
|
||||||
assertEquals("1", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), 1));
|
assertNull(qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), null));
|
||||||
assertEquals("1,000", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), 1000));
|
|
||||||
assertEquals("1000", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(null), 1000));
|
|
||||||
assertEquals("$1,000.00", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.CURRENCY), 1000));
|
|
||||||
assertEquals("1,000.00", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.DECIMAL2_COMMAS), 1000));
|
|
||||||
assertEquals("1000.00", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.DECIMAL2), 1000));
|
|
||||||
|
|
||||||
assertEquals("1", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), new BigDecimal("1")));
|
assertEquals("1", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), 1));
|
||||||
assertEquals("1,000", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), new BigDecimal("1000")));
|
assertEquals("1,000", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), 1000));
|
||||||
assertEquals("1000", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.STRING), new BigDecimal("1000")));
|
assertEquals("1000", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(null), 1000));
|
||||||
assertEquals("1000", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.STRING), 1000));
|
assertEquals("$1,000.00", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.CURRENCY), 1000));
|
||||||
|
assertEquals("1,000.00", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.DECIMAL2_COMMAS), 1000));
|
||||||
|
assertEquals("1000.00", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.DECIMAL2), 1000));
|
||||||
|
|
||||||
|
assertEquals("1", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), new BigDecimal("1")));
|
||||||
|
assertEquals("1,000", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), new BigDecimal("1000")));
|
||||||
|
assertEquals("1000", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.STRING), new BigDecimal("1000")));
|
||||||
|
assertEquals("1000", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.STRING), 1000));
|
||||||
|
|
||||||
//////////////////////////////////////////////////
|
//////////////////////////////////////////////////
|
||||||
// this one flows through the exceptional cases //
|
// this one flows through the exceptional cases //
|
||||||
//////////////////////////////////////////////////
|
//////////////////////////////////////////////////
|
||||||
assertEquals("1000.01", QValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), new BigDecimal("1000.01")));
|
assertEquals("1000.01", qValueFormatter.formatValue(new QFieldMetaData().withDisplayFormat(DisplayFormat.COMMAS), new BigDecimal("1000.01")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -75,40 +78,42 @@ class QValueFormatterTest
|
|||||||
@Test
|
@Test
|
||||||
void testFormatRecordLabel()
|
void testFormatRecordLabel()
|
||||||
{
|
{
|
||||||
QTableMetaData table = new QTableMetaData().withRecordLabelFormat("%s %s").withRecordLabelFields(List.of("firstName", "lastName"));
|
QValueFormatter qValueFormatter = new QValueFormatter();
|
||||||
assertEquals("Darin Kelkhoff", QValueFormatter.formatRecordLabel(table, new QRecord().withValue("firstName", "Darin").withValue("lastName", "Kelkhoff")));
|
|
||||||
assertEquals("Darin ", QValueFormatter.formatRecordLabel(table, new QRecord().withValue("firstName", "Darin")));
|
|
||||||
assertEquals("Darin ", QValueFormatter.formatRecordLabel(table, new QRecord().withValue("firstName", "Darin").withValue("lastName", null)));
|
|
||||||
|
|
||||||
table = new QTableMetaData().withRecordLabelFormat("%s " + DisplayFormat.CURRENCY).withRecordLabelFields(List.of("firstName", "price"));
|
QTableMetaData table = new QTableMetaData().withRecordLabelFormat("%s %s").withRecordLabelFields(List.of("firstName", "lastName"));
|
||||||
assertEquals("Darin $10,000.00", QValueFormatter.formatRecordLabel(table, new QRecord().withValue("firstName", "Darin").withValue("price", new BigDecimal(10000))));
|
assertEquals("Darin Kelkhoff", qValueFormatter.formatRecordLabel(table, new QRecord().withValue("firstName", "Darin").withValue("lastName", "Kelkhoff")));
|
||||||
|
assertEquals("Darin ", qValueFormatter.formatRecordLabel(table, new QRecord().withValue("firstName", "Darin")));
|
||||||
|
assertEquals("Darin ", qValueFormatter.formatRecordLabel(table, new QRecord().withValue("firstName", "Darin").withValue("lastName", null)));
|
||||||
|
|
||||||
|
table = new QTableMetaData().withRecordLabelFormat("%s " + DisplayFormat.CURRENCY).withRecordLabelFields("firstName", "price");
|
||||||
|
assertEquals("Darin $10,000.00", qValueFormatter.formatRecordLabel(table, new QRecord().withValue("firstName", "Darin").withValue("price", new BigDecimal(10000))));
|
||||||
|
|
||||||
table = new QTableMetaData().withRecordLabelFormat(DisplayFormat.DEFAULT).withRecordLabelFields(List.of("id"));
|
table = new QTableMetaData().withRecordLabelFormat(DisplayFormat.DEFAULT).withRecordLabelFields(List.of("id"));
|
||||||
assertEquals("123456", QValueFormatter.formatRecordLabel(table, new QRecord().withValue("id", "123456")));
|
assertEquals("123456", qValueFormatter.formatRecordLabel(table, new QRecord().withValue("id", "123456")));
|
||||||
|
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
// exceptional flow: no recordLabelFormat specified //
|
// exceptional flow: no recordLabelFormat specified //
|
||||||
///////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////
|
||||||
table = new QTableMetaData().withPrimaryKeyField("id");
|
table = new QTableMetaData().withPrimaryKeyField("id");
|
||||||
assertEquals("42", QValueFormatter.formatRecordLabel(table, new QRecord().withValue("id", 42)));
|
assertEquals("42", qValueFormatter.formatRecordLabel(table, new QRecord().withValue("id", 42)));
|
||||||
|
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
// exceptional flow: no fields for the format //
|
// exceptional flow: no fields for the format //
|
||||||
/////////////////////////////////////////////////
|
/////////////////////////////////////////////////
|
||||||
table = new QTableMetaData().withRecordLabelFormat("%s %s").withPrimaryKeyField("id");
|
table = new QTableMetaData().withRecordLabelFormat("%s %s").withPrimaryKeyField("id");
|
||||||
assertEquals("128", QValueFormatter.formatRecordLabel(table, new QRecord().withValue("id", 128)));
|
assertEquals("128", qValueFormatter.formatRecordLabel(table, new QRecord().withValue("id", 128)));
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////
|
||||||
// exceptional flow: not enough fields for the format //
|
// exceptional flow: not enough fields for the format //
|
||||||
/////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////
|
||||||
table = new QTableMetaData().withRecordLabelFormat("%s %s").withRecordLabelFields(List.of("a")).withPrimaryKeyField("id");
|
table = new QTableMetaData().withRecordLabelFormat("%s %s").withRecordLabelFields("a").withPrimaryKeyField("id");
|
||||||
assertEquals("256", QValueFormatter.formatRecordLabel(table, new QRecord().withValue("a", 47).withValue("id", 256)));
|
assertEquals("256", qValueFormatter.formatRecordLabel(table, new QRecord().withValue("a", 47).withValue("id", 256)));
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// exceptional flow (kinda): too many fields for the format (just get the ones that are in the format) //
|
// exceptional flow (kinda): too many fields for the format (just get the ones that are in the format) //
|
||||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
table = new QTableMetaData().withRecordLabelFormat("%s %s").withRecordLabelFields(List.of("a", "b", "c")).withPrimaryKeyField("id");
|
table = new QTableMetaData().withRecordLabelFormat("%s %s").withRecordLabelFields(List.of("a", "b", "c")).withPrimaryKeyField("id");
|
||||||
assertEquals("47 48", QValueFormatter.formatRecordLabel(table, new QRecord().withValue("a", 47).withValue("b", 48).withValue("c", 49).withValue("id", 256)));
|
assertEquals("47 48", qValueFormatter.formatRecordLabel(table, new QRecord().withValue("a", 47).withValue("b", 48).withValue("c", 49).withValue("id", 256)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -121,40 +126,46 @@ class QValueFormatterTest
|
|||||||
{
|
{
|
||||||
QTableMetaData table = new QTableMetaData()
|
QTableMetaData table = new QTableMetaData()
|
||||||
.withRecordLabelFormat("%s %s")
|
.withRecordLabelFormat("%s %s")
|
||||||
.withRecordLabelFields(List.of("firstName", "lastName"))
|
.withRecordLabelFields("firstName", "lastName")
|
||||||
.withField(new QFieldMetaData("firstName", QFieldType.STRING))
|
.withField(new QFieldMetaData("firstName", QFieldType.STRING))
|
||||||
.withField(new QFieldMetaData("lastName", QFieldType.STRING))
|
.withField(new QFieldMetaData("lastName", QFieldType.STRING))
|
||||||
.withField(new QFieldMetaData("price", QFieldType.DECIMAL).withDisplayFormat(DisplayFormat.CURRENCY))
|
.withField(new QFieldMetaData("price", QFieldType.DECIMAL).withDisplayFormat(DisplayFormat.CURRENCY))
|
||||||
.withField(new QFieldMetaData("quantity", QFieldType.INTEGER).withDisplayFormat(DisplayFormat.COMMAS));
|
.withField(new QFieldMetaData("quantity", QFieldType.INTEGER).withDisplayFormat(DisplayFormat.COMMAS))
|
||||||
|
.withField(new QFieldMetaData("homeStateId", QFieldType.INTEGER).withPossibleValueSourceName(TestUtils.POSSIBLE_VALUE_SOURCE_STATE));
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
// first, make sure it doesn't crash with null or empty inputs //
|
// first, make sure it doesn't crash with null or empty inputs //
|
||||||
/////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////
|
||||||
QValueFormatter.setDisplayValuesInRecords(table, null);
|
QValueFormatter qValueFormatter = new QValueFormatter();
|
||||||
QValueFormatter.setDisplayValuesInRecords(table, Collections.emptyList());
|
qValueFormatter.setDisplayValuesInRecords(table, null);
|
||||||
|
qValueFormatter.setDisplayValuesInRecords(table, Collections.emptyList());
|
||||||
|
|
||||||
List<QRecord> records = List.of(
|
List<QRecord> records = List.of(
|
||||||
new QRecord()
|
new QRecord()
|
||||||
.withValue("firstName", "Tim")
|
.withValue("firstName", "Tim")
|
||||||
.withValue("lastName", "Chamberlain")
|
.withValue("lastName", "Chamberlain")
|
||||||
.withValue("price", new BigDecimal("3.50"))
|
.withValue("price", new BigDecimal("3.50"))
|
||||||
.withValue("quantity", 1701),
|
.withValue("quantity", 1701)
|
||||||
|
.withValue("homeStateId", 1),
|
||||||
new QRecord()
|
new QRecord()
|
||||||
.withValue("firstName", "Tyler")
|
.withValue("firstName", "Tyler")
|
||||||
.withValue("lastName", "Samples")
|
.withValue("lastName", "Samples")
|
||||||
.withValue("price", new BigDecimal("174999.99"))
|
.withValue("price", new BigDecimal("174999.99"))
|
||||||
.withValue("quantity", 47)
|
.withValue("quantity", 47)
|
||||||
|
.withValue("homeStateId", 2)
|
||||||
);
|
);
|
||||||
|
|
||||||
QValueFormatter.setDisplayValuesInRecords(table, records);
|
qValueFormatter.setDisplayValuesInRecords(table, records);
|
||||||
|
|
||||||
assertEquals("Tim Chamberlain", records.get(0).getRecordLabel());
|
assertEquals("Tim Chamberlain", records.get(0).getRecordLabel());
|
||||||
assertEquals("$3.50", records.get(0).getDisplayValue("price"));
|
assertEquals("$3.50", records.get(0).getDisplayValue("price"));
|
||||||
assertEquals("1,701", records.get(0).getDisplayValue("quantity"));
|
assertEquals("1,701", records.get(0).getDisplayValue("quantity"));
|
||||||
|
assertEquals("1", records.get(0).getDisplayValue("homeStateId")); // PVS NOT translated by this class.
|
||||||
|
|
||||||
assertEquals("Tyler Samples", records.get(1).getRecordLabel());
|
assertEquals("Tyler Samples", records.get(1).getRecordLabel());
|
||||||
assertEquals("$174,999.99", records.get(1).getDisplayValue("price"));
|
assertEquals("$174,999.99", records.get(1).getDisplayValue("price"));
|
||||||
assertEquals("47", records.get(1).getDisplayValue("quantity"));
|
assertEquals("47", records.get(1).getDisplayValue("quantity"));
|
||||||
|
assertEquals("2", records.get(1).getDisplayValue("homeStateId")); // PVS NOT translated by this class.
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -31,6 +31,7 @@ import org.junit.jupiter.api.Assertions;
|
|||||||
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;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
|
||||||
@ -281,4 +282,60 @@ class CsvToQRecordAdapterTest
|
|||||||
// todo - this is what the method header comment means when it says we don't handle all cases well...
|
// todo - this is what the method header comment means when it says we don't handle all cases well...
|
||||||
// Assertions.assertEquals(List.of("A", "B", "C", "C 2", "C 3"), csvToQRecordAdapter.makeHeadersUnique(List.of("A", "B", "C 2", "C", "C 3")));
|
// Assertions.assertEquals(List.of("A", "B", "C", "C 2", "C 3"), csvToQRecordAdapter.makeHeadersUnique(List.of("A", "B", "C 2", "C", "C 3")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testByteOrderMarker()
|
||||||
|
{
|
||||||
|
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
|
||||||
|
List<QRecord> records = csvToQRecordAdapter.buildRecordsFromCsv("""
|
||||||
|
id,firstName
|
||||||
|
1,John""", TestUtils.defineTablePerson(), null);
|
||||||
|
|
||||||
|
assertEquals(1, records.get(0).getValueInteger("id"));
|
||||||
|
assertEquals("John", records.get(0).getValueString("firstName"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Fix an IndexOutOfBounds that we used to throw.
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testTooFewBodyColumns()
|
||||||
|
{
|
||||||
|
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
|
||||||
|
List<QRecord> records = csvToQRecordAdapter.buildRecordsFromCsv("""
|
||||||
|
id,firstName,lastName
|
||||||
|
1,John""", TestUtils.defineTablePerson(), null);
|
||||||
|
|
||||||
|
assertEquals(1, records.get(0).getValueInteger("id"));
|
||||||
|
assertEquals("John", records.get(0).getValueString("firstName"));
|
||||||
|
assertNull(records.get(0).getValueString("lastName"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
public void testTooFewColumnsIndexMapping()
|
||||||
|
{
|
||||||
|
int index = 1;
|
||||||
|
QIndexBasedFieldMapping mapping = new QIndexBasedFieldMapping()
|
||||||
|
.withMapping("id", index++)
|
||||||
|
.withMapping("firstName", index++)
|
||||||
|
.withMapping("lastName", index++);
|
||||||
|
|
||||||
|
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
|
||||||
|
List<QRecord> records = csvToQRecordAdapter.buildRecordsFromCsv("1,John", TestUtils.defineTablePerson(), mapping);
|
||||||
|
|
||||||
|
assertEquals(1, records.get(0).getValueInteger("id"));
|
||||||
|
assertEquals("John", records.get(0).getValueString("firstName"));
|
||||||
|
assertNull(records.get(0).getValueString("lastName"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,10 +22,10 @@
|
|||||||
package com.kingsrook.qqq.backend.core.instances;
|
package com.kingsrook.qqq.backend.core.instances;
|
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
|
|
||||||
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.QFieldType;
|
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||||
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;
|
||||||
@ -130,6 +130,20 @@ class QInstanceEnricherTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testNameToLabel()
|
||||||
|
{
|
||||||
|
assertEquals("Address 2", QInstanceEnricher.nameToLabel("address2"));
|
||||||
|
assertEquals("Field 20", QInstanceEnricher.nameToLabel("field20"));
|
||||||
|
assertEquals("Something USA", QInstanceEnricher.nameToLabel("somethingUSA"));
|
||||||
|
assertEquals("Number 1 Dad", QInstanceEnricher.nameToLabel("number1Dad"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -146,4 +160,28 @@ class QInstanceEnricherTest
|
|||||||
assertEquals("tla_and_another_tla", QInstanceEnricher.inferBackendName("TLAAndAnotherTLA"));
|
assertEquals("tla_and_another_tla", QInstanceEnricher.inferBackendName("TLAAndAnotherTLA"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testInferredRecordLabelFormat()
|
||||||
|
{
|
||||||
|
QInstance qInstance = TestUtils.defineInstance();
|
||||||
|
QTableMetaData table = qInstance.getTable("person").withRecordLabelFormat(null).withRecordLabelFields(new ArrayList<>());
|
||||||
|
new QInstanceEnricher().enrich(qInstance);
|
||||||
|
assertNull(table.getRecordLabelFormat());
|
||||||
|
|
||||||
|
qInstance = TestUtils.defineInstance();
|
||||||
|
table = qInstance.getTable("person").withRecordLabelFormat(null).withRecordLabelFields("firstName");
|
||||||
|
new QInstanceEnricher().enrich(qInstance);
|
||||||
|
assertEquals("%s", table.getRecordLabelFormat());
|
||||||
|
|
||||||
|
qInstance = TestUtils.defineInstance();
|
||||||
|
table = qInstance.getTable("person").withRecordLabelFormat(null).withRecordLabelFields("firstName", "lastName");
|
||||||
|
new QInstanceEnricher().enrich(qInstance);
|
||||||
|
assertEquals("%s %s", table.getRecordLabelFormat());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,16 +22,20 @@
|
|||||||
package com.kingsrook.qqq.backend.core.instances;
|
package com.kingsrook.qqq.backend.core.instances;
|
||||||
|
|
||||||
|
|
||||||
|
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.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
||||||
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.code.QCodeReference;
|
||||||
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.layout.QAppMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
import com.kingsrook.qqq.backend.core.model.metadata.layout.QIcon;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
||||||
@ -110,7 +114,7 @@ class QInstanceValidatorTest
|
|||||||
@Test
|
@Test
|
||||||
public void test_validateNullTables()
|
public void test_validateNullTables()
|
||||||
{
|
{
|
||||||
assertValidationFailureReasons((qInstance) ->
|
assertValidationFailureReasonsAllowingExtraReasons((qInstance) ->
|
||||||
{
|
{
|
||||||
qInstance.setTables(null);
|
qInstance.setTables(null);
|
||||||
qInstance.setProcesses(null);
|
qInstance.setProcesses(null);
|
||||||
@ -127,7 +131,7 @@ class QInstanceValidatorTest
|
|||||||
@Test
|
@Test
|
||||||
public void test_validateEmptyTables()
|
public void test_validateEmptyTables()
|
||||||
{
|
{
|
||||||
assertValidationFailureReasons((qInstance) ->
|
assertValidationFailureReasonsAllowingExtraReasons((qInstance) ->
|
||||||
{
|
{
|
||||||
qInstance.setTables(new HashMap<>());
|
qInstance.setTables(new HashMap<>());
|
||||||
qInstance.setProcesses(new HashMap<>());
|
qInstance.setProcesses(new HashMap<>());
|
||||||
@ -150,10 +154,13 @@ class QInstanceValidatorTest
|
|||||||
qInstance.getTable("person").setName("notPerson");
|
qInstance.getTable("person").setName("notPerson");
|
||||||
qInstance.getBackend("default").setName("notDefault");
|
qInstance.getBackend("default").setName("notDefault");
|
||||||
qInstance.getProcess(TestUtils.PROCESS_NAME_GREET_PEOPLE).setName("notGreetPeople");
|
qInstance.getProcess(TestUtils.PROCESS_NAME_GREET_PEOPLE).setName("notGreetPeople");
|
||||||
|
qInstance.getPossibleValueSource(TestUtils.POSSIBLE_VALUE_SOURCE_STATE).setName("notStates");
|
||||||
},
|
},
|
||||||
"Inconsistent naming for table",
|
"Inconsistent naming for table",
|
||||||
"Inconsistent naming for backend",
|
"Inconsistent naming for backend",
|
||||||
"Inconsistent naming for process");
|
"Inconsistent naming for process",
|
||||||
|
"Inconsistent naming for possibleValueSource"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -184,6 +191,19 @@ class QInstanceValidatorTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
public void test_validateTableBadRecordFormatField()
|
||||||
|
{
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getTable("person").withRecordLabelFields("notAField"),
|
||||||
|
"not a field");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Test that if a process specifies a table that doesn't exist, that it fails.
|
** Test that if a process specifies a table that doesn't exist, that it fails.
|
||||||
**
|
**
|
||||||
@ -252,7 +272,7 @@ class QInstanceValidatorTest
|
|||||||
@Test
|
@Test
|
||||||
public void test_validateFieldWithMissingPossibleValueSource()
|
public void test_validateFieldWithMissingPossibleValueSource()
|
||||||
{
|
{
|
||||||
assertValidationFailureReasons((qInstance) -> qInstance.getTable("person").getField("homeState").setPossibleValueSourceName("not a real possible value source"),
|
assertValidationFailureReasons((qInstance) -> qInstance.getTable("person").getField("homeStateId").setPossibleValueSourceName("not a real possible value source"),
|
||||||
"Unrecognized possibleValueSourceName");
|
"Unrecognized possibleValueSourceName");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -319,6 +339,7 @@ class QInstanceValidatorTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -376,6 +397,7 @@ class QInstanceValidatorTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -391,6 +413,7 @@ class QInstanceValidatorTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -408,6 +431,96 @@ class QInstanceValidatorTest
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testPossibleValueSourceMissingType()
|
||||||
|
{
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getPossibleValueSource(TestUtils.POSSIBLE_VALUE_SOURCE_STATE).setType(null),
|
||||||
|
"Missing type for possibleValueSource");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testPossibleValueSourceMissingIdType()
|
||||||
|
{
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getPossibleValueSource(TestUtils.POSSIBLE_VALUE_SOURCE_STATE).setIdType(null),
|
||||||
|
"Missing an idType for possibleValueSource");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testPossibleValueSourceMisConfiguredEnum()
|
||||||
|
{
|
||||||
|
assertValidationFailureReasons((qInstance) -> {
|
||||||
|
QPossibleValueSource possibleValueSource = qInstance.getPossibleValueSource(TestUtils.POSSIBLE_VALUE_SOURCE_STATE);
|
||||||
|
possibleValueSource.setTableName("person");
|
||||||
|
possibleValueSource.setCustomCodeReference(new QCodeReference());
|
||||||
|
possibleValueSource.setEnumValues(null);
|
||||||
|
},
|
||||||
|
"should not have a tableName",
|
||||||
|
"should not have a customCodeReference",
|
||||||
|
"is missing enum values");
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getPossibleValueSource(TestUtils.POSSIBLE_VALUE_SOURCE_STATE).setEnumValues(new ArrayList<>()),
|
||||||
|
"is missing enum values");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testPossibleValueSourceMisConfiguredTable()
|
||||||
|
{
|
||||||
|
assertValidationFailureReasons((qInstance) -> {
|
||||||
|
QPossibleValueSource possibleValueSource = qInstance.getPossibleValueSource(TestUtils.POSSIBLE_VALUE_SOURCE_SHAPE);
|
||||||
|
possibleValueSource.setTableName(null);
|
||||||
|
possibleValueSource.setCustomCodeReference(new QCodeReference());
|
||||||
|
possibleValueSource.setEnumValues(List.of(new QPossibleValue<>("test")));
|
||||||
|
},
|
||||||
|
"should not have enum values",
|
||||||
|
"should not have a customCodeReference",
|
||||||
|
"is missing a tableName");
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getPossibleValueSource(TestUtils.POSSIBLE_VALUE_SOURCE_SHAPE).setTableName("Not a table"),
|
||||||
|
"Unrecognized table");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Test
|
||||||
|
void testPossibleValueSourceMisConfiguredCustom()
|
||||||
|
{
|
||||||
|
assertValidationFailureReasons((qInstance) -> {
|
||||||
|
QPossibleValueSource possibleValueSource = qInstance.getPossibleValueSource(TestUtils.POSSIBLE_VALUE_SOURCE_CUSTOM);
|
||||||
|
possibleValueSource.setTableName("person");
|
||||||
|
possibleValueSource.setCustomCodeReference(null);
|
||||||
|
possibleValueSource.setEnumValues(List.of(new QPossibleValue<>("test")));
|
||||||
|
},
|
||||||
|
"should not have enum values",
|
||||||
|
"should not have a tableName",
|
||||||
|
"is missing a customCodeReference");
|
||||||
|
|
||||||
|
assertValidationFailureReasons((qInstance) -> qInstance.getPossibleValueSource(TestUtils.POSSIBLE_VALUE_SOURCE_CUSTOM).setCustomCodeReference(new QCodeReference()),
|
||||||
|
"not a possibleValueProvider");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** Run a little setup code on a qInstance; then validate it, and assert that it
|
** Run a little setup code on a qInstance; then validate it, and assert that it
|
||||||
** failed validation with reasons that match the supplied vararg-reasons (but allow
|
** failed validation with reasons that match the supplied vararg-reasons (but allow
|
||||||
|
@ -48,6 +48,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
|||||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
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;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
@ -62,8 +63,9 @@ class MemoryBackendModuleTest
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
@BeforeEach
|
||||||
@AfterEach
|
@AfterEach
|
||||||
void afterEach()
|
void beforeAndAfter()
|
||||||
{
|
{
|
||||||
MemoryRecordStore.getInstance().reset();
|
MemoryRecordStore.getInstance().reset();
|
||||||
}
|
}
|
||||||
@ -120,6 +122,8 @@ class MemoryBackendModuleTest
|
|||||||
|
|
||||||
assertEquals(3, new CountAction().execute(countInput).getCount());
|
assertEquals(3, new CountAction().execute(countInput).getCount());
|
||||||
|
|
||||||
|
// todo - filters in query
|
||||||
|
|
||||||
//////////////////
|
//////////////////
|
||||||
// do an update //
|
// do an update //
|
||||||
//////////////////
|
//////////////////
|
||||||
|
@ -22,10 +22,12 @@
|
|||||||
package com.kingsrook.qqq.backend.core.utils;
|
package com.kingsrook.qqq.backend.core.utils;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage.AddAge;
|
import com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage.AddAge;
|
||||||
import com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage.GetAgeStatistics;
|
import com.kingsrook.qqq.backend.core.actions.processes.person.addtopeoplesage.GetAgeStatistics;
|
||||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||||
|
import com.kingsrook.qqq.backend.core.actions.values.QCustomPossibleValueProvider;
|
||||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||||
@ -39,6 +41,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeUsage;
|
|||||||
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.layout.QAppMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
|
||||||
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValue;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSource;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleValueSourceType;
|
||||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||||
@ -80,6 +83,10 @@ public class TestUtils
|
|||||||
public static final String TABLE_NAME_PERSON_FILE = "personFile";
|
public static final String TABLE_NAME_PERSON_FILE = "personFile";
|
||||||
public static final String TABLE_NAME_ID_AND_NAME_ONLY = "idAndNameOnly";
|
public static final String TABLE_NAME_ID_AND_NAME_ONLY = "idAndNameOnly";
|
||||||
|
|
||||||
|
public static final String POSSIBLE_VALUE_SOURCE_STATE = "state"; // enum-type
|
||||||
|
public static final String POSSIBLE_VALUE_SOURCE_SHAPE = "shape"; // table-type
|
||||||
|
public static final String POSSIBLE_VALUE_SOURCE_CUSTOM = "custom"; // custom-type
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -99,6 +106,8 @@ public class TestUtils
|
|||||||
qInstance.addTable(defineTableShape());
|
qInstance.addTable(defineTableShape());
|
||||||
|
|
||||||
qInstance.addPossibleValueSource(defineStatesPossibleValueSource());
|
qInstance.addPossibleValueSource(defineStatesPossibleValueSource());
|
||||||
|
qInstance.addPossibleValueSource(defineShapePossibleValueSource());
|
||||||
|
qInstance.addPossibleValueSource(defineCustomPossibleValueSource());
|
||||||
|
|
||||||
qInstance.addProcess(defineProcessGreetPeople());
|
qInstance.addProcess(defineProcessGreetPeople());
|
||||||
qInstance.addProcess(defineProcessGreetPeopleInteractive());
|
qInstance.addProcess(defineProcessGreetPeopleInteractive());
|
||||||
@ -141,12 +150,40 @@ public class TestUtils
|
|||||||
** Define the "states" possible value source used in standard tests
|
** Define the "states" possible value source used in standard tests
|
||||||
**
|
**
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
private static QPossibleValueSource<String> defineStatesPossibleValueSource()
|
private static QPossibleValueSource defineStatesPossibleValueSource()
|
||||||
{
|
{
|
||||||
return new QPossibleValueSource<String>()
|
return new QPossibleValueSource()
|
||||||
.withName("state")
|
.withName(POSSIBLE_VALUE_SOURCE_STATE)
|
||||||
.withType(QPossibleValueSourceType.ENUM)
|
.withType(QPossibleValueSourceType.ENUM)
|
||||||
.withEnumValues(List.of("IL", "MO"));
|
.withEnumValues(List.of(new QPossibleValue<>(1, "IL"), new QPossibleValue<>(2, "MO")));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Define the "shape" possible value source used in standard tests
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static QPossibleValueSource defineShapePossibleValueSource()
|
||||||
|
{
|
||||||
|
return new QPossibleValueSource()
|
||||||
|
.withName(POSSIBLE_VALUE_SOURCE_SHAPE)
|
||||||
|
.withType(QPossibleValueSourceType.TABLE)
|
||||||
|
.withTableName(TABLE_NAME_SHAPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Define the "custom" possible value source used in standard tests
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
private static QPossibleValueSource defineCustomPossibleValueSource()
|
||||||
|
{
|
||||||
|
return new QPossibleValueSource()
|
||||||
|
.withName(POSSIBLE_VALUE_SOURCE_CUSTOM)
|
||||||
|
.withType(QPossibleValueSourceType.CUSTOM)
|
||||||
|
.withCustomCodeReference(new QCodeReference(CustomPossibleValueSource.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -205,7 +242,10 @@ public class TestUtils
|
|||||||
.withField(new QFieldMetaData("lastName", QFieldType.STRING))
|
.withField(new QFieldMetaData("lastName", QFieldType.STRING))
|
||||||
.withField(new QFieldMetaData("birthDate", QFieldType.DATE))
|
.withField(new QFieldMetaData("birthDate", QFieldType.DATE))
|
||||||
.withField(new QFieldMetaData("email", QFieldType.STRING))
|
.withField(new QFieldMetaData("email", QFieldType.STRING))
|
||||||
.withField(new QFieldMetaData("homeState", QFieldType.STRING).withPossibleValueSourceName("state"));
|
.withField(new QFieldMetaData("homeStateId", QFieldType.INTEGER).withPossibleValueSourceName(POSSIBLE_VALUE_SOURCE_STATE))
|
||||||
|
.withField(new QFieldMetaData("favoriteShapeId", QFieldType.INTEGER).withPossibleValueSourceName(POSSIBLE_VALUE_SOURCE_SHAPE))
|
||||||
|
.withField(new QFieldMetaData("customValue", QFieldType.INTEGER).withPossibleValueSourceName(POSSIBLE_VALUE_SOURCE_CUSTOM))
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -219,6 +259,7 @@ public class TestUtils
|
|||||||
.withName(TABLE_NAME_SHAPE)
|
.withName(TABLE_NAME_SHAPE)
|
||||||
.withBackendName(MEMORY_BACKEND_NAME)
|
.withBackendName(MEMORY_BACKEND_NAME)
|
||||||
.withPrimaryKeyField("id")
|
.withPrimaryKeyField("id")
|
||||||
|
.withRecordLabelFields("name")
|
||||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER).withIsEditable(false))
|
.withField(new QFieldMetaData("id", QFieldType.INTEGER).withIsEditable(false))
|
||||||
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME).withIsEditable(false))
|
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME).withIsEditable(false))
|
||||||
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME).withIsEditable(false))
|
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME).withIsEditable(false))
|
||||||
@ -452,4 +493,21 @@ public class TestUtils
|
|||||||
""");
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
public static class CustomPossibleValueSource implements QCustomPossibleValueProvider
|
||||||
|
{
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
**
|
||||||
|
*******************************************************************************/
|
||||||
|
@Override
|
||||||
|
public QPossibleValue<?> getPossibleValue(Serializable idValue)
|
||||||
|
{
|
||||||
|
return (new QPossibleValue<>(idValue, "Custom[" + idValue + "]"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,8 +60,8 @@
|
|||||||
"type": "STRING",
|
"type": "STRING",
|
||||||
"possibleValueSourceName": null
|
"possibleValueSourceName": null
|
||||||
},
|
},
|
||||||
"homeState": {
|
"homeStateId": {
|
||||||
"name": "homeState",
|
"name": "homeStateId",
|
||||||
"label": null,
|
"label": null,
|
||||||
"backendName": null,
|
"backendName": null,
|
||||||
"type": "STRING",
|
"type": "STRING",
|
||||||
|
@ -27,8 +27,8 @@
|
|||||||
"type": "DATE_TIME",
|
"type": "DATE_TIME",
|
||||||
"possibleValueSourceName": null
|
"possibleValueSourceName": null
|
||||||
},
|
},
|
||||||
"homeState": {
|
"homeStateId": {
|
||||||
"name": "homeState",
|
"name": "homeStateId",
|
||||||
"backendName": null,
|
"backendName": null,
|
||||||
"label": null,
|
"label": null,
|
||||||
"type": "STRING",
|
"type": "STRING",
|
||||||
|
@ -200,6 +200,10 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
|
|||||||
{
|
{
|
||||||
return (QueryManager.getLocalDateTime(resultSet, i));
|
return (QueryManager.getLocalDateTime(resultSet, i));
|
||||||
}
|
}
|
||||||
|
case BOOLEAN:
|
||||||
|
{
|
||||||
|
return (QueryManager.getBoolean(resultSet, i));
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
throw new IllegalStateException("Unexpected field type: " + qFieldMetaData.getType());
|
throw new IllegalStateException("Unexpected field type: " + qFieldMetaData.getType());
|
||||||
|
@ -428,12 +428,14 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
|
|||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
** This doesn't really test any RDBMS code, but is a checkpoint that the core
|
** This doesn't really test any RDBMS code, but is a checkpoint that the core
|
||||||
** module is populating displayValues when it performs the system-level query action.
|
** module is populating displayValues when it performs the system-level query action
|
||||||
|
** (if so requested by input field).
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@Test
|
@Test
|
||||||
public void testThatDisplayValuesGetSetGoingThroughQueryAction() throws QException
|
public void testThatDisplayValuesGetSetGoingThroughQueryAction() throws QException
|
||||||
{
|
{
|
||||||
QueryInput queryInput = initQueryRequest();
|
QueryInput queryInput = initQueryRequest();
|
||||||
|
queryInput.setShouldGenerateDisplayValues(true);
|
||||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||||
Assertions.assertEquals(5, queryOutput.getRecords().size(), "Unfiltered query should find all rows");
|
Assertions.assertEquals(5, queryOutput.getRecords().size(), "Unfiltered query should find all rows");
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ import java.util.Locale;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobManager;
|
import com.kingsrook.qqq.backend.core.actions.async.AsyncJobManager;
|
||||||
import com.kingsrook.qqq.backend.core.actions.metadata.MetaDataAction;
|
import com.kingsrook.qqq.backend.core.actions.metadata.MetaDataAction;
|
||||||
import com.kingsrook.qqq.backend.core.actions.metadata.ProcessMetaDataAction;
|
import com.kingsrook.qqq.backend.core.actions.metadata.ProcessMetaDataAction;
|
||||||
@ -114,6 +115,11 @@ public class QJavalinImplementation
|
|||||||
|
|
||||||
static QInstance qInstance;
|
static QInstance qInstance;
|
||||||
|
|
||||||
|
private static Supplier<QInstance> qInstanceHotSwapSupplier;
|
||||||
|
private static long lastQInstanceHotSwapMillis;
|
||||||
|
|
||||||
|
private static final long MILLIS_BETWEEN_HOT_SWAPS = 2500;
|
||||||
|
|
||||||
private static int DEFAULT_PORT = 8001;
|
private static int DEFAULT_PORT = 8001;
|
||||||
|
|
||||||
private static Javalin service;
|
private static Javalin service;
|
||||||
@ -166,6 +172,44 @@ public class QJavalinImplementation
|
|||||||
// todo base path from arg? - and then potentially multiple instances too (chosen based on the root path??)
|
// todo base path from arg? - and then potentially multiple instances too (chosen based on the root path??)
|
||||||
service = Javalin.create().start(port);
|
service = Javalin.create().start(port);
|
||||||
service.routes(getRoutes());
|
service.routes(getRoutes());
|
||||||
|
service.before(QJavalinImplementation::hotSwapQInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** If there's a qInstanceHotSwapSupplier, and its been a little while, replace
|
||||||
|
** the qInstance with a new one from the supplier. Meant to be used while doing
|
||||||
|
** development.
|
||||||
|
*******************************************************************************/
|
||||||
|
public static void hotSwapQInstance(Context context)
|
||||||
|
{
|
||||||
|
if(qInstanceHotSwapSupplier != null)
|
||||||
|
{
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
if(now - lastQInstanceHotSwapMillis < MILLIS_BETWEEN_HOT_SWAPS)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
lastQInstanceHotSwapMillis = now;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
QInstance newQInstance = qInstanceHotSwapSupplier.get();
|
||||||
|
new QInstanceValidator().validate(newQInstance);
|
||||||
|
QJavalinImplementation.qInstance = newQInstance;
|
||||||
|
LOG.info("Swapped qInstance");
|
||||||
|
}
|
||||||
|
catch(QInstanceValidationException e)
|
||||||
|
{
|
||||||
|
LOG.warn(e.getMessage());
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
LOG.error("Error swapping QInstance", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -429,6 +473,8 @@ public class QJavalinImplementation
|
|||||||
|
|
||||||
setupSession(context, queryInput);
|
setupSession(context, queryInput);
|
||||||
queryInput.setTableName(tableName);
|
queryInput.setTableName(tableName);
|
||||||
|
queryInput.setShouldGenerateDisplayValues(true);
|
||||||
|
queryInput.setShouldTranslatePossibleValues(true);
|
||||||
|
|
||||||
// todo - validate that the primary key is of the proper type (e.g,. not a string for an id field)
|
// todo - validate that the primary key is of the proper type (e.g,. not a string for an id field)
|
||||||
// and throw a 400-series error (tell the user bad-request), rather than, we're doing a 500 (server error)
|
// and throw a 400-series error (tell the user bad-request), rather than, we're doing a 500 (server error)
|
||||||
@ -524,6 +570,8 @@ public class QJavalinImplementation
|
|||||||
QueryInput queryInput = new QueryInput(qInstance);
|
QueryInput queryInput = new QueryInput(qInstance);
|
||||||
setupSession(context, queryInput);
|
setupSession(context, queryInput);
|
||||||
queryInput.setTableName(context.pathParam("table"));
|
queryInput.setTableName(context.pathParam("table"));
|
||||||
|
queryInput.setShouldGenerateDisplayValues(true);
|
||||||
|
queryInput.setShouldTranslatePossibleValues(true);
|
||||||
queryInput.setSkip(integerQueryParam(context, "skip"));
|
queryInput.setSkip(integerQueryParam(context, "skip"));
|
||||||
queryInput.setLimit(integerQueryParam(context, "limit"));
|
queryInput.setLimit(integerQueryParam(context, "limit"));
|
||||||
|
|
||||||
@ -836,4 +884,14 @@ public class QJavalinImplementation
|
|||||||
return (null);
|
return (null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
** Setter for qInstanceHotSwapSupplier
|
||||||
|
*******************************************************************************/
|
||||||
|
public static void setQInstanceHotSwapSupplier(Supplier<QInstance> qInstanceHotSwapSupplier)
|
||||||
|
{
|
||||||
|
QJavalinImplementation.qInstanceHotSwapSupplier = qInstanceHotSwapSupplier;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -536,6 +536,11 @@ public class QPicoCliImplementation
|
|||||||
queryInput.setSession(session);
|
queryInput.setSession(session);
|
||||||
queryInput.setTableName(tableName);
|
queryInput.setTableName(tableName);
|
||||||
queryInput.setSkip(subParseResult.matchedOptionValue("skip", null));
|
queryInput.setSkip(subParseResult.matchedOptionValue("skip", null));
|
||||||
|
|
||||||
|
// todo - think about these (e.g., based on user's requested output format?
|
||||||
|
// queryInput.setShouldGenerateDisplayValues(true);
|
||||||
|
// queryInput.setShouldTranslatePossibleValues(true);
|
||||||
|
|
||||||
String primaryKeyValue = subParseResult.matchedPositionalValue(0, null);
|
String primaryKeyValue = subParseResult.matchedPositionalValue(0, null);
|
||||||
|
|
||||||
if(primaryKeyValue == null)
|
if(primaryKeyValue == null)
|
||||||
@ -581,6 +586,10 @@ public class QPicoCliImplementation
|
|||||||
queryInput.setLimit(subParseResult.matchedOptionValue("limit", null));
|
queryInput.setLimit(subParseResult.matchedOptionValue("limit", null));
|
||||||
queryInput.setFilter(generateQueryFilter(subParseResult));
|
queryInput.setFilter(generateQueryFilter(subParseResult));
|
||||||
|
|
||||||
|
// todo - think about these (e.g., based on user's requested output format?
|
||||||
|
// queryInput.setShouldGenerateDisplayValues(true);
|
||||||
|
// queryInput.setShouldTranslatePossibleValues(true);
|
||||||
|
|
||||||
QueryAction queryAction = new QueryAction();
|
QueryAction queryAction = new QueryAction();
|
||||||
QueryOutput queryOutput = queryAction.execute(queryInput);
|
QueryOutput queryOutput = queryAction.execute(queryInput);
|
||||||
commandLine.getOut().println(JsonUtils.toPrettyJson(queryOutput));
|
commandLine.getOut().println(JsonUtils.toPrettyJson(queryOutput));
|
||||||
|
@ -253,7 +253,7 @@ public class SampleMetaDataProvider
|
|||||||
.withBackendName(RDBMS_BACKEND_NAME)
|
.withBackendName(RDBMS_BACKEND_NAME)
|
||||||
.withPrimaryKeyField("id")
|
.withPrimaryKeyField("id")
|
||||||
.withRecordLabelFormat("%s %s")
|
.withRecordLabelFormat("%s %s")
|
||||||
.withRecordLabelFields(List.of("firstName", "lastName"))
|
.withRecordLabelFields("firstName", "lastName")
|
||||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER).withIsEditable(false))
|
.withField(new QFieldMetaData("id", QFieldType.INTEGER).withIsEditable(false))
|
||||||
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME).withBackendName("create_date").withIsEditable(false))
|
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME).withBackendName("create_date").withIsEditable(false))
|
||||||
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME).withBackendName("modify_date").withIsEditable(false))
|
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME).withBackendName("modify_date").withIsEditable(false))
|
||||||
@ -267,7 +267,7 @@ public class SampleMetaDataProvider
|
|||||||
|
|
||||||
.withSection(new QFieldSection("identity", "Identity", new QIcon("badge"), Tier.T1, List.of("id", "firstName", "lastName")))
|
.withSection(new QFieldSection("identity", "Identity", new QIcon("badge"), Tier.T1, List.of("id", "firstName", "lastName")))
|
||||||
.withSection(new QFieldSection("basicInfo", "Basic Info", new QIcon("dataset"), Tier.T2, List.of("email", "birthDate")))
|
.withSection(new QFieldSection("basicInfo", "Basic Info", new QIcon("dataset"), Tier.T2, List.of("email", "birthDate")))
|
||||||
.withSection(new QFieldSection("employmentInfo", "Employment Info", new QIcon("work"), Tier.T2, List.of("annualSalary", "daysWorked")))
|
.withSection(new QFieldSection("employmentInfo", "Employment Info", new QIcon("work"), Tier.T2, List.of("isEmployed", "annualSalary", "daysWorked")))
|
||||||
.withSection(new QFieldSection("dates", "Dates", new QIcon("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")));
|
.withSection(new QFieldSection("dates", "Dates", new QIcon("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")));
|
||||||
|
|
||||||
QInstanceEnricher.setInferredFieldBackendNames(qTableMetaData);
|
QInstanceEnricher.setInferredFieldBackendNames(qTableMetaData);
|
||||||
|
@ -31,15 +31,16 @@ CREATE TABLE person
|
|||||||
birth_date DATE,
|
birth_date DATE,
|
||||||
email VARCHAR(250) NOT NULL,
|
email VARCHAR(250) NOT NULL,
|
||||||
|
|
||||||
|
is_employed BOOLEAN,
|
||||||
annual_salary DECIMAL(12, 2),
|
annual_salary DECIMAL(12, 2),
|
||||||
days_worked INTEGER
|
days_worked INTEGER
|
||||||
);
|
);
|
||||||
|
|
||||||
INSERT INTO person (id, first_name, last_name, birth_date, email, annual_salary, days_worked) VALUES (1, 'Darin', 'Kelkhoff', '1980-05-31', 'darin.kelkhoff@gmail.com', 75003.50, 1001);
|
INSERT INTO person (id, first_name, last_name, birth_date, email, is_employed, annual_salary, days_worked) VALUES (1, 'Darin', 'Kelkhoff', '1980-05-31', 'darin.kelkhoff@gmail.com', 1, 75003.50, 1001);
|
||||||
INSERT INTO person (id, first_name, last_name, birth_date, email, annual_salary, days_worked) VALUES (2, 'James', 'Maes', '1980-05-15', 'jmaes@mmltholdings.com', 150000, 10100);
|
INSERT INTO person (id, first_name, last_name, birth_date, email, is_employed, annual_salary, days_worked) VALUES (2, 'James', 'Maes', '1980-05-15', 'jmaes@mmltholdings.com', 1, 150000, 10100);
|
||||||
INSERT INTO person (id, first_name, last_name, birth_date, email, annual_salary, days_worked) VALUES (3, 'Tim', 'Chamberlain', '1976-05-28', 'tchamberlain@mmltholdings.com', 300000, 100100);
|
INSERT INTO person (id, first_name, last_name, birth_date, email, is_employed, annual_salary, days_worked) VALUES (3, 'Tim', 'Chamberlain', '1976-05-28', 'tchamberlain@mmltholdings.com', 1, 300000, 100100);
|
||||||
INSERT INTO person (id, first_name, last_name, birth_date, email, annual_salary, days_worked) VALUES (4, 'Tyler', 'Samples', NULL, 'tsamples@mmltholdings.com', 950000, 75);
|
INSERT INTO person (id, first_name, last_name, birth_date, email, is_employed, annual_salary, days_worked) VALUES (4, 'Tyler', 'Samples', NULL, 'tsamples@mmltholdings.com', 1, 950000, 75);
|
||||||
INSERT INTO person (id, first_name, last_name, birth_date, email, annual_salary, days_worked) VALUES (5, 'Garret', 'Richardson', '1981-01-01', 'grichardson@mmltholdings.com', 1500000, 1);
|
INSERT INTO person (id, first_name, last_name, birth_date, email, is_employed, annual_salary, days_worked) VALUES (5, 'Garret', 'Richardson', '1981-01-01', 'grichardson@mmltholdings.com', 0, 1500000, 1);
|
||||||
|
|
||||||
DROP TABLE IF EXISTS carrier;
|
DROP TABLE IF EXISTS carrier;
|
||||||
CREATE TABLE carrier
|
CREATE TABLE carrier
|
||||||
|
Reference in New Issue
Block a user