mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-18 05:01:07 +00:00
Checkpoint - WIP on MetaData action, Insert action, csv/json record adapters
This commit is contained in:
14
pom.xml
14
pom.xml
@ -41,6 +41,11 @@
|
||||
<artifactId>json</artifactId>
|
||||
<version>20210307</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-csv</artifactId>
|
||||
<version>1.8</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Common deps for all qqq modules -->
|
||||
<dependency>
|
||||
@ -122,6 +127,14 @@
|
||||
</build>
|
||||
|
||||
<!--
|
||||
<distributionManagement>
|
||||
<snapshotRepository>
|
||||
<id>nexus-snapshots</id>
|
||||
<url>http://localhost:8088/repository/maven-snapshots</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
-->
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>github</id>
|
||||
@ -129,6 +142,5 @@
|
||||
<url>https://maven.pkg.github.com/Kingsrook/qqq-maven-registry</url>
|
||||
</repository>
|
||||
</distributionManagement>
|
||||
-->
|
||||
|
||||
</project>
|
||||
|
@ -0,0 +1,37 @@
|
||||
package com.kingsrook.qqq.backend.core.actions;
|
||||
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.MetaDataRequest;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.MetaDataResult;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendTableMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class MetaDataAction
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public MetaDataResult execute(MetaDataRequest metaDataRequest) throws QException
|
||||
{
|
||||
// todo pre-customization - just get to modify the request?
|
||||
MetaDataResult metaDataResult = new MetaDataResult();
|
||||
|
||||
Map<String, QFrontendTableMetaData> tables = new LinkedHashMap<>();
|
||||
for(Map.Entry<String, QTableMetaData> entry : metaDataRequest.getInstance().getTables().entrySet())
|
||||
{
|
||||
tables.put(entry.getKey(), new QFrontendTableMetaData(entry.getValue(), false));
|
||||
}
|
||||
|
||||
metaDataResult.setTables(tables);
|
||||
// todo post-customization - can do whatever w/ the result if you want
|
||||
|
||||
return metaDataResult;
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
package com.kingsrook.qqq.backend.core.adapters;
|
||||
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractQFieldMapping;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import org.apache.commons.csv.CSVFormat;
|
||||
import org.apache.commons.csv.CSVParser;
|
||||
import org.apache.commons.csv.CSVRecord;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class CsvToQRecordAdapter
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** todo - meta-data validation, mapping, type handling
|
||||
*******************************************************************************/
|
||||
public List<QRecord> buildRecordsFromCsv(String csv, QTableMetaData table, AbstractQFieldMapping<?> mapping)
|
||||
{
|
||||
if(!StringUtils.hasContent(csv))
|
||||
{
|
||||
throw (new IllegalArgumentException("Empty csv value was provided."));
|
||||
}
|
||||
|
||||
List<QRecord> rs = new ArrayList<>();
|
||||
try
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// if there's no mapping (e.g., table-standard field names), or key-based mapping, then first row is headers //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
if(mapping == null || AbstractQFieldMapping.SourceType.KEY.equals(mapping.getSourceType()))
|
||||
{
|
||||
CSVParser csvParser = new CSVParser(new StringReader(csv),
|
||||
CSVFormat.DEFAULT
|
||||
.withFirstRecordAsHeader()
|
||||
.withIgnoreHeaderCase()
|
||||
.withTrim());
|
||||
|
||||
List<String> headers = csvParser.getHeaderNames();
|
||||
List<CSVRecord> csvRecords = csvParser.getRecords();
|
||||
for(CSVRecord csvRecord : csvRecords)
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// put values from the CSV record into a map of header -> value //
|
||||
//////////////////////////////////////////////////////////////////
|
||||
Map<String, String> csvValues = new HashMap<>();
|
||||
for(String header : headers)
|
||||
{
|
||||
csvValues.put(header, csvRecord.get(header));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// now move values into the QRecord, using the mapping to get the 'header' corresponding to each QField //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QRecord qRecord = new QRecord();
|
||||
rs.add(qRecord);
|
||||
for(QFieldMetaData field : table.getFields().values())
|
||||
{
|
||||
String fieldSource = mapping == null ? field.getName() : String.valueOf(mapping.getFieldSource(field.getName()));
|
||||
qRecord.setValue(field.getName(), csvValues.get(fieldSource));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(AbstractQFieldMapping.SourceType.INDEX.equals(mapping.getSourceType()))
|
||||
{
|
||||
///////////////////////////////
|
||||
// else, index-based mapping //
|
||||
///////////////////////////////
|
||||
CSVParser csvParser = new CSVParser(new StringReader(csv),
|
||||
CSVFormat.DEFAULT
|
||||
.withTrim());
|
||||
|
||||
List<CSVRecord> csvRecords = csvParser.getRecords();
|
||||
for(CSVRecord csvRecord : csvRecords)
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// put values from the CSV record into a map of index -> value //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
Map<Integer, String> csvValues = new HashMap<>();
|
||||
int index = 1;
|
||||
for(String value : csvRecord)
|
||||
{
|
||||
csvValues.put(index++, value);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// now move values into the QRecord, using the mapping to get the 'header' corresponding to each QField //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QRecord qRecord = new QRecord();
|
||||
rs.add(qRecord);
|
||||
for(QFieldMetaData field : table.getFields().values())
|
||||
{
|
||||
Integer fieldIndex = (Integer) mapping.getFieldSource(field.getName());
|
||||
qRecord.setValue(field.getName(), csvValues.get(fieldIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw (new IllegalArgumentException("Unrecognized mapping source type: " + mapping.getSourceType()));
|
||||
}
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
throw (new IllegalArgumentException("Error parsing CSV: " + e.getMessage(), e));
|
||||
}
|
||||
|
||||
return (rs);
|
||||
}
|
||||
|
||||
}
|
@ -2,6 +2,7 @@ package com.kingsrook.qqq.backend.core.adapters;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractQFieldMapping;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.QIndexBasedFieldMapping;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.QKeyBasedFieldMapping;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
@ -33,12 +34,37 @@ public class JsonToQFieldMappingAdapter
|
||||
// look at the keys in the mapping - if they're strings, then we're doing key-based mapping //
|
||||
// if they're numbers, then we're doing index based -- and if they're a mix, that's illegal //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
AbstractQFieldMapping.SourceType sourceType = determineSourceType(jsonObject);
|
||||
|
||||
QKeyBasedFieldMapping mapping = new QKeyBasedFieldMapping();
|
||||
for(String key : jsonObject.keySet())
|
||||
@SuppressWarnings("rawtypes")
|
||||
AbstractQFieldMapping mapping = null;
|
||||
|
||||
switch(sourceType)
|
||||
{
|
||||
mapping.addMapping(key, jsonObject.getString(key));
|
||||
case KEY:
|
||||
{
|
||||
mapping = new QKeyBasedFieldMapping();
|
||||
for(String fieldName : jsonObject.keySet())
|
||||
{
|
||||
((QKeyBasedFieldMapping) mapping).addMapping(fieldName, jsonObject.getString(fieldName));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case INDEX:
|
||||
{
|
||||
mapping = new QIndexBasedFieldMapping();
|
||||
for(String fieldName : jsonObject.keySet())
|
||||
{
|
||||
((QIndexBasedFieldMapping) mapping).addMapping(fieldName, jsonObject.getInt(fieldName));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw (new IllegalArgumentException("Unsupported sourceType: " + sourceType));
|
||||
}
|
||||
}
|
||||
|
||||
return (mapping);
|
||||
}
|
||||
catch(JSONException je)
|
||||
@ -47,4 +73,30 @@ public class JsonToQFieldMappingAdapter
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private AbstractQFieldMapping.SourceType determineSourceType(JSONObject jsonObject)
|
||||
{
|
||||
for(String fieldName : jsonObject.keySet())
|
||||
{
|
||||
Object sourceObject = jsonObject.get(fieldName);
|
||||
if(sourceObject instanceof String)
|
||||
{
|
||||
return (AbstractQFieldMapping.SourceType.KEY);
|
||||
}
|
||||
else if(sourceObject instanceof Integer)
|
||||
{
|
||||
return (AbstractQFieldMapping.SourceType.INDEX);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("Source object is unsupported type: " + sourceObject.getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No fields were found in the mapping.");
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,7 +4,9 @@ package com.kingsrook.qqq.backend.core.adapters;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractQFieldMapping;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import org.json.JSONArray;
|
||||
@ -21,7 +23,7 @@ public class JsonToQRecordAdapter
|
||||
/*******************************************************************************
|
||||
** todo - meta-data validation, mapping, type handling
|
||||
*******************************************************************************/
|
||||
public List<QRecord> buildRecordsFromJson(String json)
|
||||
public List<QRecord> buildRecordsFromJson(String json, QTableMetaData table, AbstractQFieldMapping<?> mapping)
|
||||
{
|
||||
if(!StringUtils.hasContent(json))
|
||||
{
|
||||
|
@ -0,0 +1,82 @@
|
||||
package com.kingsrook.qqq.backend.core.exceptions;
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class QInstanceValidationException extends QException
|
||||
{
|
||||
private List<String> reasons;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QInstanceValidationException(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QInstanceValidationException(List<String> reasons)
|
||||
{
|
||||
super(
|
||||
(reasons != null && reasons.size() > 0)
|
||||
? "Instance validation failed for the following reasons: " + StringUtils.joinWithCommasAndAnd(reasons)
|
||||
: "Validation failed, but no reasons were provided");
|
||||
|
||||
if(reasons != null && reasons.size() > 0)
|
||||
{
|
||||
this.reasons = reasons;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QInstanceValidationException(String... reasons)
|
||||
{
|
||||
super(
|
||||
(reasons != null && reasons.length > 0)
|
||||
? "Instance validation failed for the following reasons: " + StringUtils.joinWithCommasAndAnd(Arrays.stream(reasons).toList())
|
||||
: "Validation failed, but no reasons were provided");
|
||||
|
||||
if(reasons != null && reasons.length > 0)
|
||||
{
|
||||
this.reasons = Arrays.stream(reasons).toList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QInstanceValidationException(String message, Throwable cause)
|
||||
{
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for reasons
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<String> getReasons()
|
||||
{
|
||||
return reasons;
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package com.kingsrook.qqq.backend.core.instances;
|
||||
|
||||
|
||||
import java.util.Locale;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class QInstanceEnricher
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void enrich(QInstance qInstance)
|
||||
{
|
||||
if (qInstance.getTables() != null)
|
||||
{
|
||||
qInstance.getTables().values().forEach(this::enrich);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void enrich(QTableMetaData table)
|
||||
{
|
||||
if(!StringUtils.hasContent(table.getLabel()))
|
||||
{
|
||||
table.setLabel(nameToLabel(table.getName()));
|
||||
}
|
||||
|
||||
if (table.getFields() != null)
|
||||
{
|
||||
table.getFields().values().forEach(this::enrich);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void enrich(QFieldMetaData field)
|
||||
{
|
||||
if(!StringUtils.hasContent(field.getLabel()))
|
||||
{
|
||||
field.setLabel(nameToLabel(field.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String nameToLabel(String name)
|
||||
{
|
||||
if(name == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
return (name.substring(0, 1).toUpperCase(Locale.ROOT) + name.substring(1).replaceAll("([A-Z])", " $1"));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.kingsrook.qqq.backend.core.instances;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public final class QInstanceValidationKey
|
||||
{
|
||||
/*******************************************************************************
|
||||
** package-private constructor, so that only this package can create an instance
|
||||
** of this class, but an instance of this class is required to mark an instance
|
||||
** as validated, so no one can cheat.
|
||||
*******************************************************************************/
|
||||
QInstanceValidationKey()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,104 @@
|
||||
package com.kingsrook.qqq.backend.core.instances;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class QInstanceValidator
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void validate(QInstance qInstance) throws QInstanceValidationException
|
||||
{
|
||||
if(qInstance.getHasBeenValidated())
|
||||
{
|
||||
//////////////////////////////////////////
|
||||
// don't re-validate if previously done //
|
||||
//////////////////////////////////////////
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// before validation, enrich the object (e.g., to fill in values that the user doesn't have to //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
new QInstanceEnricher().enrich(qInstance);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
throw (new QInstanceValidationException("Error enriching qInstance prior to validation.", e));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// do the validation checks - a good qInstance has all conditions TRUE! //
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
List<String> errors = new ArrayList<>();
|
||||
try
|
||||
{
|
||||
if(assertCondition(errors, CollectionUtils.nullSafeHasContents(qInstance.getBackends()), "At least 1 backend must be defined."))
|
||||
{
|
||||
qInstance.getBackends().forEach((backendName, backend) ->
|
||||
{
|
||||
assertCondition(errors, Objects.equals(backendName, backend.getName()), "Inconsistent naming for backend: " + backendName + "/" + backend.getName() + ".");
|
||||
});
|
||||
}
|
||||
|
||||
if(assertCondition(errors, CollectionUtils.nullSafeHasContents(qInstance.getTables()), "At least 1 table must be defined."))
|
||||
{
|
||||
qInstance.getTables().forEach((tableName, table) ->
|
||||
{
|
||||
assertCondition(errors, Objects.equals(tableName, table.getName()), "Inconsistent naming for table: " + tableName + "/" + table.getName() + ".");
|
||||
|
||||
if(assertCondition(errors, StringUtils.hasContent(table.getBackendName()), "Missing backend name for table " + tableName + "."))
|
||||
{
|
||||
if(CollectionUtils.nullSafeHasContents(qInstance.getBackends()))
|
||||
{
|
||||
assertCondition(errors, qInstance.getBackendForTable(tableName) != null, "Unrecognized backend " + table.getBackendName() + " for table " + tableName + ".");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
throw (new QInstanceValidationException("Error performing qInstance validation.", e));
|
||||
}
|
||||
|
||||
if(!errors.isEmpty())
|
||||
{
|
||||
throw (new QInstanceValidationException(errors));
|
||||
}
|
||||
|
||||
qInstance.setHasBeenValidated(new QInstanceValidationKey());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private boolean assertCondition(List<String> errors, boolean condition, String message)
|
||||
{
|
||||
if(!condition)
|
||||
{
|
||||
errors.add(message);
|
||||
}
|
||||
|
||||
return (condition);
|
||||
}
|
||||
|
||||
}
|
@ -2,12 +2,56 @@ package com.kingsrook.qqq.backend.core.model.actions;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** For bulk-loads, define where a QField comes from in an input data source.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public abstract class AbstractQFieldMapping<T>
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
** Returns the fieldName for the input 'key' or 'index'.
|
||||
** Enum to define the types of sources a mapping may use
|
||||
*******************************************************************************/
|
||||
public abstract String getMappedField(T t);
|
||||
@SuppressWarnings("rawtypes")
|
||||
public enum SourceType
|
||||
{
|
||||
KEY(String.class),
|
||||
INDEX(Integer.class);
|
||||
|
||||
private Class sourceClass;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** enum constructor
|
||||
*******************************************************************************/
|
||||
SourceType(Class sourceClass)
|
||||
{
|
||||
this.sourceClass = sourceClass;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for sourceClass
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Class getSourceClass()
|
||||
{
|
||||
return sourceClass;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** For a given field, return its source - a key (e.g., from a json object or csv
|
||||
** with a header row) or an index (for a csv w/o a header)
|
||||
*******************************************************************************/
|
||||
public abstract T getFieldSource(String fieldName);
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** for a mapping instance, get what its source-type is
|
||||
*******************************************************************************/
|
||||
public abstract SourceType getSourceType();
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
package com.kingsrook.qqq.backend.core.model.actions;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceValidator;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -15,13 +16,6 @@ public abstract class AbstractQRequest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public abstract QBackendMetaData getBackend();
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -37,6 +31,23 @@ public abstract class AbstractQRequest
|
||||
public AbstractQRequest(QInstance instance)
|
||||
{
|
||||
this.instance = instance;
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// if this instance hasn't been validated yet, do so now //
|
||||
// noting that this will also enrich any missing metaData //
|
||||
////////////////////////////////////////////////////////////
|
||||
if(! instance.getHasBeenValidated())
|
||||
{
|
||||
try
|
||||
{
|
||||
new QInstanceValidator().validate(instance);
|
||||
}
|
||||
catch(QInstanceValidationException e)
|
||||
{
|
||||
System.err.println(e.getMessage());
|
||||
throw (new IllegalArgumentException("QInstance failed validation" + e.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
package com.kingsrook.qqq.backend.core.model.actions;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
|
||||
|
||||
|
||||
@ -18,13 +18,13 @@ public abstract class AbstractQTableRequest extends AbstractQRequest
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public QBackendMetaData getBackend()
|
||||
{
|
||||
return (instance.getBackendForTable(getTableName()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -2,8 +2,8 @@ package com.kingsrook.qqq.backend.core.model.actions;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
|
@ -0,0 +1,30 @@
|
||||
package com.kingsrook.qqq.backend.core.model.actions;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class MetaDataRequest extends AbstractQRequest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public MetaDataRequest()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public MetaDataRequest(QInstance instance)
|
||||
{
|
||||
super(instance);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package com.kingsrook.qqq.backend.core.model.actions;
|
||||
|
||||
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendTableMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Result for a metaData action
|
||||
*
|
||||
*******************************************************************************/
|
||||
public class MetaDataResult extends AbstractQResult
|
||||
{
|
||||
Map<String, QFrontendTableMetaData> tables;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tables
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, QFrontendTableMetaData> getTables()
|
||||
{
|
||||
return tables;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tables
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setTables(Map<String, QFrontendTableMetaData> tables)
|
||||
{
|
||||
this.tables = tables;
|
||||
}
|
||||
}
|
@ -10,7 +10,7 @@ import java.util.Map;
|
||||
*******************************************************************************/
|
||||
public class QIndexBasedFieldMapping extends AbstractQFieldMapping<Integer>
|
||||
{
|
||||
private Map<Integer, String> mapping;
|
||||
private Map<String, Integer> mapping;
|
||||
|
||||
|
||||
|
||||
@ -18,14 +18,14 @@ public class QIndexBasedFieldMapping extends AbstractQFieldMapping<Integer>
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getMappedField(Integer key)
|
||||
public Integer getFieldSource(String fieldName)
|
||||
{
|
||||
if(mapping == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
return (mapping.get(key));
|
||||
return (mapping.get(fieldName));
|
||||
}
|
||||
|
||||
|
||||
@ -33,13 +33,24 @@ public class QIndexBasedFieldMapping extends AbstractQFieldMapping<Integer>
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addMapping(Integer key, String fieldName)
|
||||
@Override
|
||||
public SourceType getSourceType()
|
||||
{
|
||||
return (SourceType.INDEX);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addMapping(String fieldName, Integer key)
|
||||
{
|
||||
if(mapping == null)
|
||||
{
|
||||
mapping = new LinkedHashMap<>();
|
||||
}
|
||||
mapping.put(key, fieldName);
|
||||
mapping.put(fieldName, key);
|
||||
}
|
||||
|
||||
|
||||
@ -47,9 +58,9 @@ public class QIndexBasedFieldMapping extends AbstractQFieldMapping<Integer>
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QIndexBasedFieldMapping withMapping(Integer key, String fieldName)
|
||||
public QIndexBasedFieldMapping withMapping(String fieldName, Integer key)
|
||||
{
|
||||
addMapping(key, fieldName);
|
||||
addMapping(fieldName, key);
|
||||
return (this);
|
||||
}
|
||||
|
||||
@ -59,7 +70,7 @@ public class QIndexBasedFieldMapping extends AbstractQFieldMapping<Integer>
|
||||
** Getter for mapping
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<Integer, String> getMapping()
|
||||
public Map<String, Integer> getMapping()
|
||||
{
|
||||
return mapping;
|
||||
}
|
||||
@ -70,7 +81,7 @@ public class QIndexBasedFieldMapping extends AbstractQFieldMapping<Integer>
|
||||
** Setter for mapping
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setMapping(Map<Integer, String> mapping)
|
||||
public void setMapping(Map<String, Integer> mapping)
|
||||
{
|
||||
this.mapping = mapping;
|
||||
}
|
||||
|
@ -18,14 +18,14 @@ public class QKeyBasedFieldMapping extends AbstractQFieldMapping<String>
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getMappedField(String key)
|
||||
public String getFieldSource(String fieldName)
|
||||
{
|
||||
if(mapping == null)
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
return (mapping.get(key));
|
||||
return (mapping.get(fieldName));
|
||||
}
|
||||
|
||||
|
||||
@ -33,13 +33,24 @@ public class QKeyBasedFieldMapping extends AbstractQFieldMapping<String>
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addMapping(String key, String fieldName)
|
||||
@Override
|
||||
public SourceType getSourceType()
|
||||
{
|
||||
return (SourceType.KEY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void addMapping(String fieldName, String key)
|
||||
{
|
||||
if(mapping == null)
|
||||
{
|
||||
mapping = new LinkedHashMap<>();
|
||||
}
|
||||
mapping.put(key, fieldName);
|
||||
mapping.put(fieldName, key);
|
||||
}
|
||||
|
||||
|
||||
@ -47,9 +58,9 @@ public class QKeyBasedFieldMapping extends AbstractQFieldMapping<String>
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QKeyBasedFieldMapping withMapping(String key, String fieldName)
|
||||
public QKeyBasedFieldMapping withMapping(String fieldName, String key)
|
||||
{
|
||||
addMapping(key, fieldName);
|
||||
addMapping(fieldName, key);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package com.kingsrook.qqq.backend.core.model.actions;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
|
@ -3,6 +3,7 @@ package com.kingsrook.qqq.backend.core.model.metadata;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import com.fasterxml.jackson.annotation.JsonFilter;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -12,6 +13,8 @@ public class QBackendMetaData
|
||||
{
|
||||
private String name;
|
||||
private String type;
|
||||
|
||||
@JsonFilter("secretsFilter")
|
||||
private Map<String, String> values;
|
||||
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
package com.kingsrook.qqq.backend.core.model;
|
||||
package com.kingsrook.qqq.backend.core.model.metadata;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.kingsrook.qqq.backend.core.instances.QInstanceValidationKey;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -12,9 +12,18 @@ import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
|
||||
*******************************************************************************/
|
||||
public class QInstance
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Do not let the backend data be serialized - e.g., sent to a frontend user //
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@JsonIgnore
|
||||
private Map<String, QBackendMetaData> backends = new HashMap<>();
|
||||
|
||||
private Map<String, QTableMetaData> tables = new HashMap<>();
|
||||
|
||||
// todo - lock down the object (no more changes allowed) after it's been validated?
|
||||
@JsonIgnore
|
||||
private boolean hasBeenValidated = false;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -29,16 +38,27 @@ public class QInstance
|
||||
}
|
||||
|
||||
QBackendMetaData backend = backends.get(table.getBackendName());
|
||||
if(backend == null)
|
||||
{
|
||||
throw (new IllegalArgumentException("Table [" + tableName + "] specified a backend name [" + table.getBackendName() + "] which was found in this instance."));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// validation should already let us know that this is valid, so no need to check/throw here //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
return (backend);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for hasBeenValidated
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setHasBeenValidated(QInstanceValidationKey key)
|
||||
{
|
||||
this.hasBeenValidated = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -140,4 +160,16 @@ public class QInstance
|
||||
{
|
||||
this.tables = tables;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for hasBeenValidated
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean getHasBeenValidated()
|
||||
{
|
||||
return hasBeenValidated;
|
||||
}
|
||||
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@ -13,10 +11,10 @@ import java.util.Map;
|
||||
public class QTableMetaData
|
||||
{
|
||||
private String name;
|
||||
private String label;
|
||||
private String backendName;
|
||||
private String primaryKeyField;
|
||||
private List<QFieldMetaData> fields;
|
||||
private Map<String, QFieldMetaData> _fieldMap;
|
||||
private Map<String, QFieldMetaData> fields;
|
||||
|
||||
|
||||
|
||||
@ -30,7 +28,7 @@ public class QTableMetaData
|
||||
throw (new IllegalArgumentException("Table [" + name + "] does not have its fields defined."));
|
||||
}
|
||||
|
||||
QFieldMetaData field = getFieldMap().get(fieldName);
|
||||
QFieldMetaData field = getFields().get(fieldName);
|
||||
if(field == null)
|
||||
{
|
||||
throw (new IllegalArgumentException("Field [" + fieldName + "] was not found in table [" + name + "]."));
|
||||
@ -41,25 +39,6 @@ public class QTableMetaData
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private Map<String, QFieldMetaData> getFieldMap()
|
||||
{
|
||||
if(_fieldMap == null)
|
||||
{
|
||||
_fieldMap = new LinkedHashMap<>();
|
||||
for(QFieldMetaData field : fields)
|
||||
{
|
||||
_fieldMap.put(field.getName(), field);
|
||||
}
|
||||
}
|
||||
|
||||
return (_fieldMap);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -91,6 +70,37 @@ public class QTableMetaData
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getLabel()
|
||||
{
|
||||
return label;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setLabel(String label)
|
||||
{
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withLabel(String label)
|
||||
{
|
||||
this.label = label;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for backendName
|
||||
**
|
||||
@ -158,7 +168,7 @@ public class QTableMetaData
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public List<QFieldMetaData> getFields()
|
||||
public Map<String, QFieldMetaData> getFields()
|
||||
{
|
||||
return fields;
|
||||
}
|
||||
@ -166,9 +176,10 @@ public class QTableMetaData
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for fields
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setFields(List<QFieldMetaData> fields)
|
||||
public void setFields(Map<String, QFieldMetaData> fields)
|
||||
{
|
||||
this.fields = fields;
|
||||
}
|
||||
@ -178,7 +189,7 @@ public class QTableMetaData
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QTableMetaData withFields(List<QFieldMetaData> fields)
|
||||
public QTableMetaData withFields(Map<String, QFieldMetaData> fields)
|
||||
{
|
||||
this.fields = fields;
|
||||
return (this);
|
||||
@ -193,9 +204,9 @@ public class QTableMetaData
|
||||
{
|
||||
if(this.fields == null)
|
||||
{
|
||||
this.fields = new ArrayList<>();
|
||||
this.fields = new LinkedHashMap<>();
|
||||
}
|
||||
this.fields.add(field);
|
||||
this.fields.put(field.getName(), field);
|
||||
}
|
||||
|
||||
|
||||
@ -207,9 +218,9 @@ public class QTableMetaData
|
||||
{
|
||||
if(this.fields == null)
|
||||
{
|
||||
this.fields = new ArrayList<>();
|
||||
this.fields = new LinkedHashMap<>();
|
||||
}
|
||||
this.fields.add(field);
|
||||
this.fields.put(field.getName(), field);
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,69 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.frontend;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QFieldType;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Version of QTableMetaData that's meant for transmitting to a frontend.
|
||||
* e.g., it excludes backend-only details.
|
||||
*******************************************************************************/
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
public class QFrontendFieldMetaData
|
||||
{
|
||||
private String name;
|
||||
private String label;
|
||||
private QFieldType type;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// do not add setters. take values from the source-object in the constructor!! //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFrontendFieldMetaData(QFieldMetaData fieldMetaData)
|
||||
{
|
||||
this.name = fieldMetaData.getName();
|
||||
this.label = fieldMetaData.getLabel();
|
||||
this.type = fieldMetaData.getType();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for label
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getLabel()
|
||||
{
|
||||
return label;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for type
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFieldType getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.frontend;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
* Version of QTableMetaData that's meant for transmitting to a frontend.
|
||||
* e.g., it excludes backend-only details.
|
||||
*******************************************************************************/
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
public class QFrontendTableMetaData
|
||||
{
|
||||
private String name;
|
||||
private String label;
|
||||
private String primaryKeyField;
|
||||
private Map<String, QFrontendFieldMetaData> fields;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// do not add setters. take values from the source-object in the constructor!! //
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public QFrontendTableMetaData(QTableMetaData tableMetaData, boolean includeFields)
|
||||
{
|
||||
this.name = tableMetaData.getName();
|
||||
this.label = tableMetaData.getLabel();
|
||||
|
||||
if(includeFields)
|
||||
{
|
||||
this.primaryKeyField = tableMetaData.getPrimaryKeyField();
|
||||
this.fields = new HashMap<>();
|
||||
for(Map.Entry<String, QFieldMetaData> entry : tableMetaData.getFields().entrySet())
|
||||
{
|
||||
this.fields.put(entry.getKey(), new QFrontendFieldMetaData(entry.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for name
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for label
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getLabel()
|
||||
{
|
||||
return label;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for primaryKeyField
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getPrimaryKeyField()
|
||||
{
|
||||
return primaryKeyField;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for fields
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Map<String, QFrontendFieldMetaData> getFields()
|
||||
{
|
||||
return fields;
|
||||
}
|
||||
}
|
@ -52,7 +52,7 @@ public class QModuleDispatcher
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw (new QModuleDispatchException("Error getting module", e));
|
||||
throw (new QModuleDispatchException("Error getting q backend module of type: " + backend.getType(), e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,22 @@ public class CollectionUtils
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** true if c is null or it's empty
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static boolean nullSafeIsEmpty(Map c)
|
||||
{
|
||||
if(c == null || c.isEmpty())
|
||||
{
|
||||
return (true);
|
||||
}
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** true if c is NOT null and it's not empty
|
||||
**
|
||||
@ -41,6 +57,17 @@ public class CollectionUtils
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** true if c is NOT null and it's not empty
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static boolean nullSafeHasContents(Map c)
|
||||
{
|
||||
return (!nullSafeIsEmpty(c));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** 0 if c is empty, otherwise, its size.
|
||||
**
|
||||
@ -57,6 +84,22 @@ public class CollectionUtils
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** 0 if c is empty, otherwise, its size.
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static int nullSafeSize(Map c)
|
||||
{
|
||||
if(c == null)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (c.size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -94,6 +94,15 @@ public class JsonUtils
|
||||
ObjectMapper mapper = new ObjectMapper()
|
||||
.registerModule(new JavaTimeModule())
|
||||
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
|
||||
|
||||
/* todo - some future version we may need to do inclusion/exclusion lists like this:
|
||||
// this is what we'd put on the class or member we wanted to 'filter': @JsonFilter("secretsFilter")
|
||||
|
||||
SimpleFilterProvider filterProvider = new SimpleFilterProvider();
|
||||
filterProvider.addFilter("secretsFilter", SimpleBeanPropertyFilter.serializeAllExcept("password"));
|
||||
mapper.setFilterProvider(filterProvider);
|
||||
*/
|
||||
|
||||
return (mapper);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,222 @@
|
||||
package com.kingsrook.qqq.backend.core.adapters;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.QIndexBasedFieldMapping;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.QKeyBasedFieldMapping;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
class CsvToQRecordAdapterTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_buildRecordsFromCsv_nullInput()
|
||||
{
|
||||
testExpectedToThrow(null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_buildRecordsFromCsv_emptyStringInput()
|
||||
{
|
||||
testExpectedToThrow("");
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
/* todo?
|
||||
@Test
|
||||
public void test_buildRecordsFromCsv_inputDoesntLookLikeCsv()
|
||||
{
|
||||
testExpectedToThrow("CSV");
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void testExpectedToThrow(String csv)
|
||||
{
|
||||
try
|
||||
{
|
||||
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
|
||||
List<QRecord> qRecords = csvToQRecordAdapter.buildRecordsFromCsv(csv, TestUtils.defineTablePerson(), null);
|
||||
System.out.println(qRecords);
|
||||
}
|
||||
catch(IllegalArgumentException iae)
|
||||
{
|
||||
System.out.println("Threw expected exception");
|
||||
return;
|
||||
}
|
||||
|
||||
fail("Didn't throw expected exception");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_buildRecordsFromCsv_emptyList()
|
||||
{
|
||||
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
|
||||
List<QRecord> qRecords = csvToQRecordAdapter.buildRecordsFromCsv(getPersonCsvHeader(), TestUtils.defineTablePerson(), null);
|
||||
assertNotNull(qRecords);
|
||||
assertTrue(qRecords.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String getPersonCsvHeader()
|
||||
{
|
||||
return ("""
|
||||
"id","createDate","modifyDate","firstName","lastName","birthDate","email"\r
|
||||
""");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String getPersonCsvRow2()
|
||||
{
|
||||
return ("""
|
||||
"0","2021-10-26 14:39:37","2021-10-26 14:39:37","Jane","Doe","1981-01-01","john@doe.com"\r
|
||||
""");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private String getPersonCsvRow1()
|
||||
{
|
||||
return ("""
|
||||
"0","2021-10-26 14:39:37","2021-10-26 14:39:37","John","Doe","1980-01-01","john@doe.com"\r
|
||||
""");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_buildRecordsFromCsv_oneRowStandardHeaderNoMapping()
|
||||
{
|
||||
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
|
||||
List<QRecord> qRecords = csvToQRecordAdapter.buildRecordsFromCsv(getPersonCsvHeader() + getPersonCsvRow1(), TestUtils.defineTablePerson(), null);
|
||||
assertNotNull(qRecords);
|
||||
assertEquals(1, qRecords.size());
|
||||
QRecord qRecord = qRecords.get(0);
|
||||
assertEquals("John", qRecord.getValue("firstName"));
|
||||
assertEquals("1980-01-01", qRecord.getValue("birthDate"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_buildRecordsFromCsv_twoRowsStandardHeaderNoMapping()
|
||||
{
|
||||
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
|
||||
List<QRecord> qRecords = csvToQRecordAdapter.buildRecordsFromCsv(getPersonCsvHeader() + getPersonCsvRow1() + getPersonCsvRow2(), TestUtils.defineTablePerson(), null);
|
||||
assertNotNull(qRecords);
|
||||
assertEquals(2, qRecords.size());
|
||||
QRecord qRecord1 = qRecords.get(0);
|
||||
assertEquals("John", qRecord1.getValue("firstName"));
|
||||
assertEquals("1980-01-01", qRecord1.getValue("birthDate"));
|
||||
QRecord qRecord2 = qRecords.get(1);
|
||||
assertEquals("Jane", qRecord2.getValue("firstName"));
|
||||
assertEquals("1981-01-01", qRecord2.getValue("birthDate"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_buildRecordsFromCsv_oneRowCustomKeyBasedMapping()
|
||||
{
|
||||
String csvCustomHeader = """
|
||||
"id","created","modified","first","last","birthday","email"\r
|
||||
""";
|
||||
|
||||
QKeyBasedFieldMapping mapping = new QKeyBasedFieldMapping()
|
||||
.withMapping("id", "id")
|
||||
.withMapping("createDate", "created")
|
||||
.withMapping("modifyDate", "modified")
|
||||
.withMapping("firstName", "first")
|
||||
.withMapping("lastName", "last")
|
||||
.withMapping("birthDate", "birthday")
|
||||
.withMapping("email", "email");
|
||||
|
||||
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
|
||||
List<QRecord> qRecords = csvToQRecordAdapter.buildRecordsFromCsv(csvCustomHeader + getPersonCsvRow1(), TestUtils.defineTablePerson(), mapping);
|
||||
assertNotNull(qRecords);
|
||||
assertEquals(1, qRecords.size());
|
||||
QRecord qRecord = qRecords.get(0);
|
||||
assertEquals("John", qRecord.getValue("firstName"));
|
||||
assertEquals("1980-01-01", qRecord.getValue("birthDate"));
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_buildRecordsFromCsv_twoRowsCustomIndexBasedMapping()
|
||||
{
|
||||
int index = 1;
|
||||
QIndexBasedFieldMapping mapping = new QIndexBasedFieldMapping()
|
||||
.withMapping("id", index++)
|
||||
.withMapping("createDate", index++)
|
||||
.withMapping("modifyDate", index++)
|
||||
.withMapping("firstName", index++)
|
||||
.withMapping("lastName", index++)
|
||||
.withMapping("birthDate", index++)
|
||||
.withMapping("email", index++);
|
||||
|
||||
CsvToQRecordAdapter csvToQRecordAdapter = new CsvToQRecordAdapter();
|
||||
List<QRecord> qRecords = csvToQRecordAdapter.buildRecordsFromCsv(getPersonCsvRow1() + getPersonCsvRow2(), TestUtils.defineTablePerson(), mapping);
|
||||
assertNotNull(qRecords);
|
||||
assertEquals(2, qRecords.size());
|
||||
QRecord qRecord1 = qRecords.get(0);
|
||||
assertEquals("John", qRecord1.getValue("firstName"));
|
||||
assertEquals("1980-01-01", qRecord1.getValue("birthDate"));
|
||||
QRecord qRecord2 = qRecords.get(1);
|
||||
assertEquals("Jane", qRecord2.getValue("firstName"));
|
||||
assertEquals("1981-01-01", qRecord2.getValue("birthDate"));
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -2,6 +2,7 @@ package com.kingsrook.qqq.backend.core.adapters;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractQFieldMapping;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.QIndexBasedFieldMapping;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.QKeyBasedFieldMapping;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
@ -51,7 +52,7 @@ class JsonToQFieldMappingAdapterTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_buildMappingFromJson_validInput()
|
||||
public void test_buildMappingFromJson_validKeyBasedInput()
|
||||
{
|
||||
JsonToQFieldMappingAdapter jsonToQFieldMappingAdapter = new JsonToQFieldMappingAdapter();
|
||||
AbstractQFieldMapping<String> mapping = (QKeyBasedFieldMapping) jsonToQFieldMappingAdapter.buildMappingFromJson("""
|
||||
@ -63,9 +64,67 @@ class JsonToQFieldMappingAdapterTest
|
||||
System.out.println(mapping);
|
||||
assertNotNull(mapping);
|
||||
|
||||
// todo - are we backwards here??
|
||||
assertEquals("source1", mapping.getMappedField("Field1"));
|
||||
assertEquals("source2", mapping.getMappedField("Field2"));
|
||||
assertEquals("source1", mapping.getFieldSource("Field1"));
|
||||
assertEquals("source2", mapping.getFieldSource("Field2"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_buildMappingFromJson_validIndexBasedInput()
|
||||
{
|
||||
JsonToQFieldMappingAdapter jsonToQFieldMappingAdapter = new JsonToQFieldMappingAdapter();
|
||||
AbstractQFieldMapping<Integer> mapping = (QIndexBasedFieldMapping) jsonToQFieldMappingAdapter.buildMappingFromJson("""
|
||||
{
|
||||
"Field1": 1,
|
||||
"Field2": 2,
|
||||
}
|
||||
""");
|
||||
System.out.println(mapping);
|
||||
assertNotNull(mapping);
|
||||
|
||||
assertEquals(1, mapping.getFieldSource("Field1"));
|
||||
assertEquals(2, mapping.getFieldSource("Field2"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_buildMappingFromJson_unsupportedTypeForSource()
|
||||
{
|
||||
testExpectedToThrow("""
|
||||
{
|
||||
"Field1": [1, 2],
|
||||
"Field2": {"A": "B"}
|
||||
}
|
||||
""");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_buildMappingFromJson_emptyMapping()
|
||||
{
|
||||
testExpectedToThrow("{}");
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_buildMappingFromJson_inputJsonList()
|
||||
{
|
||||
testExpectedToThrow("[]");
|
||||
}
|
||||
|
||||
|
||||
|
@ -3,6 +3,7 @@ package com.kingsrook.qqq.backend.core.adapters;
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
@ -65,7 +66,7 @@ class JsonToQRecordAdapterTest
|
||||
try
|
||||
{
|
||||
JsonToQRecordAdapter jsonToQRecordAdapter = new JsonToQRecordAdapter();
|
||||
List<QRecord> qRecords = jsonToQRecordAdapter.buildRecordsFromJson(json);
|
||||
List<QRecord> qRecords = jsonToQRecordAdapter.buildRecordsFromJson(json, TestUtils.defineTablePerson(), null);
|
||||
System.out.println(qRecords);
|
||||
}
|
||||
catch(IllegalArgumentException iae)
|
||||
@ -86,7 +87,7 @@ class JsonToQRecordAdapterTest
|
||||
public void test_buildRecordsFromJson_emptyList()
|
||||
{
|
||||
JsonToQRecordAdapter jsonToQRecordAdapter = new JsonToQRecordAdapter();
|
||||
List<QRecord> qRecords = jsonToQRecordAdapter.buildRecordsFromJson("[]");
|
||||
List<QRecord> qRecords = jsonToQRecordAdapter.buildRecordsFromJson("[]", TestUtils.defineTablePerson(), null);
|
||||
assertNotNull(qRecords);
|
||||
assertTrue(qRecords.isEmpty());
|
||||
}
|
||||
@ -105,7 +106,7 @@ class JsonToQRecordAdapterTest
|
||||
"field1":"value1",
|
||||
"field2":"value2"
|
||||
}
|
||||
""");
|
||||
""", TestUtils.defineTablePerson(), null);
|
||||
assertNotNull(qRecords);
|
||||
assertEquals(1, qRecords.size());
|
||||
assertEquals("value1", qRecords.get(0).getValue("field1"));
|
||||
@ -126,7 +127,7 @@ class JsonToQRecordAdapterTest
|
||||
{ "field1":"value1", "field2":"value2" },
|
||||
{ "fieldA":"valueA", "fieldB":"valueB" }
|
||||
]
|
||||
""");
|
||||
""", TestUtils.defineTablePerson(), null);
|
||||
assertNotNull(qRecords);
|
||||
assertEquals(2, qRecords.size());
|
||||
assertEquals("value1", qRecords.get(0).getValue("field1"));
|
||||
|
@ -0,0 +1,188 @@
|
||||
package com.kingsrook.qqq.backend.core.instances;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
class QInstanceValidatorTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_validatePass() throws QInstanceValidationException
|
||||
{
|
||||
new QInstanceValidator().validate(TestUtils.defineInstance());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_validateNullBackends()
|
||||
{
|
||||
try
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
qInstance.setBackends(null);
|
||||
new QInstanceValidator().validate(qInstance);
|
||||
fail("Should have thrown validationException");
|
||||
}
|
||||
catch(QInstanceValidationException e)
|
||||
{
|
||||
assertReason("At least 1 backend must be defined", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_validateEmptyBackends()
|
||||
{
|
||||
try
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
qInstance.setBackends(new HashMap<>());
|
||||
new QInstanceValidator().validate(qInstance);
|
||||
fail("Should have thrown validationException");
|
||||
}
|
||||
catch(QInstanceValidationException e)
|
||||
{
|
||||
assertReason("At least 1 backend must be defined", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_validateNullTables()
|
||||
{
|
||||
try
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
qInstance.setTables(null);
|
||||
new QInstanceValidator().validate(qInstance);
|
||||
fail("Should have thrown validationException");
|
||||
}
|
||||
catch(QInstanceValidationException e)
|
||||
{
|
||||
assertReason("At least 1 table must be defined", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_validateEmptyTables()
|
||||
{
|
||||
try
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
qInstance.setTables(new HashMap<>());
|
||||
new QInstanceValidator().validate(qInstance);
|
||||
fail("Should have thrown validationException");
|
||||
}
|
||||
catch(QInstanceValidationException e)
|
||||
{
|
||||
assertReason("At least 1 table must be defined", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_validateInconsistentNames()
|
||||
{
|
||||
try
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
qInstance.getTable("person").setName("notPerson");
|
||||
qInstance.getBackend("default").setName("notDefault");
|
||||
new QInstanceValidator().validate(qInstance);
|
||||
fail("Should have thrown validationException");
|
||||
}
|
||||
catch(QInstanceValidationException e)
|
||||
{
|
||||
assertReason("Inconsistent naming for table", e);
|
||||
assertReason("Inconsistent naming for backend", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_validateTableWithoutBackend()
|
||||
{
|
||||
try
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
qInstance.getTable("person").setBackendName(null);
|
||||
new QInstanceValidator().validate(qInstance);
|
||||
fail("Should have thrown validationException");
|
||||
}
|
||||
catch(QInstanceValidationException e)
|
||||
{
|
||||
assertReason("Missing backend name for table", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
public void test_validateTableWithMissingBackend()
|
||||
{
|
||||
try
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
qInstance.getTable("person").setBackendName("notARealBackend");
|
||||
new QInstanceValidator().validate(qInstance);
|
||||
fail("Should have thrown validationException");
|
||||
}
|
||||
catch(QInstanceValidationException e)
|
||||
{
|
||||
assertReason("Unrecognized backend", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void assertReason(String reason, QInstanceValidationException e)
|
||||
{
|
||||
assertNotNull(e.getReasons());
|
||||
assertTrue(e.getReasons().stream().anyMatch(s -> s.contains(reason)));
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package com.kingsrook.qqq.backend.core.utils;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QFieldMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QFieldType;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QTableMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class TestUtils
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QInstance defineInstance()
|
||||
{
|
||||
QInstance qInstance = new QInstance();
|
||||
qInstance.addBackend(defineBackend());
|
||||
qInstance.addTable(defineTablePerson());
|
||||
return (qInstance);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QBackendMetaData defineBackend()
|
||||
{
|
||||
return new QBackendMetaData()
|
||||
.withName("default")
|
||||
.withType("rdbms")
|
||||
.withValue("vendor", "h2")
|
||||
.withValue("hostName", "mem")
|
||||
.withValue("databaseName", "test_database")
|
||||
.withValue("username", "sa")
|
||||
.withValue("password", "");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QTableMetaData defineTablePerson()
|
||||
{
|
||||
return new QTableMetaData()
|
||||
.withName("person")
|
||||
.withLabel("Person")
|
||||
.withBackendName(defineBackend().getName())
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME))
|
||||
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME))
|
||||
.withField(new QFieldMetaData("firstName", QFieldType.STRING))
|
||||
.withField(new QFieldMetaData("lastName", QFieldType.STRING))
|
||||
.withField(new QFieldMetaData("birthDate", QFieldType.DATE))
|
||||
.withField(new QFieldMetaData("email", QFieldType.STRING));
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user