Importing initial codes / checkpoint

This commit is contained in:
Darin Kelkhoff
2021-10-25 22:44:00 -05:00
parent eeedf9c36c
commit 206d3a3e75
40 changed files with 3912 additions and 0 deletions

242
checkstyle.xml Normal file
View File

@ -0,0 +1,242 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://checkstyle.sourceforge.net/dtds/configuration_1_3.dtd">
<!--
See reference at https://checkstyle.sourceforge.io/checks.html
-->
<module name = "Checker">
<property name="charset" value="UTF-8"/>
<property name="severity" value="warning"/>
<property name="fileExtensions" value="java, properties, xml"/>
<!-- Checks for whitespace -->
<!-- See http://checkstyle.sf.net/config_whitespace.html -->
<module name="FileTabCharacter">
<property name="eachLine" value="true"/>
</module>
<!--
<module name="LineLength">
<property name="max" value="100"/>
<property name="ignorePattern" value="^package.*|^import.*|a href|href|http://|https://|ftp://"/>
</module>
-->
<module name="TreeWalker">
<module name="OuterTypeFilename"/>
<module name="IllegalTokenText">
<property name="tokens" value="STRING_LITERAL, CHAR_LITERAL"/>
<property name="format" value="\\u00(09|0(a|A)|0(c|C)|0(d|D)|22|27|5(C|c))|\\(0(10|11|12|14|15|42|47)|134)"/>
<property name="message" value="Consider using special escape sequence instead of octal value or Unicode escaped value."/>
</module>
<module name="AvoidEscapedUnicodeCharacters">
<property name="allowEscapesForControlCharacters" value="true"/>
<property name="allowByTailComment" value="true"/>
<property name="allowNonPrintableEscapes" value="true"/>
</module>
<module name="AvoidStarImport"/>
<module name="OneTopLevelClass"/>
<module name="NoLineWrap"/>
<module name="EmptyBlock">
<property name="option" value="TEXT"/>
<property name="tokens" value="LITERAL_TRY, LITERAL_FINALLY, LITERAL_IF, LITERAL_ELSE, LITERAL_SWITCH"/>
</module>
<module name="NeedBraces"/>
<module name="LeftCurly">
<property name="option" value="nl"/>
</module>
<module name="RightCurly">
<property name="option" value="alone"/>
</module>
<!--
<module name="WhitespaceAround">
<property name="allowEmptyConstructors" value="true"/>
<property name="allowEmptyMethods" value="true"/>
<property name="allowEmptyTypes" value="true"/>
<property name="allowEmptyLoops" value="true"/>
<message key="ws.notFollowed"
value="WhitespaceAround: ''{0}'' is not followed by whitespace. Empty blocks may only be represented as '{}' when not part of a multi-block statement (4.1.3)"/>
<message key="ws.notPreceded"
value="WhitespaceAround: ''{0}'' is not preceded with whitespace."/>
</module>
-->
<module name="OneStatementPerLine"/>
<module name="MultipleVariableDeclarations"/>
<module name="ArrayTypeStyle"/>
<module name="MissingSwitchDefault"/>
<module name="FallThrough"/>
<module name="UpperEll"/>
<module name="ModifierOrder"/>
<module name="EmptyLineSeparator">
<property name="allowNoEmptyLineBetweenFields" value="true"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapDot"/>
<property name="tokens" value="DOT"/>
<property name="option" value="nl"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapComma"/>
<property name="tokens" value="COMMA"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<!-- ELLIPSIS is EOL until https://github.com/google/styleguide/issues/258 -->
<property name="id" value="SeparatorWrapEllipsis"/>
<property name="tokens" value="ELLIPSIS"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<!-- ARRAY_DECLARATOR is EOL until https://github.com/google/styleguide/issues/259 -->
<property name="id" value="SeparatorWrapArrayDeclarator"/>
<property name="tokens" value="ARRAY_DECLARATOR"/>
<property name="option" value="EOL"/>
</module>
<module name="SeparatorWrap">
<property name="id" value="SeparatorWrapMethodRef"/>
<property name="tokens" value="METHOD_REF"/>
<property name="option" value="nl"/>
</module>
<module name="PackageName">
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
<message key="name.invalidPattern"
value="Package name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="TypeName">
<message key="name.invalidPattern"
value="Type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MemberName">
<property name="format" value="^[_a-zA-Z]([a-z0-9A-Z][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern" value="Member name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ParameterName">
<property name="format" value="^[a-zA-Z]([a-z0-9A-Z][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern" value="Parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="CatchParameterName">
<property name="format" value="^[a-zA-Z]([a-z0-9A-Z][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern" value="Catch parameter name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="LocalVariableName">
<property name="tokens" value="VARIABLE_DEF"/>
<property name="format" value="^[a-zA-Z]([a-z0-9A-Z][a-zA-Z0-9]*)?$"/>
<message key="name.invalidPattern" value="Local variable name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="ClassTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][_A-Z0-9]*$)"/>
<message key="name.invalidPattern" value="Class type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="MethodTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][_A-Z0-9]*$)"/>
<message key="name.invalidPattern" value="Method type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="InterfaceTypeParameterName">
<property name="format" value="(^[A-Z][0-9]?)$|([A-Z][_A-Z0-9]*$)"/>
<message key="name.invalidPattern" value="Interface type name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="NoFinalizer"/>
<module name="GenericWhitespace">
<message key="ws.followed" value="GenericWhitespace ''{0}'' is followed by whitespace."/>
<message key="ws.preceded" value="GenericWhitespace ''{0}'' is preceded with whitespace."/>
<message key="ws.illegalFollow" value="GenericWhitespace ''{0}'' should followed by whitespace."/>
<message key="ws.notPreceded" value="GenericWhitespace ''{0}'' is not preceded with whitespace."/>
</module>
<module name="Indentation">
<property name="basicOffset" value="3"/>
<property name="braceAdjustment" value="0"/>
<property name="caseIndent" value="3"/>
<property name="throwsIndent" value="6"/>
<property name="lineWrappingIndentation" value="3"/>
<property name="arrayInitIndent" value="2"/>
</module>
<!--
<module name="AbbreviationAsWordInName">
<property name="ignoreFinal" value="false"/>
<property name="allowedAbbreviationLength" value="1"/>
</module>
-->
<module name="OverloadMethodsDeclarationOrder"/>
<module name="VariableDeclarationUsageDistance"/>
<!--
<module name="CustomImportOrder">
<property name="sortImportsInGroupAlphabetically" value="true"/>
<property name="separateLineBetweenGroups" value="true"/>
<property name="customImportOrderRules" value="STATIC###THIRD_PARTY_PACKAGE"/>
</module>
-->
<module name="CustomImportOrder">
<property name="customImportOrderRules" value="SPECIAL_IMPORTS###STANDARD_JAVA_PACKAGE###THIRD_PARTY_PACKAGE###STATIC"/>
<property name="specialImportsRegExp" value="^javax\."/>
<property name="standardPackageRegExp" value="^java\."/>
<property name="sortImportsInGroupAlphabetically" value="true"/>
<property name="separateLineBetweenGroups" value="false"/>
</module>
<module name="MethodParamPad"/>
<module name="NoWhitespaceBefore">
<property name="tokens" value="COMMA, SEMI, POST_INC, POST_DEC, DOT, ELLIPSIS, METHOD_REF"/>
<property name="allowLineBreaks" value="true"/>
</module>
<module name="ParenPad"/>
<module name="OperatorWrap">
<property name="option" value="NL"/>
<property name="tokens" value="BAND, BOR, BSR, BXOR, DIV, EQUAL, GE, GT, LAND, LE, LITERAL_INSTANCEOF, LOR, LT, MINUS, MOD, NOT_EQUAL, PLUS, QUESTION, SL, SR, STAR, METHOD_REF "/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationMostCases"/>
<property name="tokens" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF"/>
</module>
<module name="AnnotationLocation">
<property name="id" value="AnnotationLocationVariables"/>
<property name="tokens" value="VARIABLE_DEF"/>
<property name="allowSamelineMultipleAnnotations" value="true"/>
</module>
<module name="NonEmptyAtclauseDescription"/>
<!-- <module name="JavadocTagContinuationIndentation"/> -->
<!--
<module name="SummaryJavadoc">
<property name="forbiddenSummaryFragments" value="^@return the *|^This method returns |^A [{]@code [a-zA-Z0-9]+[}]( is a )"/>
</module>
-->
<!-- <module name="JavadocParagraph"/> -->
<module name="AtclauseOrder">
<property name="tagOrder" value="@param, @return, @throws, @deprecated"/>
<property name="target" value="CLASS_DEF, INTERFACE_DEF, ENUM_DEF, METHOD_DEF, CTOR_DEF, VARIABLE_DEF"/>
</module>
<module name="JavadocMethod">
<property name="allowMissingParamTags" value="true"/>
<property name="allowMissingReturnTag" value="true"/>
<property name="allowedAnnotations" value="Override, Test"/>
</module>
<module name="MissingJavadocMethod">
<property name="scope" value="private"/>
</module>
<module name="MethodName">
<property name="format" value="^[a-z][a-zA-Z0-9_]*$"/>
<message key="name.invalidPattern"
value="Method name ''{0}'' must match pattern ''{1}''."/>
</module>
<module name="SingleLineJavadoc">
<property name="ignoreInlineTags" value="false"/>
</module>
<module name="MagicNumber">
<property name="severity" value="info"/>
<property name="tokens" value="NUM_DOUBLE, NUM_FLOAT, NUM_INT"/>
<property name="ignoreNumbers" value="0, 1, 2, 3, 4, 5, 6, 7, 8"/>
<property name="ignoreFieldDeclaration" value="true"/>
<property name="ignoreAnnotation" value="true"/>
</module>
<module name="EmptyCatchBlock">
<property name="exceptionVariableName" value="expected"/>
</module>
<module name="CommentsIndentation"/>
<!-- this would be nice, but requires the the @inheritDoc javadoc tag... -->
<module name="MissingOverride"/>
</module>
</module>

134
pom.xml Normal file
View File

@ -0,0 +1,134 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.kingsrook.qqq</groupId>
<artifactId>qqq-backend-core</artifactId>
<version>0.0-SNAPSHOT</version>
<properties>
<!-- props specifically to this module -->
<!-- none at this time -->
<!-- Common props for all qqq modules -->
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.showDeprecation>true</maven.compiler.showDeprecation>
<maven.compiler.showWarnings>true</maven.compiler.showWarnings>
</properties>
<dependencies>
<!-- other qqq modules deps -->
<!-- none, this is core. -->
<!-- 3rd party deps specifically for this module -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20210307</version>
</dependency>
<!-- Common deps for all qqq modules -->
<dependency>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.14.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.14.1</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- plugins specifically for this module -->
<!-- none at this time -->
<!-- Common plugins for all qqq modules -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<compilerArgument>-Xlint:unchecked</compilerArgument>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>3.1.2</version>
<dependencies>
<dependency>
<groupId>com.puppycrawl.tools</groupId>
<artifactId>checkstyle</artifactId>
<version>9.0</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>validate</id>
<phase>validate</phase>
<configuration>
<configLocation>checkstyle.xml</configLocation>
<!-- <suppressionsLocation>checkstyle-suppressions.xml</suppressionsLocation> -->
<encoding>UTF-8</encoding>
<consoleOutput>true</consoleOutput>
<failsOnError>false</failsOnError>
<failOnViolation>true</failOnViolation>
<violationSeverity>warning</violationSeverity>
<excludes>**/target/generated-sources/*.*</excludes>
<!-- <linkXRef>false</linkXRef> -->
</configuration>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<!--
<distributionManagement>
<repository>
<id>github</id>
<name>GitHub kingsrook Maven Packages</name>
<url>https://maven.pkg.github.com/Kingsrook/qqq-maven-registry</url>
</repository>
</distributionManagement>
-->
</project>

View File

@ -0,0 +1,32 @@
package com.kingsrook.qqq.backend.core.actions;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.InsertRequest;
import com.kingsrook.qqq.backend.core.model.actions.InsertResult;
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
import com.kingsrook.qqq.backend.core.modules.QModuleDispatcher;
import com.kingsrook.qqq.backend.core.modules.interfaces.QModuleInterface;
/*******************************************************************************
**
*******************************************************************************/
public class InsertAction
{
/*******************************************************************************
**
*******************************************************************************/
public InsertResult execute(InsertRequest insertRequest) throws QException
{
QModuleDispatcher qModuleDispatcher = new QModuleDispatcher();
QBackendMetaData backend = insertRequest.getBackend();
QModuleInterface qModule = qModuleDispatcher.getQModule(backend);
// todo pre-customization - just get to modify the request?
InsertResult insertResult = qModule.getInsertInterface().execute(insertRequest);
// todo post-customization - can do whatever w/ the result if you want
return insertResult;
}
}

View File

@ -0,0 +1,32 @@
package com.kingsrook.qqq.backend.core.actions;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.QueryRequest;
import com.kingsrook.qqq.backend.core.model.actions.QueryResult;
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
import com.kingsrook.qqq.backend.core.modules.QModuleDispatcher;
import com.kingsrook.qqq.backend.core.modules.interfaces.QModuleInterface;
/*******************************************************************************
**
*******************************************************************************/
public class QueryAction
{
/*******************************************************************************
**
*******************************************************************************/
public QueryResult execute(QueryRequest queryRequest) throws QException
{
QModuleDispatcher qModuleDispatcher = new QModuleDispatcher();
QBackendMetaData backend = queryRequest.getBackend();
QModuleInterface qModule = qModuleDispatcher.getQModule(backend);
// todo pre-customization - just get to modify the request?
QueryResult queryResult = qModule.getQueryInterface().execute(queryRequest);
// todo post-customization - can do whatever w/ the result if you want
return queryResult;
}
}

View File

@ -0,0 +1,50 @@
package com.kingsrook.qqq.backend.core.adapters;
import com.kingsrook.qqq.backend.core.model.actions.AbstractQFieldMapping;
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;
import org.json.JSONException;
import org.json.JSONObject;
/*******************************************************************************
**
*******************************************************************************/
public class JsonToQFieldMappingAdapter
{
/*******************************************************************************
**
*******************************************************************************/
public AbstractQFieldMapping<?> buildMappingFromJson(String json)
{
if(!StringUtils.hasContent(json))
{
throw (new IllegalArgumentException("Empty json value was provided."));
}
try
{
JSONObject jsonObject = JsonUtils.toJSONObject(json);
//////////////////////////////////////////////////////////////////////////////////////////////
// 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 //
//////////////////////////////////////////////////////////////////////////////////////////////
QKeyBasedFieldMapping mapping = new QKeyBasedFieldMapping();
for(String key : jsonObject.keySet())
{
mapping.addMapping(key, jsonObject.getString(key));
}
return (mapping);
}
catch(JSONException je)
{
throw (new IllegalArgumentException("Malformed JSON value: " + je.getMessage(), je));
}
}
}

View File

@ -0,0 +1,84 @@
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.data.QRecord;
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/*******************************************************************************
**
*******************************************************************************/
public class JsonToQRecordAdapter
{
/*******************************************************************************
** todo - meta-data validation, mapping, type handling
*******************************************************************************/
public List<QRecord> buildRecordsFromJson(String json)
{
if(!StringUtils.hasContent(json))
{
throw (new IllegalArgumentException("Empty json value was provided."));
}
List<QRecord> rs = new ArrayList<>();
try
{
if(JsonUtils.looksLikeObject(json))
{
JSONObject jsonObject = JsonUtils.toJSONObject(json);
rs.add(buildRecordFromJsonObject(jsonObject));
}
else if(JsonUtils.looksLikeArray(json))
{
JSONArray jsonArray = JsonUtils.toJSONArray(json);
for(Object object : jsonArray)
{
if(object instanceof JSONObject jsonObject)
{
rs.add(buildRecordFromJsonObject(jsonObject));
}
else
{
throw (new IllegalArgumentException("Element at index " + rs.size() + " in json array was not a json object."));
}
}
}
else
{
throw (new IllegalArgumentException("Malformed JSON value - did not start with '{' or '['."));
}
}
catch(JSONException je)
{
throw (new IllegalArgumentException("Malformed JSON value: " + je.getMessage(), je));
}
return (rs);
}
/*******************************************************************************
** todo - meta-data validation, mapping, type handling
*******************************************************************************/
private QRecord buildRecordFromJsonObject(JSONObject jsonObject)
{
QRecord record = new QRecord();
for(String key : jsonObject.keySet())
{
record.setValue(key, (Serializable) jsonObject.get(key));
}
return (record);
}
}

View File

@ -0,0 +1,27 @@
package com.kingsrook.qqq.backend.core.exceptions;
/*******************************************************************************
* Base class for checked exceptions thrown in qqq.
*
*******************************************************************************/
public class QException extends Exception
{
/*******************************************************************************
**
*******************************************************************************/
public QException(String message)
{
super(message);
}
/*******************************************************************************
**
*******************************************************************************/
public QException(String message, Throwable cause)
{
super(message, cause);
}
}

View File

@ -0,0 +1,27 @@
package com.kingsrook.qqq.backend.core.exceptions;
/*******************************************************************************
* Base class for checked exceptions thrown in qqq.
*
*******************************************************************************/
public class QModuleDispatchException extends QException
{
/*******************************************************************************
**
*******************************************************************************/
public QModuleDispatchException(String message)
{
super(message);
}
/*******************************************************************************
**
*******************************************************************************/
public QModuleDispatchException(String message, Throwable cause)
{
super(message, cause);
}
}

View File

@ -0,0 +1,143 @@
package com.kingsrook.qqq.backend.core.model;
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;
/*******************************************************************************
**
*******************************************************************************/
public class QInstance
{
private Map<String, QBackendMetaData> backends = new HashMap<>();
private Map<String, QTableMetaData> tables = new HashMap<>();
/*******************************************************************************
**
*******************************************************************************/
public QBackendMetaData getBackendForTable(String tableName)
{
QTableMetaData table = tables.get(tableName);
if(table == null)
{
throw (new IllegalArgumentException("No table with name [" + tableName + "] found in this instance."));
}
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."));
}
return (backend);
}
/*******************************************************************************
**
*******************************************************************************/
public void addBackend(QBackendMetaData backend)
{
this.backends.put(backend.getName(), backend);
}
/*******************************************************************************
**
*******************************************************************************/
public void addBackend(String name, QBackendMetaData backend)
{
this.backends.put(name, backend);
}
/*******************************************************************************
**
*******************************************************************************/
public QBackendMetaData getBackend(String name)
{
return (this.backends.get(name));
}
/*******************************************************************************
**
*******************************************************************************/
public void addTable(QTableMetaData table)
{
this.tables.put(table.getName(), table);
}
/*******************************************************************************
**
*******************************************************************************/
public void addTable(String name, QTableMetaData table)
{
this.tables.put(name, table);
}
/*******************************************************************************
**
*******************************************************************************/
public QTableMetaData getTable(String name)
{
return (this.tables.get(name));
}
/*******************************************************************************
** Getter for backends
**
*******************************************************************************/
public Map<String, QBackendMetaData> getBackends()
{
return backends;
}
/*******************************************************************************
** Setter for backends
**
*******************************************************************************/
public void setBackends(Map<String, QBackendMetaData> backends)
{
this.backends = backends;
}
/*******************************************************************************
** Getter for tables
**
*******************************************************************************/
public Map<String, QTableMetaData> getTables()
{
return tables;
}
/*******************************************************************************
** Setter for tables
**
*******************************************************************************/
public void setTables(Map<String, QTableMetaData> tables)
{
this.tables = tables;
}
}

View File

@ -0,0 +1,13 @@
package com.kingsrook.qqq.backend.core.model.actions;
/*******************************************************************************
**
*******************************************************************************/
public abstract class AbstractQFieldMapping<T>
{
/*******************************************************************************
** Returns the fieldName for the input 'key' or 'index'.
*******************************************************************************/
public abstract String getMappedField(T t);
}

View File

@ -0,0 +1,63 @@
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;
/*******************************************************************************
**
*******************************************************************************/
public abstract class AbstractQRequest
{
protected QInstance instance;
// todo session
/*******************************************************************************
**
*******************************************************************************/
public abstract QBackendMetaData getBackend();
/*******************************************************************************
**
*******************************************************************************/
public AbstractQRequest()
{
}
/*******************************************************************************
**
*******************************************************************************/
public AbstractQRequest(QInstance instance)
{
this.instance = instance;
}
/*******************************************************************************
** Getter for instance
**
*******************************************************************************/
public QInstance getInstance()
{
return instance;
}
/*******************************************************************************
** Setter for instance
**
*******************************************************************************/
public void setInstance(QInstance instance)
{
this.instance = instance;
}
}

View File

@ -0,0 +1,17 @@
package com.kingsrook.qqq.backend.core.model.actions;
/*******************************************************************************
**
*******************************************************************************/
public abstract class AbstractQResult
{
// todo - status codes?
/*******************************************************************************
**
*******************************************************************************/
public AbstractQResult()
{
}
}

View File

@ -0,0 +1,76 @@
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.QTableMetaData;
/*******************************************************************************
**
*******************************************************************************/
public abstract class AbstractQTableRequest extends AbstractQRequest
{
private String tableName;
/*******************************************************************************
**
*******************************************************************************/
@Override
public QBackendMetaData getBackend()
{
return (instance.getBackendForTable(getTableName()));
}
/*******************************************************************************
**
*******************************************************************************/
public QTableMetaData getTable()
{
return (instance.getTable(getTableName()));
}
/*******************************************************************************
**
*******************************************************************************/
public AbstractQTableRequest()
{
}
/*******************************************************************************
**
*******************************************************************************/
public AbstractQTableRequest(QInstance instance)
{
super(instance);
}
/*******************************************************************************
** Getter for tableName
**
*******************************************************************************/
public String getTableName()
{
return tableName;
}
/*******************************************************************************
** Setter for tableName
**
*******************************************************************************/
public void setTableName(String tableName)
{
this.tableName = tableName;
}
}

View File

@ -0,0 +1,56 @@
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;
/*******************************************************************************
**
*******************************************************************************/
public class InsertRequest extends AbstractQTableRequest
{
private List<QRecord> records;
/*******************************************************************************
**
*******************************************************************************/
public InsertRequest()
{
}
/*******************************************************************************
**
*******************************************************************************/
public InsertRequest(QInstance instance)
{
super(instance);
}
/*******************************************************************************
** Getter for records
**
*******************************************************************************/
public List<QRecord> getRecords()
{
return records;
}
/*******************************************************************************
** Setter for records
**
*******************************************************************************/
public void setRecords(List<QRecord> records)
{
this.records = records;
}
}

View File

@ -0,0 +1,35 @@
package com.kingsrook.qqq.backend.core.model.actions;
import java.util.List;
import com.kingsrook.qqq.backend.core.model.data.QRecordWithStatus;
/*******************************************************************************
* Result for a query action
*
*******************************************************************************/
public class InsertResult extends AbstractQResult
{
List<QRecordWithStatus> records;
/*******************************************************************************
**
*******************************************************************************/
public List<QRecordWithStatus> getRecords()
{
return records;
}
/*******************************************************************************
**
*******************************************************************************/
public void setRecords(List<QRecordWithStatus> records)
{
this.records = records;
}
}

View File

@ -0,0 +1,12 @@
package com.kingsrook.qqq.backend.core.model.actions;
/*******************************************************************************
**
*******************************************************************************/
public enum QCriteriaOperator
{
EQUALS,
NOT_EQUALS,
IN
}

View File

@ -0,0 +1,118 @@
package com.kingsrook.qqq.backend.core.model.actions;
import java.io.Serializable;
import java.util.List;
/*******************************************************************************
* Component of a Query
*
*******************************************************************************/
public class QFilterCriteria
{
private String fieldName;
private QCriteriaOperator operator;
private List<Serializable> values;
/*******************************************************************************
** Getter for fieldName
**
*******************************************************************************/
public String getFieldName()
{
return fieldName;
}
/*******************************************************************************
** Setter for fieldName
**
*******************************************************************************/
public void setFieldName(String fieldName)
{
this.fieldName = fieldName;
}
/*******************************************************************************
** Setter for fieldName
**
*******************************************************************************/
public QFilterCriteria withFieldName(String fieldName)
{
this.fieldName = fieldName;
return this;
}
/*******************************************************************************
** Getter for operator
**
*******************************************************************************/
public QCriteriaOperator getOperator()
{
return operator;
}
/*******************************************************************************
** Setter for operator
**
*******************************************************************************/
public void setOperator(QCriteriaOperator operator)
{
this.operator = operator;
}
/*******************************************************************************
** Setter for operator
**
*******************************************************************************/
public QFilterCriteria withOperator(QCriteriaOperator operator)
{
this.operator = operator;
return this;
}
/*******************************************************************************
** Getter for values
**
*******************************************************************************/
public List<Serializable> getValues()
{
return values;
}
/*******************************************************************************
** Setter for values
**
*******************************************************************************/
public void setValues(List<Serializable> values)
{
this.values = values;
}
/*******************************************************************************
** Setter for values
**
*******************************************************************************/
public QFilterCriteria withValues(List<Serializable> values)
{
this.values = values;
return this;
}
}

View File

@ -0,0 +1,55 @@
package com.kingsrook.qqq.backend.core.model.actions;
/*******************************************************************************
**
*******************************************************************************/
public class QFilterOrderBy
{
private String fieldName;
private boolean isAscending = true;
/*******************************************************************************
** Getter for fieldName
**
*******************************************************************************/
public String getFieldName()
{
return fieldName;
}
/*******************************************************************************
** Setter for fieldName
**
*******************************************************************************/
public void setFieldName(String fieldName)
{
this.fieldName = fieldName;
}
/*******************************************************************************
** Getter for isAscending
**
*******************************************************************************/
public boolean getIsAscending()
{
return isAscending;
}
/*******************************************************************************
** Setter for isAscending
**
*******************************************************************************/
public void setIsAscending(boolean ascending)
{
isAscending = ascending;
}
}

View File

@ -0,0 +1,78 @@
package com.kingsrook.qqq.backend.core.model.actions;
import java.util.LinkedHashMap;
import java.util.Map;
/*******************************************************************************
** Note; 1-based index!!
*******************************************************************************/
public class QIndexBasedFieldMapping extends AbstractQFieldMapping<Integer>
{
private Map<Integer, String> mapping;
/*******************************************************************************
**
*******************************************************************************/
@Override
public String getMappedField(Integer key)
{
if(mapping == null)
{
return (null);
}
return (mapping.get(key));
}
/*******************************************************************************
**
*******************************************************************************/
public void addMapping(Integer key, String fieldName)
{
if(mapping == null)
{
mapping = new LinkedHashMap<>();
}
mapping.put(key, fieldName);
}
/*******************************************************************************
**
*******************************************************************************/
public QIndexBasedFieldMapping withMapping(Integer key, String fieldName)
{
addMapping(key, fieldName);
return (this);
}
/*******************************************************************************
** Getter for mapping
**
*******************************************************************************/
public Map<Integer, String> getMapping()
{
return mapping;
}
/*******************************************************************************
** Setter for mapping
**
*******************************************************************************/
public void setMapping(Map<Integer, String> mapping)
{
this.mapping = mapping;
}
}

View File

@ -0,0 +1,78 @@
package com.kingsrook.qqq.backend.core.model.actions;
import java.util.LinkedHashMap;
import java.util.Map;
/*******************************************************************************
**
*******************************************************************************/
public class QKeyBasedFieldMapping extends AbstractQFieldMapping<String>
{
private Map<String, String> mapping;
/*******************************************************************************
**
*******************************************************************************/
@Override
public String getMappedField(String key)
{
if(mapping == null)
{
return (null);
}
return (mapping.get(key));
}
/*******************************************************************************
**
*******************************************************************************/
public void addMapping(String key, String fieldName)
{
if(mapping == null)
{
mapping = new LinkedHashMap<>();
}
mapping.put(key, fieldName);
}
/*******************************************************************************
**
*******************************************************************************/
public QKeyBasedFieldMapping withMapping(String key, String fieldName)
{
addMapping(key, fieldName);
return (this);
}
/*******************************************************************************
** Getter for mapping
**
*******************************************************************************/
public Map<String, String> getMapping()
{
return mapping;
}
/*******************************************************************************
** Setter for mapping
**
*******************************************************************************/
public void setMapping(Map<String, String> mapping)
{
this.mapping = mapping;
}
}

View File

@ -0,0 +1,111 @@
package com.kingsrook.qqq.backend.core.model.actions;
import java.util.ArrayList;
import java.util.List;
/*******************************************************************************
* Full "filter" for a query - a list of criteria and order-bys
*
*******************************************************************************/
public class QQueryFilter
{
private List<QFilterCriteria> criteria = new ArrayList<>();
private List<QFilterOrderBy> orderBys = new ArrayList<>();
/*******************************************************************************
** Getter for criteria
**
*******************************************************************************/
public List<QFilterCriteria> getCriteria()
{
return criteria;
}
/*******************************************************************************
** Setter for criteria
**
*******************************************************************************/
public void setCriteria(List<QFilterCriteria> criteria)
{
this.criteria = criteria;
}
/*******************************************************************************
** Getter for order
**
*******************************************************************************/
public List<QFilterOrderBy> getOrderBys()
{
return orderBys;
}
/*******************************************************************************
** Setter for order
**
*******************************************************************************/
public void setOrderBys(List<QFilterOrderBy> orderBys)
{
this.orderBys = orderBys;
}
/*******************************************************************************
**
*******************************************************************************/
public void addCriteria(QFilterCriteria qFilterCriteria)
{
if(criteria == null)
{
criteria = new ArrayList<>();
}
criteria.add(qFilterCriteria);
}
/*******************************************************************************
**
*******************************************************************************/
public QQueryFilter withCriteria(QFilterCriteria qFilterCriteria)
{
addCriteria(qFilterCriteria);
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
public void addOrderBy(QFilterOrderBy qFilterOrderBy)
{
if(orderBys == null)
{
orderBys = new ArrayList<>();
}
orderBys.add(qFilterOrderBy);
}
/*******************************************************************************
**
*******************************************************************************/
public QQueryFilter withOrderBy(QFilterOrderBy qFilterOrderBy)
{
addOrderBy(qFilterOrderBy);
return (this);
}
}

View File

@ -0,0 +1,101 @@
package com.kingsrook.qqq.backend.core.model.actions;
import com.kingsrook.qqq.backend.core.model.QInstance;
/*******************************************************************************
**
*******************************************************************************/
public class QueryRequest extends AbstractQTableRequest
{
private QQueryFilter filter;
private Integer skip;
private Integer limit;
/*******************************************************************************
**
*******************************************************************************/
public QueryRequest()
{
System.out.println("In the Query Request void-constructor!");
}
/*******************************************************************************
**
*******************************************************************************/
public QueryRequest(QInstance instance)
{
super(instance);
}
/*******************************************************************************
** Getter for filter
**
*******************************************************************************/
public QQueryFilter getFilter()
{
return filter;
}
/*******************************************************************************
** Setter for filter
**
*******************************************************************************/
public void setFilter(QQueryFilter filter)
{
this.filter = filter;
}
/*******************************************************************************
** Getter for skip
**
*******************************************************************************/
public Integer getSkip()
{
return skip;
}
/*******************************************************************************
** Setter for skip
**
*******************************************************************************/
public void setSkip(Integer skip)
{
this.skip = skip;
}
/*******************************************************************************
** Getter for limit
**
*******************************************************************************/
public Integer getLimit()
{
return limit;
}
/*******************************************************************************
** Setter for limit
**
*******************************************************************************/
public void setLimit(Integer limit)
{
this.limit = limit;
}
}

View File

@ -0,0 +1,35 @@
package com.kingsrook.qqq.backend.core.model.actions;
import java.util.List;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
/*******************************************************************************
* Result for a query action
*
*******************************************************************************/
public class QueryResult extends AbstractQResult
{
List<QRecord> records;
/*******************************************************************************
**
*******************************************************************************/
public List<QRecord> getRecords()
{
return records;
}
/*******************************************************************************
**
*******************************************************************************/
public void setRecords(List<QRecord> records)
{
this.records = records;
}
}

View File

@ -0,0 +1,133 @@
package com.kingsrook.qqq.backend.core.model.data;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
/*******************************************************************************
* Data Record within qqq
*
*******************************************************************************/
public class QRecord
{
private String tableName;
private Serializable primaryKey;
private Map<String, Serializable> values;
/*******************************************************************************
**
*******************************************************************************/
public void setValue(String fieldName, Serializable value)
{
if(values == null)
{
values = new LinkedHashMap<>();
}
values.put(fieldName, value);
}
/*******************************************************************************
** Getter for tableName
**
*******************************************************************************/
public String getTableName()
{
return tableName;
}
/*******************************************************************************
** Setter for tableName
**
*******************************************************************************/
public void setTableName(String tableName)
{
this.tableName = tableName;
}
/*******************************************************************************
** Getter for primaryKey
**
*******************************************************************************/
public Serializable getPrimaryKey()
{
return primaryKey;
}
/*******************************************************************************
** Setter for primaryKey
**
*******************************************************************************/
public void setPrimaryKey(Serializable primaryKey)
{
this.primaryKey = primaryKey;
}
/*******************************************************************************
** Getter for values
**
*******************************************************************************/
public Map<String, Serializable> getValues()
{
return values;
}
/*******************************************************************************
** Setter for values
**
*******************************************************************************/
public void setValues(Map<String, Serializable> values)
{
this.values = values;
}
/*******************************************************************************
** Getter for a single field's value
**
*******************************************************************************/
public Serializable getValue(String fieldName)
{
return (values.get(fieldName));
}
/*******************************************************************************
** Getter for a single field's value
**
*******************************************************************************/
public String getValueString(String fieldName)
{
return ((String) values.get(fieldName));
}
/*******************************************************************************
** Getter for a single field's value
**
*******************************************************************************/
public Integer getValueInteger(String fieldName)
{
return ((Integer) values.get(fieldName));
}
}

View File

@ -0,0 +1,56 @@
package com.kingsrook.qqq.backend.core.model.data;
import java.util.List;
/*******************************************************************************
**
*******************************************************************************/
public class QRecordWithStatus extends QRecord
{
private List<Exception> errors;
/*******************************************************************************
**
*******************************************************************************/
public QRecordWithStatus()
{
}
/*******************************************************************************
**
*******************************************************************************/
public QRecordWithStatus(QRecord record)
{
super.setTableName(record.getTableName());
super.setPrimaryKey(record.getPrimaryKey());
super.setValues(record.getValues());
}
/*******************************************************************************
** Getter for errors
**
*******************************************************************************/
public List<Exception> getErrors()
{
return errors;
}
/*******************************************************************************
** Setter for errors
**
*******************************************************************************/
public void setErrors(List<Exception> errors)
{
this.errors = errors;
}
}

View File

@ -0,0 +1,155 @@
package com.kingsrook.qqq.backend.core.model.metadata;
import java.util.HashMap;
import java.util.Map;
/*******************************************************************************
**
*******************************************************************************/
public class QBackendMetaData
{
private String name;
private String type;
private Map<String, String> values;
/*******************************************************************************
**
*******************************************************************************/
public String getValue(String key)
{
if(values == null)
{
return null;
}
return values.get(key);
}
/*******************************************************************************
**
*******************************************************************************/
public void setValue(String key, String value)
{
if(values == null)
{
values = new HashMap<>();
}
values.put(key, value);
}
/*******************************************************************************
**
*******************************************************************************/
public QBackendMetaData withValue(String key, String value)
{
if(values == null)
{
values = new HashMap<>();
}
values.put(key, value);
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
public String getName()
{
return name;
}
/*******************************************************************************
**
*******************************************************************************/
public void setName(String name)
{
this.name = name;
}
/*******************************************************************************
**
*******************************************************************************/
public QBackendMetaData withName(String name)
{
this.name = name;
return (this);
}
/*******************************************************************************
** Getter for type
**
*******************************************************************************/
public String getType()
{
return type;
}
/*******************************************************************************
** Setter for type
**
*******************************************************************************/
public void setType(String type)
{
this.type = type;
}
/*******************************************************************************
**
*******************************************************************************/
public QBackendMetaData withType(String type)
{
this.type = type;
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
public Map<String, String> getValues()
{
return values;
}
/*******************************************************************************
**
*******************************************************************************/
public void setValues(Map<String, String> values)
{
this.values = values;
}
/*******************************************************************************
**
*******************************************************************************/
public QBackendMetaData withVales(Map<String, String> values)
{
this.values = values;
return (this);
}
}

View File

@ -0,0 +1,163 @@
package com.kingsrook.qqq.backend.core.model.metadata;
/*******************************************************************************
**
*******************************************************************************/
public class QFieldMetaData
{
private String name;
private String label;
private String backendName;
private QFieldType type;
/*******************************************************************************
**
*******************************************************************************/
public QFieldMetaData()
{
}
/*******************************************************************************
**
*******************************************************************************/
public QFieldMetaData(String name, QFieldType type)
{
this.name = name;
this.type = type;
}
/*******************************************************************************
**
*******************************************************************************/
public String getName()
{
return name;
}
/*******************************************************************************
**
*******************************************************************************/
public void setName(String name)
{
this.name = name;
}
/*******************************************************************************
**
*******************************************************************************/
public QFieldMetaData withName(String name)
{
this.name = name;
return (this);
}
/*******************************************************************************
** Getter for type
**
*******************************************************************************/
public QFieldType getType()
{
return type;
}
/*******************************************************************************
** Setter for type
**
*******************************************************************************/
public void setType(QFieldType type)
{
this.type = type;
}
/*******************************************************************************
**
*******************************************************************************/
public QFieldMetaData withType(QFieldType type)
{
this.type = type;
return (this);
}
/*******************************************************************************
** Getter for label
**
*******************************************************************************/
public String getLabel()
{
return label;
}
/*******************************************************************************
** Setter for label
**
*******************************************************************************/
public void setLabel(String label)
{
this.label = label;
}
/*******************************************************************************
**
*******************************************************************************/
public QFieldMetaData withLabel(String label)
{
this.label = label;
return (this);
}
/*******************************************************************************
** Getter for backendName
**
*******************************************************************************/
public String getBackendName()
{
return backendName;
}
/*******************************************************************************
** Setter for backendName
**
*******************************************************************************/
public void setBackendName(String backendName)
{
this.backendName = backendName;
}
/*******************************************************************************
**
*******************************************************************************/
public QFieldMetaData withBackendName(String backendName)
{
this.backendName = backendName;
return (this);
}
}

View File

@ -0,0 +1,18 @@
package com.kingsrook.qqq.backend.core.model.metadata;
/*******************************************************************************
**
*******************************************************************************/
public enum QFieldType
{
STRING,
INTEGER,
DECIMAL,
DATE,
TIME,
DATE_TIME,
TEXT,
HTML,
PASSWORD
}

View File

@ -0,0 +1,216 @@
package com.kingsrook.qqq.backend.core.model.metadata;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/*******************************************************************************
**
*******************************************************************************/
public class QTableMetaData
{
private String name;
private String backendName;
private String primaryKeyField;
private List<QFieldMetaData> fields;
private Map<String, QFieldMetaData> _fieldMap;
/*******************************************************************************
**
*******************************************************************************/
public QFieldMetaData getField(String fieldName)
{
if(fields == null)
{
throw (new IllegalArgumentException("Table [" + name + "] does not have its fields defined."));
}
QFieldMetaData field = getFieldMap().get(fieldName);
if(field == null)
{
throw (new IllegalArgumentException("Field [" + fieldName + "] was not found in table [" + name + "]."));
}
return (field);
}
/*******************************************************************************
**
*******************************************************************************/
private Map<String, QFieldMetaData> getFieldMap()
{
if(_fieldMap == null)
{
_fieldMap = new LinkedHashMap<>();
for(QFieldMetaData field : fields)
{
_fieldMap.put(field.getName(), field);
}
}
return (_fieldMap);
}
/*******************************************************************************
**
*******************************************************************************/
public String getName()
{
return name;
}
/*******************************************************************************
**
*******************************************************************************/
public void setName(String name)
{
this.name = name;
}
/*******************************************************************************
**
*******************************************************************************/
public QTableMetaData withName(String name)
{
this.name = name;
return (this);
}
/*******************************************************************************
** Getter for backendName
**
*******************************************************************************/
public String getBackendName()
{
return backendName;
}
/*******************************************************************************
** Setter for backendName
**
*******************************************************************************/
public void setBackendName(String backendName)
{
this.backendName = backendName;
}
/*******************************************************************************
**
*******************************************************************************/
public QTableMetaData withBackendName(String backendName)
{
this.backendName = backendName;
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
public String getPrimaryKeyField()
{
return primaryKeyField;
}
/*******************************************************************************
**
*******************************************************************************/
public void setPrimaryKeyField(String primaryKeyField)
{
this.primaryKeyField = primaryKeyField;
}
/*******************************************************************************
**
*******************************************************************************/
public QTableMetaData withPrimaryKeyField(String primaryKeyField)
{
this.primaryKeyField = primaryKeyField;
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
public List<QFieldMetaData> getFields()
{
return fields;
}
/*******************************************************************************
**
*******************************************************************************/
public void setFields(List<QFieldMetaData> fields)
{
this.fields = fields;
}
/*******************************************************************************
**
*******************************************************************************/
public QTableMetaData withFields(List<QFieldMetaData> fields)
{
this.fields = fields;
return (this);
}
/*******************************************************************************
**
*******************************************************************************/
public void addField(QFieldMetaData field)
{
if(this.fields == null)
{
this.fields = new ArrayList<>();
}
this.fields.add(field);
}
/*******************************************************************************
**
*******************************************************************************/
public QTableMetaData withField(QFieldMetaData field)
{
if(this.fields == null)
{
this.fields = new ArrayList<>();
}
this.fields.add(field);
return (this);
}
}

View File

@ -0,0 +1,58 @@
package com.kingsrook.qqq.backend.core.modules;
import java.util.HashMap;
import java.util.Map;
import com.kingsrook.qqq.backend.core.exceptions.QModuleDispatchException;
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
import com.kingsrook.qqq.backend.core.modules.interfaces.QModuleInterface;
/*******************************************************************************
**
*******************************************************************************/
public class QModuleDispatcher
{
private Map<String, String> backendTypeToModuleClassNameMap;
/*******************************************************************************
**
*******************************************************************************/
public QModuleDispatcher()
{
backendTypeToModuleClassNameMap = new HashMap<>();
backendTypeToModuleClassNameMap.put("rdbms", "com.kingsrook.qqq.backend.module.rdbms.RDBSMModule");
backendTypeToModuleClassNameMap.put("nosql", "com.kingsrook.qqq.backend.module.nosql.NoSQLModule");
// todo - let user define custom type -> classes
}
/*******************************************************************************
**
*******************************************************************************/
public QModuleInterface getQModule(QBackendMetaData backend) throws QModuleDispatchException
{
try
{
String className = backendTypeToModuleClassNameMap.get(backend.getType());
if (className == null)
{
throw (new QModuleDispatchException("Unrecognized backend type [" + backend.getType() + "] in dispatcher."));
}
Class<?> moduleClass = Class.forName(className);
return (QModuleInterface) moduleClass.getDeclaredConstructor().newInstance();
}
catch(QModuleDispatchException qmde)
{
throw (qmde);
}
catch(Exception e)
{
throw (new QModuleDispatchException("Error getting module", e));
}
}
}

View File

@ -0,0 +1,18 @@
package com.kingsrook.qqq.backend.core.modules.interfaces;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.InsertRequest;
import com.kingsrook.qqq.backend.core.model.actions.InsertResult;
/*******************************************************************************
**
*******************************************************************************/
public interface InsertInterface
{
/*******************************************************************************
**
*******************************************************************************/
InsertResult execute(InsertRequest insertRequest) throws QException;
}

View File

@ -0,0 +1,21 @@
package com.kingsrook.qqq.backend.core.modules.interfaces;
import com.kingsrook.qqq.backend.core.actions.InsertAction;
/*******************************************************************************
**
*******************************************************************************/
public interface QModuleInterface
{
/*******************************************************************************
**
*******************************************************************************/
QueryInterface getQueryInterface();
/*******************************************************************************
**
*******************************************************************************/
InsertInterface getInsertInterface();
}

View File

@ -0,0 +1,18 @@
package com.kingsrook.qqq.backend.core.modules.interfaces;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.QueryRequest;
import com.kingsrook.qqq.backend.core.model.actions.QueryResult;
/*******************************************************************************
**
*******************************************************************************/
public interface QueryInterface
{
/*******************************************************************************
**
*******************************************************************************/
QueryResult execute(QueryRequest queryRequest) throws QException;
}

View File

@ -0,0 +1,257 @@
package com.kingsrook.qqq.backend.core.utils;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
/*******************************************************************************
**
*******************************************************************************/
public class CollectionUtils
{
/*******************************************************************************
** true if c is null or it's empty
**
*******************************************************************************/
public static boolean nullSafeIsEmpty(Collection c)
{
if(c == null || c.isEmpty())
{
return (true);
}
return (false);
}
/*******************************************************************************
** true if c is NOT null and it's not empty
**
*******************************************************************************/
public static boolean nullSafeHasContents(Collection c)
{
return (!nullSafeIsEmpty(c));
}
/*******************************************************************************
** 0 if c is empty, otherwise, its size.
**
*******************************************************************************/
public static int nullSafeSize(Collection c)
{
if(c == null)
{
return (0);
}
return (c.size());
}
/*******************************************************************************
**
*******************************************************************************/
public static <K, V> void addAllToMap(Map<K, V> addingTo, Map<K, V> addingFrom)
{
for(K key : addingFrom.keySet())
{
addingTo.put(key, addingFrom.get(key));
}
}
/*******************************************************************************
**
*******************************************************************************/
public static <K, V> Map<K, V> listToMap(List<V> values, Function<V, ? extends K> keyFunction)
{
if(values == null)
{
return (null);
}
Map<K, V> rs = new HashMap<>();
for(V value : values)
{
rs.put(keyFunction.apply(value), value);
}
return (rs);
}
/*******************************************************************************
**
*******************************************************************************/
public static <E, K, V> Map<K, V> listToMap(List<E> elements, Function<E, ? extends K> keyFunction, Function<E, ? extends V> valueFunction)
{
if(elements == null)
{
return (null);
}
Map<K, V> rs = new HashMap<>();
for(E element : elements)
{
rs.put(keyFunction.apply(element), valueFunction.apply(element));
}
return (rs);
}
/*******************************************************************************
**
*******************************************************************************/
public static <K, V> ListingHash<K, V> listToListingHash(List<V> values, Function<V, ? extends K> keyFunction)
{
if(values == null)
{
return (null);
}
ListingHash<K, V> rs = new ListingHash<>();
for(V value : values)
{
rs.add(keyFunction.apply(value), value);
}
return (rs);
}
/*******************************************************************************
* <p>Take a list of objects, and build a listing hash, using 2 lambdas to control
* how keys in the listing hash are created (from the objects), and how values
* are created.</p>
*
* <p>For example, given a list of Work records, if we want a Listing hash with
* workStatusId as keys, and workId a values, we would call:</p>
*
* <code>listToListingHash(workList, Work::getWorkStatusId, Work::getId)</code>
*
* @param elements list of objects to be mapped
* @param keyFunction function to map an object from elements list into keys
* for the listing hash
* @param valueFunction function to map an object from elements list into values
* for the listing hash
*
* @return ListingHash that might look like:
* 1 -> [ 73, 75, 68]
* 2 -> [ 74 ]
* 4 -> [ 76, 77, 79, 80, 81]
*
* end
*******************************************************************************/
public static <E, K, V> ListingHash<K, V> listToListingHash(List<E> elements, Function<E, ? extends K> keyFunction, Function<E, ? extends V> valueFunction)
{
if(elements == null)
{
return (null);
}
ListingHash<K, V> rs = new ListingHash<>();
for(E element : elements)
{
rs.add(keyFunction.apply(element), valueFunction.apply(element));
}
return (rs);
}
/*******************************************************************************
**
*******************************************************************************/
public static <K1, K2, V> Map<K1, Map<K2, V>> listTo2LevelMap(List<V> values, Function<V, ? extends K1> keyFunction1, Function<V, ? extends K2> keyFunction2)
{
if(values == null)
{
return (null);
}
Map<K1, Map<K2, V>> rs = new HashMap<>();
for(V value : values)
{
K1 k1 = keyFunction1.apply(value);
if(!rs.containsKey(k1))
{
rs.put(k1, new HashMap<>());
}
rs.get(k1).put(keyFunction2.apply(value), value);
}
return (rs);
}
/*******************************************************************************
** split a large collection into lists of lists, with specified pageSize
**
*******************************************************************************/
public static <T> List<List<T>> getPages(Collection<T> values, int pageSize)
{
List<List<T>> rs = new LinkedList<>();
if(values.isEmpty())
{
//////////////////////////////////////////////////////////////////
// if there are no input values, return an empty list of lists. //
//////////////////////////////////////////////////////////////////
return (rs);
}
List<T> currentPage = new LinkedList<T>();
rs.add(currentPage);
for(T value : values)
{
if(currentPage.size() >= pageSize)
{
currentPage = new LinkedList<T>();
rs.add(currentPage);
}
currentPage.add(value);
}
return (rs);
}
/*******************************************************************************
**
*******************************************************************************/
public static String getQuestionMarks(Collection<?> c)
{
if(CollectionUtils.nullSafeIsEmpty(c))
{
return ("");
}
StringBuilder rs = new StringBuilder();
for(int i = 0; i < c.size(); i++)
{
rs.append(i > 0 ? "," : "").append("?");
}
return (rs.toString());
}
}

View File

@ -0,0 +1,119 @@
package com.kingsrook.qqq.backend.core.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/*******************************************************************************
** See: https://www.baeldung.com/jackson-vs-gson
**
*******************************************************************************/
public class JsonUtils
{
private static final Logger LOG = LogManager.getLogger(JsonUtils.class);
/*******************************************************************************
**
*******************************************************************************/
public static String toJson(Object object)
{
try
{
ObjectMapper mapper = newObjectMapper();
String jsonResult = mapper.writeValueAsString(object);
return (jsonResult);
}
catch(JsonProcessingException e)
{
LOG.error("Error serializing object of type [" + object.getClass().getSimpleName() + "] to json", e);
throw new IllegalArgumentException("Error in JSON Serialization", e);
}
}
/*******************************************************************************
**
*******************************************************************************/
public static String toPrettyJson(Object object)
{
try
{
ObjectMapper mapper = newObjectMapper();
ObjectWriter objectWriter = mapper.writerWithDefaultPrettyPrinter();
String jsonResult = objectWriter.writeValueAsString(object);
return (jsonResult);
}
catch(JsonProcessingException e)
{
LOG.error("Error serializing object of type [" + object.getClass().getSimpleName() + "] to json", e);
throw new IllegalArgumentException("Error in JSON Serialization", e);
}
}
/*******************************************************************************
**
*******************************************************************************/
public static JSONObject toJSONObject(String json) throws JSONException
{
JSONObject jsonObject = new JSONObject(json);
return (jsonObject);
}
/*******************************************************************************
**
*******************************************************************************/
public static JSONArray toJSONArray(String json) throws JSONException
{
JSONArray jsonArray = new JSONArray(json);
return (jsonArray);
}
/*******************************************************************************
**
*******************************************************************************/
private static ObjectMapper newObjectMapper()
{
ObjectMapper mapper = new ObjectMapper()
.registerModule(new JavaTimeModule())
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
return (mapper);
}
/*******************************************************************************
**
*******************************************************************************/
public static boolean looksLikeObject(String json)
{
return (json != null && json.matches("(?s)\\s*\\{.*"));
}
/*******************************************************************************
**
*******************************************************************************/
public static boolean looksLikeArray(String json)
{
return (json != null && json.matches("(?s)\\s*\\[.*"));
}
}

View File

@ -0,0 +1,267 @@
package com.kingsrook.qqq.backend.core.utils;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
/*******************************************************************************
** Hash that provides "listing" capability -- keys map to lists of values that
** are automatically/easily added to
**
*******************************************************************************/
public class ListingHash<K, V> implements Map<K, List<V>>, Serializable
{
public static final long serialVersionUID = 0L;
private HashMap<K, List<V>> hashMap = null;
/*******************************************************************************
** Default constructor
**
*******************************************************************************/
public ListingHash()
{
this.hashMap = new HashMap<K, List<V>>();
}
/*******************************************************************************
** Add a value to the entry/list for this key
**
*******************************************************************************/
public List<V> add(K key, V value)
{
List<V> list = getOrCreateListForKey(key);
list.add(value);
return (list);
}
/*******************************************************************************
** Add all elements of the collection of v's to this listing hash, using keys
** generated by passing each v to the supplied keyFunction (which return's K's)
**
*******************************************************************************/
public void addAll(Collection<V> vs, Function<V, K> keyFunction)
{
if(vs == null || keyFunction == null)
{
return;
}
for(V v : vs)
{
add(keyFunction.apply(v), v);
}
}
/*******************************************************************************
** Add multiple values to the entry/list for this key
**
*******************************************************************************/
public List<V> addAll(K key, Collection<V> values)
{
List<V> list = getOrCreateListForKey(key);
list.addAll(values);
return (list);
}
/*******************************************************************************
**
*******************************************************************************/
public void addAll(ListingHash<K, V> that)
{
if(that == null)
{
return;
}
for(K key : that.keySet())
{
addAll(key, that.get(key));
}
}
/*******************************************************************************
**
*******************************************************************************/
private List<V> getOrCreateListForKey(K key)
{
List<V> list;
if(!this.hashMap.containsKey(key))
{
/////////////////////////////////
// create list, place into map //
/////////////////////////////////
list = new LinkedList<V>();
this.hashMap.put(key, list);
}
else
{
list = this.hashMap.get(key);
}
return list;
}
/////////////////////////////////////////////////////////////////////////
// wrappings of methods taken from the internal HashMap of this object //
/////////////////////////////////////////////////////////////////////////
/*******************************************************************************
**
*******************************************************************************/
public void clear()
{
this.hashMap.clear();
}
/*******************************************************************************
**
*******************************************************************************/
public boolean containsKey(Object key)
{
return (this.hashMap.containsKey(key));
}
/*******************************************************************************
**
*******************************************************************************/
public boolean containsValue(Object value)
{
return (this.hashMap.containsValue(value));
}
/*******************************************************************************
**
*******************************************************************************/
public Set<Entry<K, List<V>>> entrySet()
{
return (this.hashMap.entrySet());
}
/*******************************************************************************
**
*******************************************************************************/
public boolean equals(Object o)
{
return (this.hashMap.equals(o));
}
/*******************************************************************************
**
*******************************************************************************/
public List<V> get(Object key)
{
return (this.hashMap.get(key));
}
/*******************************************************************************
**
*******************************************************************************/
public int hashCode()
{
return (this.hashMap.hashCode());
}
/*******************************************************************************
**
*******************************************************************************/
public boolean isEmpty()
{
return (this.hashMap.isEmpty());
}
/*******************************************************************************
**
*******************************************************************************/
public Set<K> keySet()
{
return (this.hashMap.keySet());
}
/*******************************************************************************
**
*******************************************************************************/
public List<V> put(K key, List<V> value)
{
return (this.hashMap.put(key, value));
}
/*******************************************************************************
**
*******************************************************************************/
public void putAll(Map<? extends K, ? extends List<V>> t)
{
this.hashMap.putAll(t);
}
/*******************************************************************************
**
*******************************************************************************/
public List<V> remove(Object key)
{
return (this.hashMap.remove(key));
}
/*******************************************************************************
**
*******************************************************************************/
public int size()
{
return (this.hashMap.size());
}
/*******************************************************************************
**
*******************************************************************************/
public Collection<List<V>> values()
{
return (this.hashMap.values());
}
}

View File

@ -0,0 +1,373 @@
package com.kingsrook.qqq.backend.core.utils;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/*******************************************************************************
**
*******************************************************************************/
public class StringUtils
{
/*******************************************************************************
** test if string is not null and is not empty (after being trimmed).
**
** @param input the string to test
** @return Boolean
*******************************************************************************/
public static Boolean hasContent(String input)
{
if(input != null && !input.trim().equals(""))
{
return true;
}
return false;
}
/*******************************************************************************
** returns input.toString() if not null, or nullOutput if input == null (as in SQL NVL)
**
*******************************************************************************/
public static String nvl(Object input, String nullOutput)
{
if(input == null)
{
return nullOutput;
}
return input.toString();
}
/*******************************************************************************
** returns input if not null, or nullOutput if input == null (as in SQL NVL)
**
*******************************************************************************/
public static String nvl(String input, String nullOutput)
{
if(input == null)
{
return nullOutput;
}
return input;
}
/*******************************************************************************
** allCapsToMixedCase - ie, UNIT CODE -> Unit Code
**
** @param input
** @return
*******************************************************************************/
public static String allCapsToMixedCase(String input)
{
if(input == null)
{
return (null);
}
StringBuilder rs = new StringBuilder();
///////////////////////////////////////////////////////////////////////
// match for 0 or more non-capitals (which will pass through as-is), //
// then one capital (which will be kept uppercase), //
// then 0 or more capitals (which will be lowercased), //
// then 0 or more non-capitals (which will pass through as-is) //
// //
// Example matches are: //
// "UNIT CODE" -> ()(U)(NIT)( ), then ()(C)(ODE)() -> (Unit )(Code) //
// "UNITCODE" -> ()(U)(NITCODE)(), -> (Unitcode) //
// "UnitCode" -> ()(U)()(nit), then ()(C)()(ode) -> (Unit)(Code) //
// "UNIT0CODE" -> ()(U)(NIT)(0), then ()(C)(ODE)() -> (Unit0)(Code) //
// "0UNITCODE" -> (0)(U)(NITCODE)() -> (0Unitcode) //
///////////////////////////////////////////////////////////////////////
Pattern pattern = Pattern.compile("([^A-Z]*)([A-Z])([A-Z]*)([^A-Z]*)");
Matcher matcher = pattern.matcher(input);
while(matcher.find())
{
rs.append(matcher.group(1) != null ? matcher.group(1) : "");
rs.append(matcher.group(2) != null ? matcher.group(2) : "");
rs.append(matcher.group(3) != null ? matcher.group(3).toLowerCase() : "");
rs.append(matcher.group(4) != null ? matcher.group(4) : "");
}
///////////////////////////////////////////////////////////////////////
// just in case no match was found, return the original input string //
///////////////////////////////////////////////////////////////////////
if(rs.length() == 0)
{
return (input);
}
return (rs.toString());
}
/*******************************************************************************
* truncate a string (null safely) at a max length.
*
*******************************************************************************/
public static String safeTruncate(String input, int maxLength)
{
if(input == null)
{
return (null);
}
if(input.length() <= maxLength)
{
return (input);
}
return (input.substring(0, maxLength));
}
/*******************************************************************************
*
*******************************************************************************/
public static String safeTruncate(String input, int maxLength, String suffix)
{
if(input == null)
{
return (null);
}
if(input.length() <= maxLength)
{
return (input);
}
return (input.substring(0, (maxLength - suffix.length())) + suffix);
}
/*******************************************************************************
** returns input if not null, or nullOutput if input == null (as in SQL NVL)
**
*******************************************************************************/
public static String safeTrim(String input)
{
if(input == null)
{
return null;
}
return input.trim();
}
/*******************************************************************************
** Join a collection of objects into 1 string
**
** @param glue - String to insert between entries
** @param collection - The collection of objects to join.
** @return String
*******************************************************************************/
public static String join(String glue, Collection<? extends Object> collection)
{
StringBuffer rs = new StringBuffer();
int i = 0;
for(Object s : collection)
{
if(i++ > 0)
{
rs.append(glue);
}
rs.append(String.valueOf(s));
}
return (rs.toString());
}
/*******************************************************************************
** joinWithCommasAndAnd
**
** [one] => [one]
** [one, two] => [one and two]
** [one, two, three] => [one, two, and three]
** [one, two, three, four] => [one, two, three, and four]
** etc.
**
** @param input
** @return
*******************************************************************************/
public static String joinWithCommasAndAnd(List<String> input)
{
StringBuilder rs = new StringBuilder();
int size = input.size();
for(int i = 0; i < size; i++)
{
if(i > 0 && size == 2)
{
rs.append(" and ");
}
else if(i > 0 && i < size - 1)
{
rs.append(", ");
}
else if(i > 0 && i == size - 1)
{
rs.append(", and ");
}
rs.append(input.get(i));
}
return (rs.toString());
}
/*******************************************************************************
** isNullOrEmptyString
**
** @param input
*******************************************************************************/
private static boolean isNullOrBlankString(Object input)
{
if(input == null)
{
return (true);
}
else if(input instanceof String && !StringUtils.hasContent((String) input))
{
return (true);
}
else
{
return (false);
}
}
/*******************************************************************************
** Split a string into tokens, broken up around a given regular expression pattern.
**
** @param original the string to do the splitting to.
** @param pattern the pattern to split on.
** @return a LinkedList with the elements found in the original string
*******************************************************************************/
public static LinkedList<String> split(String original, String pattern)
{
return (new LinkedList<String>(Arrays.asList(original.split(pattern))));
}
/*******************************************************************************
** Split a string into tokens, broken up around a given regular expression pattern.
**
** @param original the string to do the splitting to.
** @param pattern the pattern to split on.
** @param limit the max number of splits to make
** @return a List with the elements found in the original string
*******************************************************************************/
public static LinkedList<String> split(String original, String pattern, Integer limit)
{
return (new LinkedList<String>(Arrays.asList(original.split(pattern, limit))));
}
/*******************************************************************************
** Trims leading white spaces from a String. Returns a blank ("") value if NULL
**
** @param input
** @return String
*******************************************************************************/
public static String ltrim(String input)
{
if(!hasContent(input))
{
return "";
}
int i = 0;
while(i < input.length() && Character.isWhitespace(input.charAt(i)))
{
i++;
}
return (input.substring(i));
}
/*******************************************************************************
** Trims trailing white spaces from a String. Returns a blank ("") value if NULL
**
** @param input
** @return String
*******************************************************************************/
public static String rtrim(String input)
{
if(!hasContent(input))
{
return "";
}
int i = input.length() - 1;
while(i > 0 && Character.isWhitespace(input.charAt(i)))
{
i--;
}
return (input.substring(0, i + 1));
}
/*******************************************************************************
**
*******************************************************************************/
public static String plural(Collection<?> collection)
{
return (plural(collection == null ? 0 : collection.size()));
}
/*******************************************************************************
**
*******************************************************************************/
public static String plural(Integer size)
{
return (plural(size, "", "s"));
}
/*******************************************************************************
**
*******************************************************************************/
public static String plural(Collection<?> collection, String ifOne, String ifNotOne)
{
return (plural(collection.size(), ifOne, ifNotOne));
}
/*******************************************************************************
**
*******************************************************************************/
public static String plural(Integer size, String ifOne, String ifNotOne)
{
return (size != null && size.equals(1) ? ifOne : ifNotOne);
}
}

View File

@ -0,0 +1,93 @@
package com.kingsrook.qqq.backend.core.adapters;
import com.kingsrook.qqq.backend.core.model.actions.AbstractQFieldMapping;
import com.kingsrook.qqq.backend.core.model.actions.QKeyBasedFieldMapping;
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.fail;
/*******************************************************************************
**
*******************************************************************************/
class JsonToQFieldMappingAdapterTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildMappingFromJson_nullInput()
{
testExpectedToThrow(null);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildMappingFromJson_emptyStringInput()
{
testExpectedToThrow("");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildMappingFromJson_malformedJsonInput()
{
testExpectedToThrow("{foo=bar}");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildMappingFromJson_validInput()
{
JsonToQFieldMappingAdapter jsonToQFieldMappingAdapter = new JsonToQFieldMappingAdapter();
AbstractQFieldMapping<String> mapping = (QKeyBasedFieldMapping) jsonToQFieldMappingAdapter.buildMappingFromJson("""
{
"Field1": "source1",
"Field2": "source2",
}
""");
System.out.println(mapping);
assertNotNull(mapping);
// todo - are we backwards here??
assertEquals("source1", mapping.getMappedField("Field1"));
assertEquals("source2", mapping.getMappedField("Field2"));
}
/*******************************************************************************
**
*******************************************************************************/
private void testExpectedToThrow(String json)
{
try
{
JsonToQFieldMappingAdapter jsonToQFieldMappingAdapter = new JsonToQFieldMappingAdapter();
AbstractQFieldMapping<?> mapping = jsonToQFieldMappingAdapter.buildMappingFromJson(json);
System.out.println(mapping);
}
catch(IllegalArgumentException iae)
{
System.out.println("Threw expected exception");
return;
}
fail("Didn't throw expected exception");
}
}

View File

@ -0,0 +1,149 @@
package com.kingsrook.qqq.backend.core.adapters;
import java.util.List;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/*******************************************************************************
**
*******************************************************************************/
class JsonToQRecordAdapterTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromJson_nullInput()
{
testExpectedToThrow(null);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromJson_emptyStringInput()
{
testExpectedToThrow("");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromJson_inputDoesntLookLikeJson()
{
testExpectedToThrow("<HTML>");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromJson_inputLooksLikeJsonButIsMalformed()
{
testExpectedToThrow("{json=not}");
}
/*******************************************************************************
**
*******************************************************************************/
private void testExpectedToThrow(String json)
{
try
{
JsonToQRecordAdapter jsonToQRecordAdapter = new JsonToQRecordAdapter();
List<QRecord> qRecords = jsonToQRecordAdapter.buildRecordsFromJson(json);
System.out.println(qRecords);
}
catch(IllegalArgumentException iae)
{
System.out.println("Threw expected exception");
return;
}
fail("Didn't throw expected exception");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromJson_emptyList()
{
JsonToQRecordAdapter jsonToQRecordAdapter = new JsonToQRecordAdapter();
List<QRecord> qRecords = jsonToQRecordAdapter.buildRecordsFromJson("[]");
assertNotNull(qRecords);
assertTrue(qRecords.isEmpty());
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromJson_inputObject()
{
JsonToQRecordAdapter jsonToQRecordAdapter = new JsonToQRecordAdapter();
List<QRecord> qRecords = jsonToQRecordAdapter.buildRecordsFromJson("""
{
"field1":"value1",
"field2":"value2"
}
""");
assertNotNull(qRecords);
assertEquals(1, qRecords.size());
assertEquals("value1", qRecords.get(0).getValue("field1"));
assertEquals("value2", qRecords.get(0).getValue("field2"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromJson_inputList()
{
JsonToQRecordAdapter jsonToQRecordAdapter = new JsonToQRecordAdapter();
List<QRecord> qRecords = jsonToQRecordAdapter.buildRecordsFromJson("""
[
{ "field1":"value1", "field2":"value2" },
{ "fieldA":"valueA", "fieldB":"valueB" }
]
""");
assertNotNull(qRecords);
assertEquals(2, qRecords.size());
assertEquals("value1", qRecords.get(0).getValue("field1"));
assertEquals("value2", qRecords.get(0).getValue("field2"));
assertEquals("valueA", qRecords.get(1).getValue("fieldA"));
assertEquals("valueB", qRecords.get(1).getValue("fieldB"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_buildRecordsFromJson_inputListWithNonObjectMembers()
{
testExpectedToThrow("[ 1701 ]");
}
}

View File

@ -0,0 +1,179 @@
package com.kingsrook.qqq.backend.core.utils;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.LinkedHashMap;
import java.util.Map;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/*******************************************************************************
**
*******************************************************************************/
class JsonUtilsTest
{
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_toJsonQRecordInput()
{
QRecord qRecord = getQRecord();
String json = JsonUtils.toJson(qRecord);
assertEquals("{\"tableName\":\"foo\",\"primaryKey\":1,\"values\":{\"foo\":\"Foo\",\"bar\":3.14159}}", json);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_toPrettyJsonQRecordInput()
{
QRecord qRecord = getQRecord();
String json = JsonUtils.toPrettyJson(qRecord);
// todo assertEquals("{\"tableName\":\"foo\",\"primaryKey\":1,\"values\":{\"foo\":\"Foo\",\"bar\":3.14159}}", json);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_toJSONObject()
{
JSONObject jsonObject = JsonUtils.toJSONObject("""
{
"Foo": "Bar",
"Baz": [1, 2, 3]
}
""");
assertNotNull(jsonObject);
assertTrue(jsonObject.has("Foo"));
assertEquals("Bar", jsonObject.getString("Foo"));
assertTrue(jsonObject.has("Baz"));
assertEquals(3, jsonObject.getJSONArray("Baz").length());
assertEquals(1, jsonObject.getJSONArray("Baz").get(0));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_toJSONObjectNonJsonObject()
{
try
{
JsonUtils.toJSONObject("");
}
catch(JSONException je)
{
System.out.println("Caught Expected exception");
return;
}
fail("Did not catch expected exception");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_toJSONArray()
{
JSONArray jsonArray = JsonUtils.toJSONArray("""
[
{"Foo": "Bar"},
{"Baz": [1, 2, 3]}
]
""");
assertNotNull(jsonArray);
assertEquals(2, jsonArray.length());
assertTrue(jsonArray.getJSONObject(0).has("Foo"));
assertEquals("Bar", jsonArray.getJSONObject(0).getString("Foo"));
assertTrue(jsonArray.getJSONObject(1).has("Baz"));
assertEquals(3, jsonArray.getJSONObject(1).getJSONArray("Baz").length());
}
/*******************************************************************************
**
*******************************************************************************/
private QRecord getQRecord()
{
QRecord qRecord = new QRecord();
qRecord.setTableName("foo");
qRecord.setPrimaryKey(1);
Map<String, Serializable> values = new LinkedHashMap<>();
qRecord.setValues(values);
values.put("foo", "Foo");
values.put("bar", new BigDecimal("3.14159"));
return qRecord;
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_toJsonNullInput()
{
String json = JsonUtils.toJson(null);
assertEquals("null", json);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_looksLikeObject()
{
assertFalse(JsonUtils.looksLikeObject(""));
assertFalse(JsonUtils.looksLikeObject(null));
assertFalse(JsonUtils.looksLikeObject("json"));
assertFalse(JsonUtils.looksLikeObject("[]"));
assertTrue(JsonUtils.looksLikeObject("{}"));
assertTrue(JsonUtils.looksLikeObject(" {}"));
assertTrue(JsonUtils.looksLikeObject("\n\n\n{}"));
assertTrue(JsonUtils.looksLikeObject("\n{\n[]\n}\n"));
assertTrue(JsonUtils.looksLikeObject("\n\n\n { \n}\n\n\n"));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
public void test_looksLikeArray()
{
assertFalse(JsonUtils.looksLikeArray(""));
assertFalse(JsonUtils.looksLikeArray(null));
assertFalse(JsonUtils.looksLikeArray("json"));
assertFalse(JsonUtils.looksLikeArray("{json[]}"));
assertFalse(JsonUtils.looksLikeArray("{}"));
assertTrue(JsonUtils.looksLikeArray("[]"));
assertTrue(JsonUtils.looksLikeArray(" []"));
assertTrue(JsonUtils.looksLikeArray("\n\n\n[]"));
assertTrue(JsonUtils.looksLikeArray("\n[\n{}\n}\n"));
assertTrue(JsonUtils.looksLikeArray("\n\n\n [ \n]\n\n\n"));
}
}