Feedback from code reviews

This commit is contained in:
2022-08-11 09:42:30 -05:00
parent 9a7acce3c6
commit 3b1f0b47c1
34 changed files with 486 additions and 196 deletions

View File

@ -30,13 +30,10 @@ import com.kingsrook.qqq.backend.core.modules.authentication.QAuthenticationModu
/*******************************************************************************
**
** Utility methods to be shared by all of the various Actions (e.g., InsertAction)
*******************************************************************************/
public class ActionHelper
{
private int f;
/*******************************************************************************
**

View File

@ -22,7 +22,6 @@
package com.kingsrook.qqq.backend.core.actions;
import java.io.IOException;
import com.kingsrook.qqq.backend.core.exceptions.QException;
@ -31,12 +30,14 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
** part of a transaction.
**
** Most obvious use-case would be a JDBC Connection. See subclass in rdbms module.
**
** Note: One would imagine that this class shouldn't ever implement Serializable...
*******************************************************************************/
public class QBackendTransaction
{
/*******************************************************************************
**
** Commit the transaction.
*******************************************************************************/
public void commit() throws QException
{
@ -48,7 +49,7 @@ public class QBackendTransaction
/*******************************************************************************
**
** Rollback the transaction.
*******************************************************************************/
public void rollback() throws QException
{
@ -60,18 +61,8 @@ public class QBackendTransaction
/*******************************************************************************
* Closes this stream and releases any system resources associated
* with it. If the stream is already closed then invoking this
* method has no effect.
*
* <p> As noted in {@link AutoCloseable#close()}, cases where the
* close may fail require careful attention. It is strongly advised
* to relinquish the underlying resources and to internally
* <em>mark</em> the {@code Closeable} as closed, prior to throwing
* the {@code IOException}.
*
* @throws IOException
* if an I/O error occurs
** Close any resources associated with the transaction. In theory, should only
** be called after a commit or rollback was done.
*******************************************************************************/
public void close()
{

View File

@ -30,6 +30,7 @@ import com.kingsrook.qqq.backend.core.actions.ActionHelper;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
import com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNode;
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendTableMetaData;
@ -55,7 +56,7 @@ public class MetaDataAction
// todo pre-customization - just get to modify the request?
MetaDataOutput metaDataOutput = new MetaDataOutput();
Map<String, QFrontendAppMetaData> treeNodes = new LinkedHashMap<>();
Map<String, AppTreeNode> treeNodes = new LinkedHashMap<>();
/////////////////////////////////////
// map tables to frontend metadata //
@ -64,7 +65,7 @@ public class MetaDataAction
for(Map.Entry<String, QTableMetaData> entry : metaDataInput.getInstance().getTables().entrySet())
{
tables.put(entry.getKey(), new QFrontendTableMetaData(entry.getValue(), false));
treeNodes.put(entry.getKey(), new QFrontendAppMetaData(entry.getValue()));
treeNodes.put(entry.getKey(), new AppTreeNode(entry.getValue()));
}
metaDataOutput.setTables(tables);
@ -75,7 +76,7 @@ public class MetaDataAction
for(Map.Entry<String, QProcessMetaData> entry : metaDataInput.getInstance().getProcesses().entrySet())
{
processes.put(entry.getKey(), new QFrontendProcessMetaData(entry.getValue(), false));
treeNodes.put(entry.getKey(), new QFrontendAppMetaData(entry.getValue()));
treeNodes.put(entry.getKey(), new AppTreeNode(entry.getValue()));
}
metaDataOutput.setProcesses(processes);
@ -86,11 +87,11 @@ public class MetaDataAction
for(Map.Entry<String, QAppMetaData> entry : metaDataInput.getInstance().getApps().entrySet())
{
apps.put(entry.getKey(), new QFrontendAppMetaData(entry.getValue()));
treeNodes.put(entry.getKey(), new QFrontendAppMetaData(entry.getValue()));
treeNodes.put(entry.getKey(), new AppTreeNode(entry.getValue()));
for(QAppChildMetaData child : entry.getValue().getChildren())
{
apps.get(entry.getKey()).addChild(new QFrontendAppMetaData(child));
apps.get(entry.getKey()).addChild(new AppTreeNode(child));
}
}
metaDataOutput.setApps(apps);
@ -98,7 +99,7 @@ public class MetaDataAction
////////////////////////////////////////////////
// organize app tree nodes by their hierarchy //
////////////////////////////////////////////////
List<QFrontendAppMetaData> appTree = new ArrayList<>();
List<AppTreeNode> appTree = new ArrayList<>();
for(QAppMetaData appMetaData : metaDataInput.getInstance().getApps().values())
{
if(appMetaData.getParentAppName() == null)
@ -118,9 +119,9 @@ public class MetaDataAction
/*******************************************************************************
**
*******************************************************************************/
private void buildAppTree(Map<String, QFrontendAppMetaData> treeNodes, List<QFrontendAppMetaData> nodeList, QAppChildMetaData childMetaData)
private void buildAppTree(Map<String, AppTreeNode> treeNodes, List<AppTreeNode> nodeList, QAppChildMetaData childMetaData)
{
QFrontendAppMetaData treeNode = treeNodes.get(childMetaData.getName());
AppTreeNode treeNode = treeNodes.get(childMetaData.getName());
if(treeNode == null)
{
return;

View File

@ -50,7 +50,10 @@ public class QueryAction
QueryOutput queryOutput = qModule.getQueryInterface().execute(queryInput);
// todo post-customization - can do whatever w/ the result if you want
if (queryInput.getRecordPipe() == null)
{
QValueFormatter.setDisplayValuesInRecords(queryInput.getTable(), queryOutput.getRecords());
}
return queryOutput;
}

View File

@ -447,7 +447,6 @@ public class QInstanceEnricher
/*******************************************************************************
<<<<<<< HEAD
** for all fields in a table, set their backendName, using the default "inference" logic
** see {@link #inferBackendName(String)}
*******************************************************************************/
@ -482,6 +481,17 @@ public class QInstanceEnricher
/*******************************************************************************
** Do a default mapping from a camelCase field name to an underscore_style
** name for a backend.
**
** Examples:
** <ul>
** <li>wordAnotherWordMoreWords -> word_another_word_more_words</li>
** <li>lUlUlUl -> l_ul_ul_ul</li>
** <li>StartsUpper -> starts_upper</li>
** <li>TLAFirst -> tla_first</li>
** <li>wordThenTLAInMiddle -> word_then_tla_in_middle</li>
** <li>endWithTLA -> end_with_tla</li>
** <li>TLAAndAnotherTLA -> tla_and_another_tla</li>
** </ul>
*******************************************************************************/
static String inferBackendName(String fieldName)
{

View File

@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.core.model.actions.metadata;
import java.util.List;
import java.util.Map;
import com.kingsrook.qqq.backend.core.model.actions.AbstractActionOutput;
import com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNode;
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendTableMetaData;
@ -40,7 +41,7 @@ public class MetaDataOutput extends AbstractActionOutput
private Map<String, QFrontendProcessMetaData> processes;
private Map<String, QFrontendAppMetaData> apps;
private List<QFrontendAppMetaData> appTree;
private List<AppTreeNode> appTree;
@ -93,7 +94,7 @@ public class MetaDataOutput extends AbstractActionOutput
** Getter for appTree
**
*******************************************************************************/
public List<QFrontendAppMetaData> getAppTree()
public List<AppTreeNode> getAppTree()
{
return appTree;
}
@ -104,7 +105,7 @@ public class MetaDataOutput extends AbstractActionOutput
** Setter for appTree
**
*******************************************************************************/
public void setAppTree(List<QFrontendAppMetaData> appTree)
public void setAppTree(List<AppTreeNode> appTree)
{
this.appTree = appTree;
}

View File

@ -26,17 +26,22 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
/*******************************************************************************
** Annotation to place onto fields in a QRecordEntity, to add additional attributes
** for propagating down into the corresponding QFieldMetaData
**
*******************************************************************************/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface QField
{
/*******************************************************************************
**
*******************************************************************************/
String label() default "";
/*******************************************************************************
**
*******************************************************************************/
@ -51,4 +56,13 @@ public @interface QField
**
*******************************************************************************/
boolean isEditable() default true;
/*******************************************************************************
**
*******************************************************************************/
String displayFormat() default "";
//////////////////////////////////////////////////////////////////////////////////////////
// new attributes here likely need implementation in QFieldMetaData.constructFromGetter //
//////////////////////////////////////////////////////////////////////////////////////////
}

View File

@ -28,7 +28,6 @@ import java.util.Optional;
import com.github.hervian.reflection.Fun;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.data.QField;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
@ -120,10 +119,20 @@ public class QFieldMetaData
setIsRequired(fieldAnnotation.isRequired());
setIsEditable(fieldAnnotation.isEditable());
if(StringUtils.hasContent(fieldAnnotation.label()))
{
setLabel(fieldAnnotation.label());
}
if(StringUtils.hasContent(fieldAnnotation.backendName()))
{
setBackendName(fieldAnnotation.backendName());
}
if(StringUtils.hasContent(fieldAnnotation.displayFormat()))
{
setDisplayFormat(fieldAnnotation.displayFormat());
}
}
}
catch(QException qe)

View File

@ -0,0 +1,152 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.core.model.metadata.frontend;
import java.util.ArrayList;
import java.util.List;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
/*******************************************************************************
** Frontend-version of objects that are parts of the app-hierarchy/tree.
** e.g., Tables, Processes, and Apps themselves (since they can be nested).
**
** These objects are organized into a tree - where each Node can have 0 or more
** other Nodes as children.
*******************************************************************************/
public class AppTreeNode
{
private AppTreeNodeType type;
private String name;
private String label;
private List<AppTreeNode> children;
private String iconName;
/*******************************************************************************
**
*******************************************************************************/
public AppTreeNode(QAppChildMetaData appChildMetaData)
{
this.name = appChildMetaData.getName();
this.label = appChildMetaData.getLabel();
if(appChildMetaData.getClass().equals(QTableMetaData.class))
{
this.type = AppTreeNodeType.TABLE;
}
else if(appChildMetaData.getClass().equals(QProcessMetaData.class))
{
this.type = AppTreeNodeType.PROCESS;
}
else if(appChildMetaData.getClass().equals(QAppMetaData.class))
{
this.type = AppTreeNodeType.APP;
children = new ArrayList<>();
}
else
{
throw (new IllegalStateException("Unrecognized class for app child meta data: " + appChildMetaData.getClass()));
}
if(appChildMetaData.getIcon() != null)
{
// todo - propagate icons from parents, if they aren't set here...
this.iconName = appChildMetaData.getIcon().getName();
}
}
/*******************************************************************************
** Getter for type
**
*******************************************************************************/
public AppTreeNodeType getType()
{
return type;
}
/*******************************************************************************
** Getter for name
**
*******************************************************************************/
public String getName()
{
return name;
}
/*******************************************************************************
** Getter for label
**
*******************************************************************************/
public String getLabel()
{
return label;
}
/*******************************************************************************
** Getter for children
**
*******************************************************************************/
public List<AppTreeNode> getChildren()
{
return children;
}
/*******************************************************************************
** Getter for iconName
**
*******************************************************************************/
public String getIconName()
{
return iconName;
}
/*******************************************************************************
**
*******************************************************************************/
public void addChild(AppTreeNode childTreeNode)
{
if(children == null)
{
children = new ArrayList<>();
}
children.add(childTreeNode);
}
}

View File

@ -23,7 +23,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.frontend;
/*******************************************************************************
**
** Type for an Node in the an app tree.
*******************************************************************************/
public enum AppTreeNodeType
{

View File

@ -27,10 +27,6 @@ import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppChildMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.layout.QAppMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
/*******************************************************************************
@ -40,12 +36,10 @@ import com.kingsrook.qqq.backend.core.utils.StringUtils;
@JsonInclude(Include.NON_NULL)
public class QFrontendAppMetaData
{
private AppTreeNodeType type;
private String name;
private String label;
private List<QFrontendAppMetaData> children = new ArrayList<>();
private List<AppTreeNode> children = new ArrayList<>();
private String iconName;
@ -59,24 +53,7 @@ public class QFrontendAppMetaData
this.name = appChildMetaData.getName();
this.label = appChildMetaData.getLabel();
if(appChildMetaData.getClass().equals(QTableMetaData.class))
{
this.type = AppTreeNodeType.TABLE;
}
else if(appChildMetaData.getClass().equals(QProcessMetaData.class))
{
this.type = AppTreeNodeType.PROCESS;
}
else if(appChildMetaData.getClass().equals(QAppMetaData.class))
{
this.type = AppTreeNodeType.APP;
}
else
{
throw (new IllegalStateException("Unrecognized class for app child meta data: " + appChildMetaData.getClass()));
}
if(appChildMetaData.getIcon() != null && StringUtils.hasContent(appChildMetaData.getIcon().getName()))
if(appChildMetaData.getIcon() != null)
{
this.iconName = appChildMetaData.getIcon().getName();
}
@ -106,22 +83,11 @@ public class QFrontendAppMetaData
/*******************************************************************************
** Getter for type
**
*******************************************************************************/
public AppTreeNodeType getType()
{
return type;
}
/*******************************************************************************
** Getter for children
**
*******************************************************************************/
public List<QFrontendAppMetaData> getChildren()
public List<AppTreeNode> getChildren()
{
return children;
}
@ -153,12 +119,12 @@ public class QFrontendAppMetaData
/*******************************************************************************
**
*******************************************************************************/
public void addChild(QFrontendAppMetaData qFrontendAppMetaData)
public void addChild(AppTreeNode childAppTreeNode)
{
if(children == null)
{
children = new ArrayList<>();
}
children.add(qFrontendAppMetaData);
children.add(childAppTreeNode);
}
}

View File

@ -30,7 +30,6 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QFrontendStepMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
/*******************************************************************************
@ -81,7 +80,7 @@ public class QFrontendProcessMetaData
}
}
if(processMetaData.getIcon() != null && StringUtils.hasContent(processMetaData.getIcon().getName()))
if(processMetaData.getIcon() != null)
{
this.iconName = processMetaData.getIcon().getName();
}

View File

@ -30,7 +30,6 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.utils.StringUtils;
/*******************************************************************************
@ -78,7 +77,7 @@ public class QFrontendTableMetaData
this.sections = tableMetaData.getSections();
}
if(tableMetaData.getIcon() != null && StringUtils.hasContent(tableMetaData.getIcon().getName()))
if(tableMetaData.getIcon() != null)
{
this.iconName = tableMetaData.getIcon().getName();
}

View File

@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.core.model.metadata.layout;
/*******************************************************************************
** Interface shared by meta-data objects which can be placed into an App.
** e.g., Tables, Processes, and Apps themselves (since they can be nested)
*******************************************************************************/
public interface QAppChildMetaData
{

View File

@ -27,7 +27,8 @@ import java.util.List;
/*******************************************************************************
**
** MetaData definition of an App - an entity that organizes tables & processes
** and can be arranged hierarchically (e.g, apps can contain other apps).
*******************************************************************************/
public class QAppMetaData implements QAppChildMetaData
{

View File

@ -30,7 +30,6 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
import com.kingsrook.qqq.backend.core.model.data.QRecordEntityField;
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
@ -467,17 +466,6 @@ public class QTableMetaData implements QAppChildMetaData, Serializable
/*******************************************************************************
**
*******************************************************************************/
public QTableMetaData withInferredFieldBackendNames()
{
QInstanceEnricher.setInferredFieldBackendNames(this);
return (this);
}
/*******************************************************************************
** Getter for parentAppName
**

View File

@ -46,7 +46,7 @@ public class BasicETLLoadFunction implements BackendStep
private static final Logger LOG = LogManager.getLogger(BasicETLLoadFunction.class);
private QBackendTransaction transaction;
private boolean returnStoredRecords = false;
private boolean returnStoredRecords = true;

View File

@ -78,12 +78,12 @@ public class StreamedETLBackendStep implements BackendStep
// run the query action as an async job //
//////////////////////////////////////////
AsyncJobManager asyncJobManager = new AsyncJobManager();
String queryJobUUID = asyncJobManager.startJob("ReportAction>QueryAction", (status) ->
String queryJobUUID = asyncJobManager.startJob("StreamedETL>QueryAction", (status) ->
{
basicETLExtractFunction.run(runBackendStepInput, runBackendStepOutput);
return (runBackendStepOutput);
});
LOG.info("Started query job [" + queryJobUUID + "] for report");
LOG.info("Started query job [" + queryJobUUID + "] for streamed ETL");
AsyncJobState queryJobState = AsyncJobState.RUNNING;
AsyncJobStatus asyncJobStatus = null;
@ -141,6 +141,14 @@ public class StreamedETLBackendStep implements BackendStep
LOG.info("Query job [" + queryJobUUID + "] for ETL completed with status: " + asyncJobStatus);
/////////////////////////////////////////
// propagate errors from the query job //
/////////////////////////////////////////
if(asyncJobStatus.getState().equals(AsyncJobState.ERROR))
{
throw (new QException("Query job failed with an error", asyncJobStatus.getCaughtException()));
}
//////////////////////////////////////////////////////
// send the final records to transform & load steps //
//////////////////////////////////////////////////////
@ -207,6 +215,7 @@ public class StreamedETLBackendStep implements BackendStep
runBackendStepInput.setRecords(runBackendStepOutput.getRecords());
BasicETLLoadFunction basicETLLoadFunction = new BasicETLLoadFunction();
basicETLLoadFunction.setReturnStoredRecords(false);
basicETLLoadFunction.setTransaction(transaction);
basicETLLoadFunction.run(runBackendStepInput, runBackendStepOutput);

View File

@ -42,8 +42,8 @@ import com.kingsrook.qqq.backend.core.exceptions.QValueException;
*******************************************************************************/
public class ValueUtils
{
private static final DateTimeFormatter yyyyMMddWithDashesFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter MdyyyyWithSlashesFormatter = DateTimeFormatter.ofPattern("M/d/yyyy");
private static final DateTimeFormatter dateTimeFormatter_yyyyMMddWithDashes = DateTimeFormatter.ofPattern("yyyy-MM-dd");
private static final DateTimeFormatter dateTimeFormatter_MdyyyyWithSlashes = DateTimeFormatter.ofPattern("M/d/yyyy");
@ -262,7 +262,7 @@ public class ValueUtils
private static LocalDate tryLocalDateParsers(String s)
{
DateTimeParseException lastException = null;
for(DateTimeFormatter dateTimeFormatter : List.of(yyyyMMddWithDashesFormatter, MdyyyyWithSlashesFormatter))
for(DateTimeFormatter dateTimeFormatter : List.of(dateTimeFormatter_yyyyMMddWithDashes, dateTimeFormatter_MdyyyyWithSlashes))
{
try
{
@ -386,7 +386,6 @@ public class ValueUtils
}
else if(value instanceof Calendar c)
{
TimeZone tz = c.getTimeZone();
return (c.toInstant());
}
else if(value instanceof LocalDateTime ldt)

View File

@ -30,6 +30,7 @@ import java.util.stream.Collectors;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataInput;
import com.kingsrook.qqq.backend.core.model.actions.metadata.MetaDataOutput;
import com.kingsrook.qqq.backend.core.model.metadata.frontend.AppTreeNode;
import com.kingsrook.qqq.backend.core.model.metadata.frontend.QFrontendAppMetaData;
import com.kingsrook.qqq.backend.core.utils.TestUtils;
import org.junit.jupiter.api.Test;
@ -84,7 +85,7 @@ class MetaDataActionTest
QFrontendAppMetaData peopleApp = apps.get(TestUtils.APP_NAME_PEOPLE);
assertThat(peopleApp.getChildren()).isNotEmpty();
Optional<QFrontendAppMetaData> greetingsAppUnderPeopleFromMapOptional = peopleApp.getChildren().stream()
Optional<AppTreeNode> greetingsAppUnderPeopleFromMapOptional = peopleApp.getChildren().stream()
.filter(e -> e.getName().equals(TestUtils.APP_NAME_GREETINGS)).findFirst();
assertThat(greetingsAppUnderPeopleFromMapOptional).isPresent();
@ -97,18 +98,18 @@ class MetaDataActionTest
///////////////////////////////////////////////
// assert against the hierarchical apps tree //
///////////////////////////////////////////////
List<QFrontendAppMetaData> appTree = result.getAppTree();
Set<String> appNamesInTopOfTree = appTree.stream().map(QFrontendAppMetaData::getName).collect(Collectors.toSet());
List<AppTreeNode> appTree = result.getAppTree();
Set<String> appNamesInTopOfTree = appTree.stream().map(AppTreeNode::getName).collect(Collectors.toSet());
assertThat(appNamesInTopOfTree).contains(TestUtils.APP_NAME_PEOPLE);
assertThat(appNamesInTopOfTree).contains(TestUtils.APP_NAME_MISCELLANEOUS);
assertThat(appNamesInTopOfTree).doesNotContain(TestUtils.APP_NAME_GREETINGS);
Optional<QFrontendAppMetaData> peopleAppOptional = appTree.stream()
Optional<AppTreeNode> peopleAppOptional = appTree.stream()
.filter(e -> e.getName().equals(TestUtils.APP_NAME_PEOPLE)).findFirst();
assertThat(peopleAppOptional).isPresent();
assertThat(peopleAppOptional.get().getChildren()).isNotEmpty();
Optional<QFrontendAppMetaData> greetingsAppUnderPeopleFromTree = peopleAppOptional.get().getChildren().stream()
Optional<AppTreeNode> greetingsAppUnderPeopleFromTree = peopleAppOptional.get().getChildren().stream()
.filter(e -> e.getName().equals(TestUtils.APP_NAME_GREETINGS)).findFirst();
assertThat(greetingsAppUnderPeopleFromTree).isPresent();

View File

@ -26,6 +26,7 @@ import java.math.BigDecimal;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.model.data.testentities.Item;
import com.kingsrook.qqq.backend.core.model.data.testentities.ItemWithPrimitives;
import com.kingsrook.qqq.backend.core.model.metadata.fields.DisplayFormat;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
@ -177,6 +178,8 @@ class QRecordEntityTest
///////////////////////////////////////////////////////////////
// assert about attributes that came from @QField annotation //
///////////////////////////////////////////////////////////////
assertEquals("SKU", qTableMetaData.getField("sku").getLabel());
assertEquals(DisplayFormat.COMMAS, qTableMetaData.getField("quantity").getDisplayFormat());
assertTrue(qTableMetaData.getField("sku").getIsRequired());
assertFalse(qTableMetaData.getField("quantity").getIsEditable());
assertEquals("is_featured", qTableMetaData.getField("featured").getBackendName());

View File

@ -25,6 +25,7 @@ package com.kingsrook.qqq.backend.core.model.data.testentities;
import java.math.BigDecimal;
import com.kingsrook.qqq.backend.core.model.data.QField;
import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
import com.kingsrook.qqq.backend.core.model.metadata.fields.DisplayFormat;
/*******************************************************************************
@ -32,13 +33,13 @@ import com.kingsrook.qqq.backend.core.model.data.QRecordEntity;
*******************************************************************************/
public class Item extends QRecordEntity
{
@QField(isRequired = true)
@QField(isRequired = true, label = "SKU")
private String sku;
@QField()
private String description;
@QField(isEditable = false)
@QField(isEditable = false, displayFormat = DisplayFormat.COMMAS)
private Integer quantity;
private BigDecimal price;

View File

@ -60,21 +60,8 @@ public class Auth0AuthenticationModuleTest
/*******************************************************************************
** Test an expired token where 'now' is set to a time that would not require it to be
** re-checked, so it'll show as valid
**
*******************************************************************************/
@Test
public void testLastTimeCheckedNow()
{
assertTrue(testLastTimeChecked(Instant.now(), UNDECODABLE_TOKEN), "A session just checked 'now' should always be valid");
}
/*******************************************************************************
** Test an expired token where 'now' is set to a time that would not require it to be
** re-checked, so it'll show as valid
** Test a token where last-checked is set to a time that would not require it to be
** re-checked, so it'll show as valid no matter what the token is.
**
*******************************************************************************/
@Test
@ -87,8 +74,8 @@ public class Auth0AuthenticationModuleTest
/*******************************************************************************
** Test an expired token where 'now' is set to a time that would require it to be
** re-checked
** Test a token where last-checked is set to a time that would require it to be
** re-checked, so it'll show as invalid.
**
*******************************************************************************/
@Test
@ -101,8 +88,8 @@ public class Auth0AuthenticationModuleTest
/*******************************************************************************
** Test an expired token where 'now' is set to a time that would require it to be
** re-checked
** Test a token where last-checked is past the threshold, so it'll get re-checked,
** and will fail.
**
*******************************************************************************/
@Test

View File

@ -55,7 +55,10 @@ class StreamedETLProcessTest
RunProcessOutput result = new RunProcessAction().execute(request);
assertNotNull(result);
assertTrue(result.getRecords().stream().allMatch(r -> r.getValues().containsKey("id")), "records should have an id, set by the process");
///////////////////////////////////////////////////////////////////////
// since this is streamed, assert there are no records in the output //
///////////////////////////////////////////////////////////////////////
assertTrue(result.getRecords().isEmpty());
assertTrue(result.getException().isEmpty());
}
@ -77,12 +80,14 @@ class StreamedETLProcessTest
// define our mapping from destination-table field names to source-table field names //
///////////////////////////////////////////////////////////////////////////////////////
QKeyBasedFieldMapping mapping = new QKeyBasedFieldMapping().withMapping("name", "firstName");
// request.addValue(StreamedETLProcess.FIELD_MAPPING_JSON, JsonUtils.toJson(mapping.getMapping()));
request.addValue(StreamedETLProcess.FIELD_MAPPING_JSON, JsonUtils.toJson(mapping));
RunProcessOutput result = new RunProcessAction().execute(request);
assertNotNull(result);
assertTrue(result.getRecords().stream().allMatch(r -> r.getValues().containsKey("id")), "records should have an id, set by the process");
///////////////////////////////////////////////////////////////////////
// since this is streamed, assert there are no records in the output //
///////////////////////////////////////////////////////////////////////
assertTrue(result.getRecords().isEmpty());
assertTrue(result.getException().isEmpty());
}

View File

@ -53,7 +53,6 @@ class ValueUtilsTest
@Test
void testGetValueAsString() throws QValueException
{
//noinspection ConstantConditions
assertNull(ValueUtils.getValueAsString(null));
assertEquals("", ValueUtils.getValueAsString(""));
assertEquals(" ", ValueUtils.getValueAsString(" "));
@ -164,26 +163,28 @@ class ValueUtilsTest
@Test
void testGetValueAsLocalDate() throws QValueException
{
LocalDate expected = LocalDate.of(1980, Month.MAY, 31);
assertNull(ValueUtils.getValueAsLocalDate(null));
assertNull(ValueUtils.getValueAsLocalDate(""));
assertNull(ValueUtils.getValueAsLocalDate(" "));
assertEquals(LocalDate.of(1980, Month.MAY, 31), ValueUtils.getValueAsLocalDate(LocalDate.of(1980, 5, 31)));
assertEquals(LocalDate.of(1980, Month.MAY, 31), ValueUtils.getValueAsLocalDate(new java.sql.Date(80, 4, 31)));
assertEquals(expected, ValueUtils.getValueAsLocalDate(LocalDate.of(1980, 5, 31)));
assertEquals(expected, ValueUtils.getValueAsLocalDate(new java.sql.Date(80, 4, 31)));
//noinspection MagicConstant
assertEquals(LocalDate.of(1980, Month.MAY, 31), ValueUtils.getValueAsLocalDate(new java.util.Date(80, 4, 31)));
assertEquals(LocalDate.of(1980, Month.MAY, 31), ValueUtils.getValueAsLocalDate(new java.util.Date(80, Calendar.MAY, 31)));
assertEquals(LocalDate.of(1980, Month.MAY, 31), ValueUtils.getValueAsLocalDate(new java.util.Date(80, Calendar.MAY, 31, 12, 0)));
assertEquals(LocalDate.of(1980, Month.MAY, 31), ValueUtils.getValueAsLocalDate(new java.util.Date(80, Calendar.MAY, 31, 4, 0)));
assertEquals(LocalDate.of(1980, Month.MAY, 31), ValueUtils.getValueAsLocalDate(new java.util.Date(80, Calendar.MAY, 31, 22, 0)));
assertEquals(expected, ValueUtils.getValueAsLocalDate(new java.util.Date(80, 4, 31)));
assertEquals(expected, ValueUtils.getValueAsLocalDate(new java.util.Date(80, Calendar.MAY, 31)));
assertEquals(expected, ValueUtils.getValueAsLocalDate(new java.util.Date(80, Calendar.MAY, 31, 12, 0)));
assertEquals(expected, ValueUtils.getValueAsLocalDate(new java.util.Date(80, Calendar.MAY, 31, 4, 0)));
assertEquals(expected, ValueUtils.getValueAsLocalDate(new java.util.Date(80, Calendar.MAY, 31, 22, 0)));
//noinspection MagicConstant
assertEquals(LocalDate.of(1980, Month.MAY, 31), ValueUtils.getValueAsLocalDate(new GregorianCalendar(1980, 4, 31)));
assertEquals(LocalDate.of(1980, Month.MAY, 31), ValueUtils.getValueAsLocalDate(new GregorianCalendar(1980, Calendar.MAY, 31)));
assertEquals(LocalDate.of(1980, Month.MAY, 31), ValueUtils.getValueAsLocalDate(LocalDateTime.of(1980, 5, 31, 12, 0)));
assertEquals(LocalDate.of(1980, Month.MAY, 31), ValueUtils.getValueAsLocalDate(LocalDateTime.of(1980, 5, 31, 4, 0)));
assertEquals(LocalDate.of(1980, Month.MAY, 31), ValueUtils.getValueAsLocalDate(LocalDateTime.of(1980, 5, 31, 22, 0)));
assertEquals(LocalDate.of(1980, Month.MAY, 31), ValueUtils.getValueAsLocalDate(LocalDateTime.of(1980, Month.MAY, 31, 12, 0)));
assertEquals(LocalDate.of(1980, Month.MAY, 31), ValueUtils.getValueAsLocalDate("1980-05-31"));
assertEquals(LocalDate.of(1980, Month.MAY, 31), ValueUtils.getValueAsLocalDate("05/31/1980"));
assertEquals(expected, ValueUtils.getValueAsLocalDate(new GregorianCalendar(1980, 4, 31)));
assertEquals(expected, ValueUtils.getValueAsLocalDate(new GregorianCalendar(1980, Calendar.MAY, 31)));
assertEquals(expected, ValueUtils.getValueAsLocalDate(LocalDateTime.of(1980, 5, 31, 12, 0)));
assertEquals(expected, ValueUtils.getValueAsLocalDate(LocalDateTime.of(1980, 5, 31, 4, 0)));
assertEquals(expected, ValueUtils.getValueAsLocalDate(LocalDateTime.of(1980, 5, 31, 22, 0)));
assertEquals(expected, ValueUtils.getValueAsLocalDate(LocalDateTime.of(1980, Month.MAY, 31, 12, 0)));
assertEquals(expected, ValueUtils.getValueAsLocalDate("1980-05-31"));
assertEquals(expected, ValueUtils.getValueAsLocalDate("05/31/1980"));
assertThrows(QValueException.class, () -> ValueUtils.getValueAsLocalDate("a"));
assertThrows(QValueException.class, () -> ValueUtils.getValueAsLocalDate("a,b"));

View File

@ -172,8 +172,6 @@ public class RDBMSInsertAction extends AbstractRDBMSAction implements InsertInte
{
LOG.info("Opening transaction");
Connection connection = getConnection(insertInput);
connection.setAutoCommit(false);
System.out.println("Set connection [" + connection + "] to auto-commit false");
return (new RDBMSTransaction(connection));
}

View File

@ -22,8 +22,8 @@
package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import com.kingsrook.qqq.backend.core.actions.QBackendTransaction;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import org.apache.logging.log4j.LogManager;
@ -46,8 +46,9 @@ public class RDBMSTransaction extends QBackendTransaction
/*******************************************************************************
**
*******************************************************************************/
public RDBMSTransaction(Connection connection)
public RDBMSTransaction(Connection connection) throws SQLException
{
connection.setAutoCommit(false);
this.connection = connection;
}
@ -73,7 +74,6 @@ public class RDBMSTransaction extends QBackendTransaction
try
{
RDBMSTransaction.LOG.info("Committing transaction");
System.out.println("Calling commit on connection [" + connection + "]");
connection.commit();
RDBMSTransaction.LOG.info("Commit complete");
}
@ -108,18 +108,7 @@ public class RDBMSTransaction extends QBackendTransaction
/*******************************************************************************
* Closes this stream and releases any system resources associated
* with it. If the stream is already closed then invoking this
* method has no effect.
*
* <p> As noted in {@link AutoCloseable#close()}, cases where the
* close may fail require careful attention. It is strongly advised
* to relinquish the underlying resources and to internally
* <em>mark</em> the {@code Closeable} as closed, prior to throwing
* the {@code IOException}.
*
* @throws IOException
* if an I/O error occurs
**
*******************************************************************************/
@Override
public void close()

View File

@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.module.rdbms.jdbc;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
@ -188,8 +189,6 @@ public class QueryManager
@SuppressWarnings("unchecked")
public static <T> T executeStatementForSingleValue(Connection connection, Class<T> returnClass, String sql, Object... params) throws SQLException
{
throw (new NotImplementedException());
/*
PreparedStatement statement = prepareStatementAndBindParams(connection, sql, params);
statement.execute();
ResultSet resultSet = statement.getResultSet();
@ -238,7 +237,6 @@ public class QueryManager
{
return (null);
}
*/
}
@ -1397,6 +1395,38 @@ public class QueryManager
/*******************************************************************************
**
*******************************************************************************/
public static Instant getInstant(ResultSet resultSet, String column) throws SQLException
{
Timestamp value = resultSet.getTimestamp(column);
if(resultSet.wasNull())
{
return (null);
}
return (value.toInstant());
}
/*******************************************************************************
**
*******************************************************************************/
public static Instant getInstant(ResultSet resultSet, int column) throws SQLException
{
Timestamp value = resultSet.getTimestamp(column);
if(resultSet.wasNull())
{
return (null);
}
return (value.toInstant());
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -22,14 +22,22 @@
package com.kingsrook.qqq.backend.module.rdbms;
import java.io.InputStream;
import java.sql.Connection;
import java.util.List;
import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationType;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldMetaData;
import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
import com.kingsrook.qqq.backend.core.modules.authentication.metadata.QAuthenticationMetaData;
import com.kingsrook.qqq.backend.module.rdbms.actions.RDBMSActionTest;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSBackendMetaData;
import com.kingsrook.qqq.backend.module.rdbms.model.metadata.RDBMSTableBackendDetails;
import org.apache.commons.io.IOUtils;
import static junit.framework.Assert.assertNotNull;
/*******************************************************************************
@ -42,6 +50,29 @@ public class TestUtils
/*******************************************************************************
**
*******************************************************************************/
@SuppressWarnings("unchecked")
public static void primeTestDatabase(String sqlFileName) throws Exception
{
ConnectionManager connectionManager = new ConnectionManager();
try(Connection connection = connectionManager.getConnection(TestUtils.defineBackend()))
{
InputStream primeTestDatabaseSqlStream = RDBMSActionTest.class.getResourceAsStream("/" + sqlFileName);
assertNotNull(primeTestDatabaseSqlStream);
List<String> lines = (List<String>) IOUtils.readLines(primeTestDatabaseSqlStream);
lines = lines.stream().filter(line -> !line.startsWith("-- ")).toList();
String joinedSQL = String.join("\n", lines);
for(String sql : joinedSQL.split(";"))
{
QueryManager.executeUpdate(connection, sql);
}
}
}
/*******************************************************************************
**
*******************************************************************************/

View File

@ -22,15 +22,11 @@
package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.io.InputStream;
import java.sql.Connection;
import java.util.List;
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.AfterEach;
import static junit.framework.Assert.assertNotNull;
/*******************************************************************************
@ -57,33 +53,11 @@ public class RDBMSActionTest
*******************************************************************************/
protected void primeTestDatabase() throws Exception
{
primeTestDatabase("prime-test-database.sql");
TestUtils.primeTestDatabase("prime-test-database.sql");
}
/*******************************************************************************
**
*******************************************************************************/
@SuppressWarnings("unchecked")
protected void primeTestDatabase(String sqlFileName) throws Exception
{
ConnectionManager connectionManager = new ConnectionManager();
try(Connection connection = connectionManager.getConnection(TestUtils.defineBackend()))
{
InputStream primeTestDatabaseSqlStream = RDBMSActionTest.class.getResourceAsStream("/" + sqlFileName);
assertNotNull(primeTestDatabaseSqlStream);
List<String> lines = (List<String>) IOUtils.readLines(primeTestDatabaseSqlStream);
lines = lines.stream().filter(line -> !line.startsWith("-- ")).toList();
String joinedSQL = String.join("\n", lines);
for(String sql : joinedSQL.split(";"))
{
QueryManager.executeUpdate(connection, sql);
}
}
}
/*******************************************************************************
**

View File

@ -151,7 +151,7 @@ public class RDBMSDeleteActionTest extends RDBMSActionTest
//////////////////////////////////////////////////////////////////
// load the parent-child tables, with foreign keys and instance //
//////////////////////////////////////////////////////////////////
super.primeTestDatabase("prime-test-database-parent-child-tables.sql");
TestUtils.primeTestDatabase("prime-test-database-parent-child-tables.sql");
DeleteInput deleteInput = initChildTableInstanceAndDeleteRequest();
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -0,0 +1,96 @@
/*
* QQQ - Low-code Application Framework for Engineers.
* Copyright (C) 2021-2022. Kingsrook, LLC
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
* contact@kingsrook.com
* https://github.com/Kingsrook/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.sql.Connection;
import com.kingsrook.qqq.backend.module.rdbms.TestUtils;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
/*******************************************************************************
** Unit test for RDBMSTransaction
*******************************************************************************/
class RDBMSTransactionTest
{
private final String testToken = getClass().getName();
/*******************************************************************************
**
*******************************************************************************/
@BeforeEach
protected void beforeEach() throws Exception
{
TestUtils.primeTestDatabase("prime-test-database.sql");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testCommit() throws Exception
{
ConnectionManager connectionManager = new ConnectionManager();
Connection connection = connectionManager.getConnection(TestUtils.defineBackend());
Integer preCount = QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT COUNT(*) FROM person");
Connection connectionForTransaction = connectionManager.getConnection(TestUtils.defineBackend());
RDBMSTransaction transaction = new RDBMSTransaction(connectionForTransaction);
QueryManager.executeUpdate(transaction.getConnection(), "INSERT INTO person (first_name, last_name, email) VALUES (?, ?, ?)", testToken, testToken, testToken);
transaction.commit();
Integer postCount = QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT COUNT(*) FROM person");
assertEquals(preCount + 1, postCount);
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testRollback() throws Exception
{
ConnectionManager connectionManager = new ConnectionManager();
Connection connection = connectionManager.getConnection(TestUtils.defineBackend());
Integer preCount = QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT COUNT(*) FROM person");
Connection connectionForTransaction = connectionManager.getConnection(TestUtils.defineBackend());
RDBMSTransaction transaction = new RDBMSTransaction(connectionForTransaction);
QueryManager.executeUpdate(transaction.getConnection(), "INSERT INTO person (first_name, last_name, email) VALUES (?, ?, ?)", testToken, testToken, testToken);
transaction.rollback();
Integer postCount = QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT COUNT(*) FROM person");
assertEquals(preCount, postCount);
}
}

View File

@ -329,4 +329,35 @@ class QueryManagerTest
assertEquals(59, localTime.getSecond(), "Second value");
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testExecuteStatementForSingleValue() throws SQLException
{
Connection connection = getConnection();
QueryManager.executeUpdate(connection, """
INSERT INTO test_table
( int_col, datetime_col, char_col, date_col, time_col )
VALUES
( 47, '2022-08-10 19:22:08', 'Q', '2022-08-10', '19:22:08')
""");
assertEquals(null, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table WHERE int_col = -1"));
assertEquals(1, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT COUNT(*) FROM test_table"));
assertEquals(47, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table"));
assertEquals("Q", QueryManager.executeStatementForSingleValue(connection, String.class, "SELECT char_col FROM test_table"));
assertEquals(new BigDecimal("1.1"), QueryManager.executeStatementForSingleValue(connection, BigDecimal.class, "SELECT 1.1 FROM test_table"));
assertEquals(1, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT 1.1 FROM test_table"));
QueryManager.executeUpdate(connection, """
INSERT INTO test_table
( int_col, datetime_col, char_col, date_col, time_col )
VALUES
( null, null, null, null, null)
""");
assertEquals(null, QueryManager.executeStatementForSingleValue(connection, Integer.class, "SELECT int_col FROM test_table WHERE int_col IS NULL"));
}
}

View File

@ -26,6 +26,7 @@ import java.util.List;
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QValueException;
import com.kingsrook.qqq.backend.core.instances.QInstanceEnricher;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepInput;
import com.kingsrook.qqq.backend.core.model.actions.processes.RunBackendStepOutput;
import com.kingsrook.qqq.backend.core.model.metadata.QAuthenticationType;
@ -227,7 +228,7 @@ public class SampleMetaDataProvider
table.addField(new QFieldMetaData("company_code", QFieldType.STRING) // todo PVS
.withLabel("Company")
.withIsRequired(true)
.withBackendName("comp_code"));
.withBackendName("company_code"));
table.addField(new QFieldMetaData("service_level", QFieldType.STRING) // todo PVS
.withLabel("Service Level")
@ -246,7 +247,7 @@ public class SampleMetaDataProvider
*******************************************************************************/
public static QTableMetaData defineTablePerson()
{
return new QTableMetaData()
QTableMetaData qTableMetaData = new QTableMetaData()
.withName(TABLE_NAME_PERSON)
.withLabel("Person")
.withBackendName(RDBMS_BACKEND_NAME)
@ -266,9 +267,11 @@ public class SampleMetaDataProvider
.withSection(new QFieldSection("identity", "Identity", new QIcon("badge"), Tier.T1, List.of("id", "firstName", "lastName")))
.withSection(new QFieldSection("basicInfo", "Basic Info", new QIcon("dataset"), Tier.T2, List.of("email", "birthDate")))
.withSection(new QFieldSection("employmentInfo", "Employment Info", new QIcon("work"), Tier.T2, List.of("annualSalary", "daysWorked")))
.withSection(new QFieldSection("dates", "Dates", new QIcon("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")))
.withSection(new QFieldSection("dates", "Dates", new QIcon("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")));
.withInferredFieldBackendNames();
QInstanceEnricher.setInferredFieldBackendNames(qTableMetaData);
return (qTableMetaData);
}