Merged dev into feature/CE-1405-zero-day-ledger-billing

This commit is contained in:
2024-08-16 16:57:26 -05:00
184 changed files with 2099 additions and 692 deletions

View File

@ -661,7 +661,7 @@ public abstract class AbstractRDBMSAction
}
else if(!expectedNoOfParams.equals(values.size()))
{
throw new IllegalArgumentException("Incorrect number of values given for criteria [" + field.getName() + "]");
throw new IllegalArgumentException("Incorrect number of values given for criteria [" + field.getName() + "] (expected " + expectedNoOfParams + ", received " + values.size() + ")");
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -25,11 +25,13 @@ package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.ResultSet;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import com.kingsrook.qqq.backend.core.actions.interfaces.AggregateInterface;
import com.kingsrook.qqq.backend.core.actions.tables.helpers.ActionTimeoutHelper;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
import com.kingsrook.qqq.backend.core.logging.QLogger;
@ -70,11 +72,11 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
QTableMetaData table = aggregateInput.getTable();
QQueryFilter filter = clonedOrNewFilter(aggregateInput.getFilter());
JoinsContext joinsContext = new JoinsContext(aggregateInput.getInstance(), table.getName(), aggregateInput.getQueryJoins(), filter);
JoinsContext joinsContext = new JoinsContext(QContext.getQInstance(), table.getName(), aggregateInput.getQueryJoins(), filter);
List<Serializable> params = new ArrayList<>();
String fromClause = makeFromClause(aggregateInput.getInstance(), table.getName(), joinsContext, params);
String fromClause = makeFromClause(QContext.getQInstance(), table.getName(), joinsContext, params);
List<String> selectClauses = buildSelectClauses(aggregateInput, joinsContext);
String sql = "SELECT " + StringUtils.join(", ", selectClauses)
@ -116,6 +118,14 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
actionTimeoutHelper = new ActionTimeoutHelper(aggregateInput.getTimeoutSeconds(), TimeUnit.SECONDS, new StatementTimeoutCanceller(statement, sql));
actionTimeoutHelper.start();
///////////////////////////////////////////////////////////////////////////////////////////////////
// to avoid counting time spent acquiring a connection, re-set the queryStat startTimestamp here //
///////////////////////////////////////////////////////////////////////////////////////////////////
if(queryStat != null)
{
queryStat.setStartTimestamp(Instant.now());
}
QueryManager.executeStatement(statement, sql, ((ResultSet resultSet) ->
{
/////////////////////////////////////////////////////////////////////////

View File

@ -25,11 +25,13 @@ package com.kingsrook.qqq.backend.module.rdbms.actions;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.ResultSet;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import com.kingsrook.qqq.backend.core.actions.interfaces.CountInterface;
import com.kingsrook.qqq.backend.core.actions.tables.helpers.ActionTimeoutHelper;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
import com.kingsrook.qqq.backend.core.logging.QLogger;
@ -63,7 +65,7 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf
QTableMetaData table = countInput.getTable();
QQueryFilter filter = clonedOrNewFilter(countInput.getFilter());
JoinsContext joinsContext = new JoinsContext(countInput.getInstance(), countInput.getTableName(), countInput.getQueryJoins(), filter);
JoinsContext joinsContext = new JoinsContext(QContext.getQInstance(), countInput.getTableName(), countInput.getQueryJoins(), filter);
JoinsContext.FieldAndTableNameOrAlias fieldAndTableNameOrAlias = joinsContext.getFieldAndTableNameOrAlias(table.getPrimaryKeyField());
boolean requiresDistinct = doesSelectClauseRequireDistinct(table);
@ -77,7 +79,7 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf
List<Serializable> params = new ArrayList<>();
String sql = clausePrefix + " AS record_count "
+ " FROM " + makeFromClause(countInput.getInstance(), table.getName(), joinsContext, params)
+ " FROM " + makeFromClause(QContext.getQInstance(), table.getName(), joinsContext, params)
+ " WHERE " + makeWhereClause(joinsContext, filter, params);
// todo sql customization - can edit sql and/or param list
@ -96,6 +98,14 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf
actionTimeoutHelper = new ActionTimeoutHelper(countInput.getTimeoutSeconds(), TimeUnit.SECONDS, new StatementTimeoutCanceller(statement, sql));
actionTimeoutHelper.start();
///////////////////////////////////////////////////////////////////////////////////////////////////
// to avoid counting time spent acquiring a connection, re-set the queryStat startTimestamp here //
///////////////////////////////////////////////////////////////////////////////////////////////////
if(queryStat != null)
{
queryStat.setStartTimestamp(Instant.now());
}
QueryManager.executeStatement(statement, sql, ((ResultSet resultSet) ->
{
/////////////////////////////////////////////////////////////////////////

View File

@ -29,6 +29,7 @@ import java.util.List;
import java.util.stream.Collectors;
import com.kingsrook.qqq.backend.core.actions.interfaces.DeleteInterface;
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
import com.kingsrook.qqq.backend.core.context.QContext;
import com.kingsrook.qqq.backend.core.exceptions.QException;
import com.kingsrook.qqq.backend.core.logging.QLogger;
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
@ -273,7 +274,7 @@ public class RDBMSDeleteAction extends AbstractRDBMSAction implements DeleteInte
QTableMetaData table = deleteInput.getTable();
String tableName = getTableName(table);
JoinsContext joinsContext = new JoinsContext(deleteInput.getInstance(), table.getName(), new ArrayList<>(), deleteInput.getQueryFilter());
JoinsContext joinsContext = new JoinsContext(QContext.getQInstance(), table.getName(), new ArrayList<>(), deleteInput.getQueryFilter());
String whereClause = makeWhereClause(joinsContext, filter, params);
// todo sql customization - can edit sql and/or param list?

View File

@ -28,6 +28,7 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
@ -98,10 +99,10 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
StringBuilder sql = new StringBuilder(selection.selectClause());
QQueryFilter filter = clonedOrNewFilter(queryInput.getFilter());
JoinsContext joinsContext = new JoinsContext(queryInput.getInstance(), tableName, queryInput.getQueryJoins(), filter);
JoinsContext joinsContext = new JoinsContext(QContext.getQInstance(), tableName, queryInput.getQueryJoins(), filter);
List<Serializable> params = new ArrayList<>();
sql.append(" FROM ").append(makeFromClause(queryInput.getInstance(), tableName, joinsContext, params));
sql.append(" FROM ").append(makeFromClause(QContext.getQInstance(), tableName, joinsContext, params));
sql.append(" WHERE ").append(makeWhereClause(joinsContext, filter, params));
if(filter != null && CollectionUtils.nullSafeHasContents(filter.getOrderBys()))
@ -151,6 +152,14 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
actionTimeoutHelper = new ActionTimeoutHelper(queryInput.getTimeoutSeconds(), TimeUnit.SECONDS, new StatementTimeoutCanceller(statement, sql));
actionTimeoutHelper.start();
///////////////////////////////////////////////////////////////////////////////////////////////////
// to avoid counting time spent acquiring a connection, re-set the queryStat startTimestamp here //
///////////////////////////////////////////////////////////////////////////////////////////////////
if(queryStat != null)
{
queryStat.setStartTimestamp(Instant.now());
}
//////////////////////////////////////////////
// execute the query - iterate over results //
//////////////////////////////////////////////

View File

@ -23,6 +23,7 @@ package com.kingsrook.qqq.backend.module.rdbms;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.util.List;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
@ -91,7 +92,7 @@ public class TestUtils
{
InputStream primeTestDatabaseSqlStream = RDBMSActionTest.class.getResourceAsStream("/" + sqlFileName);
assertNotNull(primeTestDatabaseSqlStream);
List<String> lines = (List<String>) IOUtils.readLines(primeTestDatabaseSqlStream);
List<String> lines = (List<String>) IOUtils.readLines(primeTestDatabaseSqlStream, StandardCharsets.UTF_8);
lines = lines.stream().filter(line -> !line.startsWith("-- ")).toList();
String joinedSQL = String.join("\n", lines);
for(String sql : joinedSQL.split(";"))
@ -200,8 +201,7 @@ public class TestUtils
.withName(TABLE_NAME_PERSON)
.withType(QPossibleValueSourceType.TABLE)
.withTableName(TABLE_NAME_PERSON)
.withValueFormatAndFields(PVSValueFormatAndFields.LABEL_ONLY)
);
.withValueFormatAndFields(PVSValueFormatAndFields.LABEL_ONLY));
}

View File

@ -103,7 +103,8 @@ public class RDBMSDeleteActionTest extends RDBMSActionTest
DeleteOutput deleteResult = new RDBMSDeleteAction().execute(deleteInput);
assertEquals(3, deleteResult.getDeletedRecordCount(), "Should delete one row");
assertEquals(0, deleteResult.getRecordsWithErrors().size(), "should have no errors");
runTestSql("SELECT id FROM person", (rs -> {
runTestSql("SELECT id FROM person", (rs ->
{
int rowsFound = 0;
while(rs.next())
{
@ -177,7 +178,8 @@ public class RDBMSDeleteActionTest extends RDBMSActionTest
assertTrue(deleteResult.getRecordsWithErrors().stream().noneMatch(r -> r.getErrors().isEmpty()), "All we got back should have errors");
assertEquals(3, deleteResult.getDeletedRecordCount(), "Should get back that 3 were deleted");
runTestSql("SELECT id FROM child_table", (rs -> {
runTestSql("SELECT id FROM child_table", (rs ->
{
int rowsFound = 0;
while(rs.next())
{
@ -223,7 +225,8 @@ public class RDBMSDeleteActionTest extends RDBMSActionTest
assertEquals(1, queryStats.get(QueryManager.STAT_QUERIES_RAN), "Number of queries ran");
assertEquals(3, deleteResult.getDeletedRecordCount(), "Should get back that 3 were deleted");
runTestSql("SELECT id FROM child_table", (rs -> {
runTestSql("SELECT id FROM child_table", (rs ->
{
int rowsFound = 0;
while(rs.next())
{
@ -275,7 +278,8 @@ public class RDBMSDeleteActionTest extends RDBMSActionTest
assertTrue(deleteResult.getRecordsWithErrors().stream().noneMatch(r -> r.getErrors().isEmpty()), "All we got back should have errors");
assertEquals(3, deleteResult.getDeletedRecordCount(), "Should get back that 3 were deleted");
runTestSql("SELECT id FROM child_table", (rs -> {
runTestSql("SELECT id FROM child_table", (rs ->
{
int rowsFound = 0;
while(rs.next())
{

View File

@ -188,9 +188,13 @@ public class RDBMSInsertActionTest extends RDBMSActionTest
/***************************************************************************
**
***************************************************************************/
private void assertAnInsertedPersonRecord(String firstName, String lastName, Integer id) throws Exception
{
runTestSql("SELECT * FROM person WHERE last_name = '" + lastName + "'", (rs -> {
runTestSql("SELECT * FROM person WHERE last_name = '" + lastName + "'", (rs ->
{
int rowsFound = 0;
while(rs.next())
{

View File

@ -984,6 +984,7 @@ public class RDBMSQueryActionTest extends RDBMSActionTest
**
*******************************************************************************/
@Test
@SuppressWarnings("unchecked")
void testHeavyFields() throws QException
{
//////////////////////////////////////////////////////////

View File

@ -118,7 +118,8 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
assertEquals(1, updateResult.getRecords().size(), "Should return 1 row");
assertEquals(2, updateResult.getRecords().get(0).getValue("id"), "Should have id=2 in the row");
// todo - add errors to QRecord? assertTrue(updateResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
runTestSql("SELECT * FROM person WHERE last_name = 'Kirk'", (rs -> {
runTestSql("SELECT * FROM person WHERE last_name = 'Kirk'", (rs ->
{
int rowsFound = 0;
while(rs.next())
{
@ -129,7 +130,8 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
}
assertEquals(1, rowsFound);
}));
runTestSql("SELECT * FROM person WHERE last_name = 'Maes'", (rs -> {
runTestSql("SELECT * FROM person WHERE last_name = 'Maes'", (rs ->
{
if(rs.next())
{
fail("Should not have found Maes any more.");
@ -176,7 +178,8 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
assertEquals(3, updateResult.getRecords().get(1).getValue("id"), "Should have expected ids in the row");
assertEquals(5, updateResult.getRecords().get(2).getValue("id"), "Should have expected ids in the row");
// todo - add errors to QRecord? assertTrue(updateResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
runTestSql("SELECT * FROM person WHERE last_name = 'From Bewitched'", (rs -> {
runTestSql("SELECT * FROM person WHERE last_name = 'From Bewitched'", (rs ->
{
int rowsFound = 0;
while(rs.next())
{
@ -188,7 +191,8 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
}
assertEquals(1, rowsFound);
}));
runTestSql("SELECT * FROM person WHERE last_name = 'Chamberlain'", (rs -> {
runTestSql("SELECT * FROM person WHERE last_name = 'Chamberlain'", (rs ->
{
int rowsFound = 0;
while(rs.next())
{
@ -199,7 +203,8 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
}
assertEquals(1, rowsFound);
}));
runTestSql("SELECT * FROM person WHERE last_name = 'Richardson'", (rs -> {
runTestSql("SELECT * FROM person WHERE last_name = 'Richardson'", (rs ->
{
int rowsFound = 0;
while(rs.next())
{
@ -243,7 +248,8 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
assertEquals(1, updateResult.getRecords().get(0).getValue("id"), "Should have expected ids in the row");
assertEquals(3, updateResult.getRecords().get(1).getValue("id"), "Should have expected ids in the row");
// todo - add errors to QRecord? assertTrue(updateResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
runTestSql("SELECT * FROM person WHERE last_name = 'From Bewitched'", (rs -> {
runTestSql("SELECT * FROM person WHERE last_name = 'From Bewitched'", (rs ->
{
int rowsFound = 0;
while(rs.next())
{
@ -255,7 +261,8 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
}
assertEquals(1, rowsFound);
}));
runTestSql("SELECT * FROM person WHERE last_name = 'Tim''s Uncle'", (rs -> {
runTestSql("SELECT * FROM person WHERE last_name = 'Tim''s Uncle'", (rs ->
{
int rowsFound = 0;
while(rs.next())
{
@ -294,7 +301,8 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
assertEquals(5, updateResult.getRecords().size(), "Should return 5 rows");
// todo - add errors to QRecord? assertTrue(updateResult.getRecords().stream().noneMatch(qrs -> CollectionUtils.nullSafeHasContents(qrs.getErrors())), "There should be no errors");
runTestSql("SELECT * FROM person WHERE id <= 5", (rs -> {
runTestSql("SELECT * FROM person WHERE id <= 5", (rs ->
{
int rowsFound = 0;
while(rs.next())
{
@ -414,7 +422,8 @@ public class RDBMSUpdateActionTest extends RDBMSActionTest
private String selectModifyDate(Integer id) throws Exception
{
StringBuilder modifyDate = new StringBuilder();
runTestSql("SELECT modify_date FROM person WHERE id = " + id, (rs -> {
runTestSql("SELECT modify_date FROM person WHERE id = " + id, (rs ->
{
if(rs.next())
{
modifyDate.append(rs.getString("modify_date"));

View File

@ -39,6 +39,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
@Disabled("This was okay for POC, but shouldn't run in CI")
class ConnectionManagerTest extends BaseTest
{
/***************************************************************************
**
***************************************************************************/
@Test
public void test() throws SQLException
{
@ -101,6 +104,9 @@ class ConnectionManagerTest extends BaseTest
/***************************************************************************
**
***************************************************************************/
private RDBMSBackendMetaData getAuroraBacked()
{
QMetaDataVariableInterpreter interpreter = new QMetaDataVariableInterpreter();

View File

@ -293,6 +293,7 @@ class QueryManagerTest extends BaseTest
** confirms (more?) correct behavior
*******************************************************************************/
@Test
@SuppressWarnings("deprecation")
void testLocalDate() throws SQLException
{
try(Connection connection = getConnection())
@ -304,17 +305,20 @@ class QueryManagerTest extends BaseTest
ResultSet rs = preparedStatement.getResultSet();
rs.next();
Date date = QueryManager.getDate(rs, 1);
Date date = QueryManager.getDate(rs, 1);
assertNotNull(date);
assertEquals(1, date.getDate(), "Date value");
assertEquals(Month.OCTOBER.getValue(), date.getMonth() + 1, "Month value");
assertEquals(2013, date.getYear() + 1900, "Year value");
LocalDate localDate = QueryManager.getLocalDate(rs, 1);
assertNotNull(localDate);
assertEquals(1, localDate.getDayOfMonth(), "Date value");
assertEquals(Month.OCTOBER, localDate.getMonth(), "Month value");
assertEquals(2013, localDate.getYear(), "Year value");
LocalDateTime localDateTime = QueryManager.getLocalDateTime(rs, 1);
assertNotNull(localDateTime);
assertEquals(1, localDateTime.getDayOfMonth(), "Date value");
assertEquals(Month.OCTOBER, localDateTime.getMonth(), "Month value");
assertEquals(2013, localDateTime.getYear(), "Year value");
@ -322,6 +326,7 @@ class QueryManagerTest extends BaseTest
assertEquals(0, localDateTime.getMinute(), "Minute value");
OffsetDateTime offsetDateTime = QueryManager.getOffsetDateTime(rs, 1);
assertNotNull(offsetDateTime);
assertEquals(1, offsetDateTime.getDayOfMonth(), "Date value");
assertEquals(Month.OCTOBER, offsetDateTime.getMonth(), "Month value");
assertEquals(2013, offsetDateTime.getYear(), "Year value");