mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-20 22:18:43 +00:00
Compare commits
40 Commits
version-0.
...
wip/qqq-ta
Author | SHA1 | Date | |
---|---|---|---|
ecffda0e18 | |||
acb311ee52 | |||
4a53c0d42a | |||
fa5c4f715f | |||
7a56d1ae22 | |||
1d309afbea | |||
02fc031e09 | |||
e57292f10f | |||
b4a63e6e1b | |||
0d78555a05 | |||
6a1db1c533 | |||
fabde303ab | |||
6d173d5485 | |||
79ac48b7f9 | |||
f7c8513845 | |||
be30422c18 | |||
53c005051e | |||
3879d5412c | |||
d596346c44 | |||
ac88def08c | |||
29bb7252e8 | |||
f0bd6b4b80 | |||
726075f041 | |||
67a1afdc1a | |||
c832028961 | |||
774309e846 | |||
a19a516fc0 | |||
e153d3a7b4 | |||
34a1755e44 | |||
b4a2ba9582 | |||
9bb6600a9d | |||
4f081e7c79 | |||
a0a43d48f5 | |||
7c4e06abcc | |||
39d714fbb1 | |||
6975069049 | |||
81e4d5d36d | |||
71672d46ee | |||
75c84cd0ff | |||
0ff98ce7ea |
2
pom.xml
2
pom.xml
@ -44,7 +44,7 @@
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<revision>0.16.0</revision>
|
||||
<revision>0.18.0-SNAPSHOT</revision>
|
||||
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||
|
@ -46,6 +46,7 @@ import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.security.RecordSecurityLock;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QUser;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTableAccessor;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.Pair;
|
||||
|
||||
@ -177,7 +178,6 @@ public class AuditAction extends AbstractQActionFunction<AuditInput, AuditOutput
|
||||
////////////////////////////////////////////////
|
||||
// map names to ids and handle default values //
|
||||
////////////////////////////////////////////////
|
||||
Integer auditTableId = getIdForName("auditTable", auditSingleInput.getAuditTableName());
|
||||
Integer auditUserId = getIdForName("auditUser", Objects.requireNonNullElse(auditSingleInput.getAuditUserName(), getSessionUserName()));
|
||||
Instant timestamp = Objects.requireNonNullElse(auditSingleInput.getTimestamp(), Instant.now());
|
||||
|
||||
@ -185,7 +185,7 @@ public class AuditAction extends AbstractQActionFunction<AuditInput, AuditOutput
|
||||
// build record //
|
||||
//////////////////
|
||||
QRecord record = new QRecord()
|
||||
.withValue("auditTableId", auditTableId)
|
||||
.withValue("tableId", QQQTableAccessor.getTableId(auditSingleInput.getAuditTableName()))
|
||||
.withValue("auditUserId", auditUserId)
|
||||
.withValue("timestamp", timestamp)
|
||||
.withValue("message", auditSingleInput.getMessage())
|
||||
@ -287,15 +287,6 @@ public class AuditAction extends AbstractQActionFunction<AuditInput, AuditOutput
|
||||
insertInput.setTableName(tableName);
|
||||
QRecord record = new QRecord().withValue("name", nameValue);
|
||||
|
||||
if(tableName.equals("auditTable"))
|
||||
{
|
||||
QTableMetaData table = QContext.getQInstance().getTable(nameValue);
|
||||
if(table != null)
|
||||
{
|
||||
record.setValue("label", table.getLabel());
|
||||
}
|
||||
}
|
||||
|
||||
insertInput.setRecords(List.of(record));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
id = insertOutput.getRecords().get(0).getValueInteger("id");
|
||||
|
@ -45,6 +45,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.QTableAut
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TableAutomationAction;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TriggerEvent;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTableAccessor;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import org.apache.commons.lang.NotImplementedException;
|
||||
|
||||
@ -171,7 +172,7 @@ public class RecordAutomationStatusUpdater
|
||||
CountInput countInput = new CountInput();
|
||||
countInput.setTableName(TableTrigger.TABLE_NAME);
|
||||
countInput.setFilter(new QQueryFilter(
|
||||
new QFilterCriteria("tableName", QCriteriaOperator.EQUALS, table.getName()),
|
||||
new QFilterCriteria("tableId", QCriteriaOperator.EQUALS, QQQTableAccessor.getTableId(table.getName())),
|
||||
new QFilterCriteria(triggerEvent.equals(TriggerEvent.POST_INSERT) ? "postInsert" : "postUpdate", QCriteriaOperator.EQUALS, true)
|
||||
));
|
||||
CountOutput countOutput = new CountAction().execute(countInput);
|
||||
|
@ -66,6 +66,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TableAuto
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.automation.TriggerEvent;
|
||||
import com.kingsrook.qqq.backend.core.model.savedfilters.SavedFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTableAccessor;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
@ -270,7 +271,7 @@ public class PollingAutomationPerTableRunner implements Runnable
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(TableTrigger.TABLE_NAME);
|
||||
queryInput.setFilter(new QQueryFilter(
|
||||
new QFilterCriteria("tableName", QCriteriaOperator.EQUALS, table.getName()),
|
||||
new QFilterCriteria("tableId", QCriteriaOperator.EQUALS, QQQTableAccessor.getTableId(table.getName())),
|
||||
new QFilterCriteria(triggerEvent.equals(TriggerEvent.POST_INSERT) ? "postInsert" : "postUpdate", QCriteriaOperator.EQUALS, true)
|
||||
));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
|
@ -29,6 +29,7 @@ import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
@ -42,18 +43,16 @@ import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
/*******************************************************************************
|
||||
** Standard/re-usable post-insert customizer, for the use case where, when we
|
||||
** do an insert into table "parent", we want a record automatically inserted into
|
||||
** table "child", and there's a foreign key in "parent", pointed at "child"
|
||||
** e.g., named: "parent.childId".
|
||||
** table "child". Optionally (based on RelationshipType), there can be a foreign
|
||||
** key in "parent", pointed at "child". e.g., named: "parent.childId".
|
||||
**
|
||||
** A similar use-case would have the foreign key in the child table - in which case,
|
||||
** we could add a "Type" enum, plus abstract method to get our "Type", then logic
|
||||
** to switch behavior based on type. See existing type enum, but w/ only 1 case :)
|
||||
*******************************************************************************/
|
||||
public abstract class ChildInserterPostInsertCustomizer extends AbstractPostInsertCustomizer
|
||||
{
|
||||
public enum RelationshipType
|
||||
{
|
||||
PARENT_POINTS_AT_CHILD
|
||||
PARENT_POINTS_AT_CHILD,
|
||||
CHILD_POINTS_AT_PARENT
|
||||
}
|
||||
|
||||
|
||||
@ -68,10 +67,17 @@ public abstract class ChildInserterPostInsertCustomizer extends AbstractPostInse
|
||||
*******************************************************************************/
|
||||
public abstract String getChildTableName();
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public abstract String getForeignKeyFieldName();
|
||||
public String getForeignKeyFieldName()
|
||||
{
|
||||
return (null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
@ -88,7 +94,7 @@ public abstract class ChildInserterPostInsertCustomizer extends AbstractPostInse
|
||||
{
|
||||
try
|
||||
{
|
||||
List<QRecord> rs = new ArrayList<>();
|
||||
List<QRecord> rs = records;
|
||||
List<QRecord> childrenToInsert = new ArrayList<>();
|
||||
QTableMetaData table = getInsertInput().getTable();
|
||||
QTableMetaData childTable = getInsertInput().getInstance().getTable(getChildTableName());
|
||||
@ -97,12 +103,37 @@ public abstract class ChildInserterPostInsertCustomizer extends AbstractPostInse
|
||||
// iterate over the inserted records, building a list child records to insert //
|
||||
// for ones missing a value in the foreign key field. //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
for(QRecord record : records)
|
||||
switch(getRelationshipType())
|
||||
{
|
||||
if(record.getValue(getForeignKeyFieldName()) == null)
|
||||
case PARENT_POINTS_AT_CHILD ->
|
||||
{
|
||||
childrenToInsert.add(buildChildForRecord(record));
|
||||
String foreignKeyFieldName = getForeignKeyFieldName();
|
||||
try
|
||||
{
|
||||
table.getField(foreignKeyFieldName);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new QRuntimeException("For RelationshipType.PARENT_POINTS_AT_CHILD, a valid foreignKeyFieldName in the parent table must be given. "
|
||||
+ "[" + foreignKeyFieldName + "] is not a valid field name in table [" + table.getName() + "]");
|
||||
}
|
||||
|
||||
for(QRecord record : records)
|
||||
{
|
||||
if(record.getValue(foreignKeyFieldName) == null)
|
||||
{
|
||||
childrenToInsert.add(buildChildForRecord(record));
|
||||
}
|
||||
}
|
||||
}
|
||||
case CHILD_POINTS_AT_PARENT ->
|
||||
{
|
||||
for(QRecord record : records)
|
||||
{
|
||||
childrenToInsert.add(buildChildForRecord(record));
|
||||
}
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected value: " + getRelationshipType());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
@ -129,51 +160,70 @@ public abstract class ChildInserterPostInsertCustomizer extends AbstractPostInse
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// for the PARENT_POINTS_AT_CHILD relationship type:
|
||||
// iterate over the original list of records again - for any that need a child (e.g., are missing //
|
||||
// foreign key), set their foreign key to a newly inserted child's key, and add them to be updated. //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
List<QRecord> recordsToUpdate = new ArrayList<>();
|
||||
for(QRecord record : records)
|
||||
switch(getRelationshipType())
|
||||
{
|
||||
Serializable primaryKey = record.getValue(table.getPrimaryKeyField());
|
||||
if(record.getValue(getForeignKeyFieldName()) == null)
|
||||
case PARENT_POINTS_AT_CHILD ->
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// get the corresponding child record, if it has any errors, set that as a warning in the parent //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QRecord childRecord = insertedRecordIterator.next();
|
||||
if(CollectionUtils.nullSafeHasContents(childRecord.getErrors()))
|
||||
rs = new ArrayList<>();
|
||||
List<QRecord> recordsToUpdate = new ArrayList<>();
|
||||
for(QRecord record : records)
|
||||
{
|
||||
for(QStatusMessage error : childRecord.getErrors())
|
||||
Serializable primaryKey = record.getValue(table.getPrimaryKeyField());
|
||||
if(record.getValue(getForeignKeyFieldName()) == null)
|
||||
{
|
||||
record.addWarning(new QWarningMessage("Error creating child " + childTable.getLabel() + " (" + error.toString() + ")"));
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// get the corresponding child record, if it has any errors, set that as a warning in the parent //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QRecord childRecord = insertedRecordIterator.next();
|
||||
if(CollectionUtils.nullSafeHasContents(childRecord.getErrors()))
|
||||
{
|
||||
for(QStatusMessage error : childRecord.getErrors())
|
||||
{
|
||||
record.addWarning(new QWarningMessage("Error creating child " + childTable.getLabel() + " (" + error.toString() + ")"));
|
||||
}
|
||||
rs.add(record);
|
||||
continue;
|
||||
}
|
||||
|
||||
Serializable foreignKey = childRecord.getValue(childTable.getPrimaryKeyField());
|
||||
recordsToUpdate.add(new QRecord().withValue(table.getPrimaryKeyField(), primaryKey).withValue(getForeignKeyFieldName(), foreignKey));
|
||||
record.setValue(getForeignKeyFieldName(), foreignKey);
|
||||
rs.add(record);
|
||||
}
|
||||
else
|
||||
{
|
||||
rs.add(record);
|
||||
}
|
||||
rs.add(record);
|
||||
continue;
|
||||
}
|
||||
|
||||
Serializable foreignKey = childRecord.getValue(childTable.getPrimaryKeyField());
|
||||
recordsToUpdate.add(new QRecord().withValue(table.getPrimaryKeyField(), primaryKey).withValue(getForeignKeyFieldName(), foreignKey));
|
||||
record.setValue(getForeignKeyFieldName(), foreignKey);
|
||||
rs.add(record);
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// update the originally inserted records to reference their new children //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(getInsertInput().getTableName());
|
||||
updateInput.setRecords(recordsToUpdate);
|
||||
updateInput.setTransaction(this.insertInput.getTransaction());
|
||||
new UpdateAction().execute(updateInput);
|
||||
}
|
||||
else
|
||||
case CHILD_POINTS_AT_PARENT ->
|
||||
{
|
||||
rs.add(record);
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// todo - some version of looking at the inserted children to confirm that they were inserted, and updating the parents with warnings if they weren't //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected value: " + getRelationshipType());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// update the originally inserted records to reference their new children //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(getInsertInput().getTableName());
|
||||
updateInput.setRecords(recordsToUpdate);
|
||||
updateInput.setTransaction(this.insertInput.getTransaction());
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
return (rs);
|
||||
}
|
||||
catch(RuntimeException re)
|
||||
{
|
||||
throw (re);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
throw new RuntimeException("Error inserting new child records for new parent records", e);
|
||||
|
@ -67,4 +67,15 @@ public interface BaseQueryInterface
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
default void cancelAction()
|
||||
{
|
||||
//////////////////////////////////////////////
|
||||
// initially at least, a noop in base class //
|
||||
//////////////////////////////////////////////
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -231,7 +231,19 @@ public class ExecuteCodeAction
|
||||
*******************************************************************************/
|
||||
public static void addApiUtilityToContext(Map<String, Serializable> context, ScriptRevision scriptRevision)
|
||||
{
|
||||
if(!StringUtils.hasContent(scriptRevision.getApiName()) || !StringUtils.hasContent(scriptRevision.getApiVersion()))
|
||||
addApiUtilityToContext(context, scriptRevision.getApiName(), scriptRevision.getApiVersion());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Try to (dynamically) load the ApiScriptUtils object from the api middleware
|
||||
** module -- in case the runtime doesn't have that module deployed (e.g, not in
|
||||
** the project pom).
|
||||
*******************************************************************************/
|
||||
public static void addApiUtilityToContext(Map<String, Serializable> context, String apiName, String apiVersion)
|
||||
{
|
||||
if(!StringUtils.hasContent(apiName) || !StringUtils.hasContent(apiVersion))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@ -239,7 +251,7 @@ public class ExecuteCodeAction
|
||||
try
|
||||
{
|
||||
Class<?> apiScriptUtilsClass = Class.forName("com.kingsrook.qqq.api.utils.ApiScriptUtils");
|
||||
Object apiScriptUtilsObject = apiScriptUtilsClass.getConstructor(String.class, String.class).newInstance(scriptRevision.getApiName(), scriptRevision.getApiVersion());
|
||||
Object apiScriptUtilsObject = apiScriptUtilsClass.getConstructor(String.class, String.class).newInstance(apiName, apiVersion);
|
||||
context.put("api", (Serializable) apiScriptUtilsObject);
|
||||
}
|
||||
catch(ClassNotFoundException e)
|
||||
|
@ -47,6 +47,7 @@ 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;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.Script;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTableAccessor;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
@ -83,7 +84,7 @@ public class RecordScriptTestInterface implements TestScriptActionInterface
|
||||
//////////////////////////////////////////////
|
||||
// look up the records being tested against //
|
||||
//////////////////////////////////////////////
|
||||
String tableName = script.getValueString("tableName");
|
||||
String tableName = QQQTableAccessor.getTableName(script.getValueInteger("tableId"));
|
||||
QTableMetaData table = QContext.getQInstance().getTable(tableName);
|
||||
if(table == null)
|
||||
{
|
||||
|
@ -41,6 +41,7 @@ public class Log4jCodeExecutionLogger implements QCodeExecutionLoggerInterface
|
||||
private QCodeReference qCodeReference;
|
||||
private String uuid = UUID.randomUUID().toString();
|
||||
|
||||
private boolean includeUUID = true;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -52,7 +53,7 @@ public class Log4jCodeExecutionLogger implements QCodeExecutionLoggerInterface
|
||||
this.qCodeReference = executeCodeInput.getCodeReference();
|
||||
|
||||
String inputString = StringUtils.safeTruncate(ValueUtils.getValueAsString(executeCodeInput.getInput()), 250, "...");
|
||||
LOG.info("Starting script execution: " + qCodeReference.getName() + ", uuid: " + uuid + ", with input: " + inputString);
|
||||
LOG.info("Starting script execution: " + qCodeReference.getName() + (includeUUID ? ", uuid: " + uuid : "") + ", with input: " + inputString);
|
||||
}
|
||||
|
||||
|
||||
@ -63,7 +64,7 @@ public class Log4jCodeExecutionLogger implements QCodeExecutionLoggerInterface
|
||||
@Override
|
||||
public void acceptLogLine(String logLine)
|
||||
{
|
||||
LOG.info("Script log: " + uuid + ": " + logLine);
|
||||
LOG.info("Script log: " + (includeUUID ? uuid + ": " : "") + logLine);
|
||||
}
|
||||
|
||||
|
||||
@ -74,7 +75,7 @@ public class Log4jCodeExecutionLogger implements QCodeExecutionLoggerInterface
|
||||
@Override
|
||||
public void acceptException(Exception exception)
|
||||
{
|
||||
LOG.info("Script Exception: " + uuid, exception);
|
||||
LOG.info("Script Exception: " + (includeUUID ? uuid : ""), exception);
|
||||
}
|
||||
|
||||
|
||||
@ -86,7 +87,38 @@ public class Log4jCodeExecutionLogger implements QCodeExecutionLoggerInterface
|
||||
public void acceptExecutionEnd(Serializable output)
|
||||
{
|
||||
String outputString = StringUtils.safeTruncate(ValueUtils.getValueAsString(output), 250, "...");
|
||||
LOG.info("Finished script execution: " + qCodeReference.getName() + ", uuid: " + uuid + ", with output: " + outputString);
|
||||
LOG.info("Finished script execution: " + qCodeReference.getName() + (includeUUID ? ", uuid: " + uuid : "") + ", with output: " + outputString);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for includeUUID
|
||||
*******************************************************************************/
|
||||
public boolean getIncludeUUID()
|
||||
{
|
||||
return (this.includeUUID);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for includeUUID
|
||||
*******************************************************************************/
|
||||
public void setIncludeUUID(boolean includeUUID)
|
||||
{
|
||||
this.includeUUID = includeUUID;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for includeUUID
|
||||
*******************************************************************************/
|
||||
public Log4jCodeExecutionLogger withIncludeUUID(boolean includeUUID)
|
||||
{
|
||||
this.includeUUID = includeUUID;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2022. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.scripts.logging;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.scripts.ExecuteCodeInput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.utils.ValueUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Implementation of a code execution logger that logs to System.out and
|
||||
** System.err (for exceptions)
|
||||
*******************************************************************************/
|
||||
public class SystemOutExecutionLogger implements QCodeExecutionLoggerInterface
|
||||
{
|
||||
private QCodeReference qCodeReference;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptExecutionStart(ExecuteCodeInput executeCodeInput)
|
||||
{
|
||||
this.qCodeReference = executeCodeInput.getCodeReference();
|
||||
|
||||
String inputString = ValueUtils.getValueAsString(executeCodeInput.getInput());
|
||||
System.out.println("Starting script execution: " + qCodeReference.getName() + ", with input: " + inputString);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptLogLine(String logLine)
|
||||
{
|
||||
System.out.println("Script log: " + logLine);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptException(Exception exception)
|
||||
{
|
||||
System.out.println("Script Exception: " + exception.getMessage());
|
||||
exception.printStackTrace();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void acceptExecutionEnd(Serializable output)
|
||||
{
|
||||
String outputString = ValueUtils.getValueAsString(output);
|
||||
System.out.println("Finished script execution: " + qCodeReference.getName() + ", with output: " + outputString);
|
||||
}
|
||||
|
||||
}
|
@ -26,6 +26,7 @@ import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.AggregateInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.helpers.QueryStatManager;
|
||||
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.aggregate.AggregateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
@ -41,6 +42,12 @@ import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||
*******************************************************************************/
|
||||
public class AggregateAction
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(AggregateAction.class);
|
||||
|
||||
private AggregateInterface aggregateInterface;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -56,7 +63,7 @@ public class AggregateAction
|
||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(aggregateInput.getBackend());
|
||||
|
||||
AggregateInterface aggregateInterface = qModule.getAggregateInterface();
|
||||
aggregateInterface = qModule.getAggregateInterface();
|
||||
aggregateInterface.setQueryStat(queryStat);
|
||||
AggregateOutput aggregateOutput = aggregateInterface.execute(aggregateInput);
|
||||
|
||||
@ -64,4 +71,20 @@ public class AggregateAction
|
||||
|
||||
return aggregateOutput;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void cancel()
|
||||
{
|
||||
if(aggregateInterface == null)
|
||||
{
|
||||
LOG.warn("aggregateInterface object was null when requested to cancel");
|
||||
return;
|
||||
}
|
||||
|
||||
aggregateInterface.cancelAction();
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import com.kingsrook.qqq.backend.core.actions.ActionHelper;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.CountInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.helpers.QueryStatManager;
|
||||
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.count.CountInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
@ -41,6 +42,12 @@ import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||
*******************************************************************************/
|
||||
public class CountAction
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(CountAction.class);
|
||||
|
||||
private CountInterface countInterface;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -56,7 +63,7 @@ public class CountAction
|
||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(countInput.getBackend());
|
||||
|
||||
CountInterface countInterface = qModule.getCountInterface();
|
||||
countInterface = qModule.getCountInterface();
|
||||
countInterface.setQueryStat(queryStat);
|
||||
CountOutput countOutput = countInterface.execute(countInput);
|
||||
|
||||
@ -64,4 +71,20 @@ public class CountAction
|
||||
|
||||
return countOutput;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void cancel()
|
||||
{
|
||||
if(countInterface == null)
|
||||
{
|
||||
LOG.warn("countInterface object was null when requested to cancel");
|
||||
return;
|
||||
}
|
||||
|
||||
countInterface.cancelAction();
|
||||
}
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ public class QueryAction
|
||||
private Optional<AbstractPostQueryCustomizer> postQueryRecordCustomizer;
|
||||
|
||||
private QueryInput queryInput;
|
||||
private QueryInterface queryInterface;
|
||||
private QPossibleValueTranslator qPossibleValueTranslator;
|
||||
|
||||
|
||||
@ -121,7 +122,7 @@ public class QueryAction
|
||||
QBackendModuleDispatcher qBackendModuleDispatcher = new QBackendModuleDispatcher();
|
||||
QBackendModuleInterface qModule = qBackendModuleDispatcher.getQBackendModule(backend);
|
||||
|
||||
QueryInterface queryInterface = qModule.getQueryInterface();
|
||||
queryInterface = qModule.getQueryInterface();
|
||||
queryInterface.setQueryStat(queryStat);
|
||||
QueryOutput queryOutput = queryInterface.execute(queryInput);
|
||||
|
||||
@ -339,4 +340,20 @@ public class QueryAction
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void cancel()
|
||||
{
|
||||
if(queryInterface == null)
|
||||
{
|
||||
LOG.warn("queryInterface object was null when requested to cancel");
|
||||
return;
|
||||
}
|
||||
|
||||
queryInterface.cancelAction();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.tables.helpers;
|
||||
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** For actions that may want to set a timeout, and cancel themselves if they run
|
||||
** too long - this class helps.
|
||||
**
|
||||
** Construct with the timeout (delay & timeUnit), and a runnable that takes care
|
||||
** of doing the cancel (e.g., cancelling a JDBC statement).
|
||||
**
|
||||
** Call start() to make a future get scheduled (note, if delay was null or <= 0,
|
||||
** then it doesn't get scheduled at all).
|
||||
**
|
||||
** Call cancel() if the action got far enough/completed, to cancel the future.
|
||||
**
|
||||
** You can check didTimeout (getDidTimeout()) to know if the timeout did occur.
|
||||
*******************************************************************************/
|
||||
public class ActionTimeoutHelper
|
||||
{
|
||||
private final Integer delay;
|
||||
private final TimeUnit timeUnit;
|
||||
private final Runnable runnable;
|
||||
private ScheduledFuture<?> future;
|
||||
|
||||
private boolean didTimeout = false;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public ActionTimeoutHelper(Integer delay, TimeUnit timeUnit, Runnable runnable)
|
||||
{
|
||||
this.delay = delay;
|
||||
this.timeUnit = timeUnit;
|
||||
this.runnable = runnable;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void start()
|
||||
{
|
||||
if(delay == null || delay <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
future = Executors.newSingleThreadScheduledExecutor().schedule(() ->
|
||||
{
|
||||
didTimeout = true;
|
||||
runnable.run();
|
||||
}, delay, timeUnit);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void cancel()
|
||||
{
|
||||
if(future != null)
|
||||
{
|
||||
future.cancel(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for didTimeout
|
||||
**
|
||||
*******************************************************************************/
|
||||
public boolean getDidTimeout()
|
||||
{
|
||||
return didTimeout;
|
||||
}
|
||||
|
||||
}
|
@ -31,16 +31,12 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.instances.QMetaDataVariableInterpreter;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterOrderBy;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
@ -54,11 +50,9 @@ import com.kingsrook.qqq.backend.core.model.querystats.QueryStatCriteriaField;
|
||||
import com.kingsrook.qqq.backend.core.model.querystats.QueryStatJoinTable;
|
||||
import com.kingsrook.qqq.backend.core.model.querystats.QueryStatOrderByField;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTablesMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTableAccessor;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
@ -363,8 +357,8 @@ public class QueryStatManager
|
||||
//////////////////////
|
||||
// set the table id //
|
||||
//////////////////////
|
||||
Integer qqqTableId = getQQQTableId(queryStat.getTableName());
|
||||
queryStat.setQqqTableId(qqqTableId);
|
||||
Integer tableId = QQQTableAccessor.getTableId(queryStat.getTableName());
|
||||
queryStat.setTableId(tableId);
|
||||
|
||||
//////////////////////////////
|
||||
// build join-table records //
|
||||
@ -374,7 +368,7 @@ public class QueryStatManager
|
||||
List<QueryStatJoinTable> queryStatJoinTableList = new ArrayList<>();
|
||||
for(String joinTableName : queryStat.getJoinTableNames())
|
||||
{
|
||||
queryStatJoinTableList.add(new QueryStatJoinTable().withQqqTableId(getQQQTableId(joinTableName)));
|
||||
queryStatJoinTableList.add(new QueryStatJoinTable().withTableId(QQQTableAccessor.getTableId(joinTableName)));
|
||||
}
|
||||
queryStat.setQueryStatJoinTableList(queryStatJoinTableList);
|
||||
}
|
||||
@ -385,14 +379,14 @@ public class QueryStatManager
|
||||
if(queryStat.getQueryFilter() != null && queryStat.getQueryFilter().hasAnyCriteria())
|
||||
{
|
||||
List<QueryStatCriteriaField> queryStatCriteriaFieldList = new ArrayList<>();
|
||||
processCriteriaFromFilter(qqqTableId, queryStatCriteriaFieldList, queryStat.getQueryFilter());
|
||||
processCriteriaFromFilter(tableId, queryStatCriteriaFieldList, queryStat.getQueryFilter());
|
||||
queryStat.setQueryStatCriteriaFieldList(queryStatCriteriaFieldList);
|
||||
}
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(queryStat.getQueryFilter().getOrderBys()))
|
||||
{
|
||||
List<QueryStatOrderByField> queryStatOrderByFieldList = new ArrayList<>();
|
||||
processOrderByFromFilter(qqqTableId, queryStatOrderByFieldList, queryStat.getQueryFilter());
|
||||
processOrderByFromFilter(tableId, queryStatOrderByFieldList, queryStat.getQueryFilter());
|
||||
queryStat.setQueryStatOrderByFieldList(queryStatOrderByFieldList);
|
||||
}
|
||||
|
||||
@ -434,7 +428,7 @@ public class QueryStatManager
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void processCriteriaFromFilter(Integer qqqTableId, List<QueryStatCriteriaField> queryStatCriteriaFieldList, QQueryFilter queryFilter) throws QException
|
||||
private static void processCriteriaFromFilter(Integer tableId, List<QueryStatCriteriaField> queryStatCriteriaFieldList, QQueryFilter queryFilter) throws QException
|
||||
{
|
||||
for(QFilterCriteria criteria : CollectionUtils.nonNullList(queryFilter.getCriteria()))
|
||||
{
|
||||
@ -452,13 +446,13 @@ public class QueryStatManager
|
||||
String[] parts = fieldName.split("\\.");
|
||||
if(parts.length > 1)
|
||||
{
|
||||
queryStatCriteriaField.setQqqTableId(getQQQTableId(parts[0]));
|
||||
queryStatCriteriaField.setTableId(QQQTableAccessor.getTableId(parts[0]));
|
||||
queryStatCriteriaField.setName(parts[1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
queryStatCriteriaField.setQqqTableId(qqqTableId);
|
||||
queryStatCriteriaField.setTableId(tableId);
|
||||
queryStatCriteriaField.setName(fieldName);
|
||||
}
|
||||
|
||||
@ -467,7 +461,7 @@ public class QueryStatManager
|
||||
|
||||
for(QQueryFilter subFilter : CollectionUtils.nonNullList(queryFilter.getSubFilters()))
|
||||
{
|
||||
processCriteriaFromFilter(qqqTableId, queryStatCriteriaFieldList, subFilter);
|
||||
processCriteriaFromFilter(tableId, queryStatCriteriaFieldList, subFilter);
|
||||
}
|
||||
}
|
||||
|
||||
@ -476,7 +470,7 @@ public class QueryStatManager
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void processOrderByFromFilter(Integer qqqTableId, List<QueryStatOrderByField> queryStatOrderByFieldList, QQueryFilter queryFilter) throws QException
|
||||
private static void processOrderByFromFilter(Integer tableId, List<QueryStatOrderByField> queryStatOrderByFieldList, QQueryFilter queryFilter) throws QException
|
||||
{
|
||||
for(QFilterOrderBy orderBy : CollectionUtils.nonNullList(queryFilter.getOrderBys()))
|
||||
{
|
||||
@ -490,13 +484,13 @@ public class QueryStatManager
|
||||
String[] parts = fieldName.split("\\.");
|
||||
if(parts.length > 1)
|
||||
{
|
||||
queryStatOrderByField.setQqqTableId(getQQQTableId(parts[0]));
|
||||
queryStatOrderByField.setTableId(QQQTableAccessor.getTableId(parts[0]));
|
||||
queryStatOrderByField.setName(parts[1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
queryStatOrderByField.setQqqTableId(qqqTableId);
|
||||
queryStatOrderByField.setTableId(tableId);
|
||||
queryStatOrderByField.setName(fieldName);
|
||||
}
|
||||
|
||||
@ -505,43 +499,6 @@ public class QueryStatManager
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static Integer getQQQTableId(String tableName) throws QException
|
||||
{
|
||||
/////////////////////////////
|
||||
// look in the cache table //
|
||||
/////////////////////////////
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(QQQTablesMetaDataProvider.QQQ_TABLE_CACHE_TABLE_NAME);
|
||||
getInput.setUniqueKey(MapBuilder.of("name", tableName));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
|
||||
////////////////////////
|
||||
// upon cache miss... //
|
||||
////////////////////////
|
||||
if(getOutput.getRecord() == null)
|
||||
{
|
||||
///////////////////////////////////////////////////////
|
||||
// insert the record (into the table, not the cache) //
|
||||
///////////////////////////////////////////////////////
|
||||
QTableMetaData tableMetaData = getInstance().qInstance.getTable(tableName);
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(QQQTable.TABLE_NAME);
|
||||
insertInput.setRecords(List.of(new QRecord().withValue("name", tableName).withValue("label", tableMetaData.getLabel())));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
|
||||
///////////////////////////////////
|
||||
// repeat the get from the cache //
|
||||
///////////////////////////////////
|
||||
getOutput = new GetAction().execute(getInput);
|
||||
}
|
||||
|
||||
return getOutput.getRecord().getValueInteger("id");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -40,6 +40,8 @@ public class AggregateInput extends AbstractTableActionInput
|
||||
private List<GroupBy> groupBys = new ArrayList<>();
|
||||
private Integer limit;
|
||||
|
||||
private Integer timeoutSeconds;
|
||||
|
||||
private List<QueryJoin> queryJoins = null;
|
||||
|
||||
|
||||
@ -269,4 +271,35 @@ public class AggregateInput extends AbstractTableActionInput
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for timeoutSeconds
|
||||
*******************************************************************************/
|
||||
public Integer getTimeoutSeconds()
|
||||
{
|
||||
return (this.timeoutSeconds);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for timeoutSeconds
|
||||
*******************************************************************************/
|
||||
public void setTimeoutSeconds(Integer timeoutSeconds)
|
||||
{
|
||||
this.timeoutSeconds = timeoutSeconds;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for timeoutSeconds
|
||||
*******************************************************************************/
|
||||
public AggregateInput withTimeoutSeconds(Integer timeoutSeconds)
|
||||
{
|
||||
this.timeoutSeconds = timeoutSeconds;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,6 +37,8 @@ public class CountInput extends AbstractTableActionInput
|
||||
{
|
||||
private QQueryFilter filter;
|
||||
|
||||
private Integer timeoutSeconds;
|
||||
|
||||
private List<QueryJoin> queryJoins = null;
|
||||
private Boolean includeDistinctCount = false;
|
||||
|
||||
@ -174,4 +176,35 @@ public class CountInput extends AbstractTableActionInput
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for timeoutSeconds
|
||||
*******************************************************************************/
|
||||
public Integer getTimeoutSeconds()
|
||||
{
|
||||
return (this.timeoutSeconds);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for timeoutSeconds
|
||||
*******************************************************************************/
|
||||
public void setTimeoutSeconds(Integer timeoutSeconds)
|
||||
{
|
||||
this.timeoutSeconds = timeoutSeconds;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for timeoutSeconds
|
||||
*******************************************************************************/
|
||||
public CountInput withTimeoutSeconds(Integer timeoutSeconds)
|
||||
{
|
||||
this.timeoutSeconds = timeoutSeconds;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ public class QueryInput extends AbstractTableActionInput implements QueryOrGetIn
|
||||
private QQueryFilter filter;
|
||||
|
||||
private RecordPipe recordPipe;
|
||||
private Integer timeoutSeconds;
|
||||
|
||||
private boolean shouldTranslatePossibleValues = false;
|
||||
private boolean shouldGenerateDisplayValues = false;
|
||||
@ -537,4 +538,35 @@ public class QueryInput extends AbstractTableActionInput implements QueryOrGetIn
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for timeoutSeconds
|
||||
*******************************************************************************/
|
||||
public Integer getTimeoutSeconds()
|
||||
{
|
||||
return (this.timeoutSeconds);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for timeoutSeconds
|
||||
*******************************************************************************/
|
||||
public void setTimeoutSeconds(Integer timeoutSeconds)
|
||||
{
|
||||
this.timeoutSeconds = timeoutSeconds;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for timeoutSeconds
|
||||
*******************************************************************************/
|
||||
public QueryInput withTimeoutSeconds(Integer timeoutSeconds)
|
||||
{
|
||||
this.timeoutSeconds = timeoutSeconds;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.possiblevalues.QPossibleVal
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Capability;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -46,7 +47,6 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
||||
*******************************************************************************/
|
||||
public class AuditsMetaDataProvider
|
||||
{
|
||||
public static final String TABLE_NAME_AUDIT_TABLE = "auditTable";
|
||||
public static final String TABLE_NAME_AUDIT_USER = "auditUser";
|
||||
public static final String TABLE_NAME_AUDIT = "audit";
|
||||
public static final String TABLE_NAME_AUDIT_DETAIL = "auditDetail";
|
||||
@ -72,10 +72,10 @@ public class AuditsMetaDataProvider
|
||||
{
|
||||
instance.addJoin(new QJoinMetaData()
|
||||
.withLeftTable(TABLE_NAME_AUDIT)
|
||||
.withRightTable(TABLE_NAME_AUDIT_TABLE)
|
||||
.withRightTable(QQQTable.TABLE_NAME)
|
||||
.withInferredName()
|
||||
.withType(JoinType.MANY_TO_ONE)
|
||||
.withJoinOn(new JoinOn("auditTableId", "id")));
|
||||
.withJoinOn(new JoinOn("tableId", "id")));
|
||||
|
||||
instance.addJoin(new QJoinMetaData()
|
||||
.withLeftTable(TABLE_NAME_AUDIT)
|
||||
@ -113,11 +113,6 @@ public class AuditsMetaDataProvider
|
||||
*******************************************************************************/
|
||||
public void defineStandardAuditPossibleValueSources(QInstance instance)
|
||||
{
|
||||
instance.addPossibleValueSource(new QPossibleValueSource()
|
||||
.withName(TABLE_NAME_AUDIT_TABLE)
|
||||
.withTableName(TABLE_NAME_AUDIT_TABLE)
|
||||
);
|
||||
|
||||
instance.addPossibleValueSource(new QPossibleValueSource()
|
||||
.withName(TABLE_NAME_AUDIT_USER)
|
||||
.withTableName(TABLE_NAME_AUDIT_USER)
|
||||
@ -138,7 +133,6 @@ public class AuditsMetaDataProvider
|
||||
{
|
||||
List<QTableMetaData> rs = new ArrayList<>();
|
||||
rs.add(enrich(backendDetailEnricher, defineAuditUserTable(backendName)));
|
||||
rs.add(enrich(backendDetailEnricher, defineAuditTableTable(backendName)));
|
||||
rs.add(enrich(backendDetailEnricher, defineAuditTable(backendName)));
|
||||
rs.add(enrich(backendDetailEnricher, defineAuditDetailTable(backendName)));
|
||||
return (rs);
|
||||
@ -160,29 +154,6 @@ public class AuditsMetaDataProvider
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QTableMetaData defineAuditTableTable(String backendName)
|
||||
{
|
||||
return new QTableMetaData()
|
||||
.withName(TABLE_NAME_AUDIT_TABLE)
|
||||
.withBackendName(backendName)
|
||||
.withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.NONE))
|
||||
.withRecordLabelFormat("%s")
|
||||
.withRecordLabelFields("label")
|
||||
.withPrimaryKeyField("id")
|
||||
.withUniqueKey(new UniqueKey("name"))
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||
.withField(new QFieldMetaData("name", QFieldType.STRING))
|
||||
.withField(new QFieldMetaData("label", QFieldType.STRING))
|
||||
.withField(new QFieldMetaData("createDate", QFieldType.DATE_TIME))
|
||||
.withField(new QFieldMetaData("modifyDate", QFieldType.DATE_TIME))
|
||||
.withoutCapabilities(Capability.TABLE_INSERT, Capability.TABLE_UPDATE, Capability.TABLE_DELETE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -215,10 +186,10 @@ public class AuditsMetaDataProvider
|
||||
.withBackendName(backendName)
|
||||
.withAuditRules(new QAuditRules().withAuditLevel(AuditLevel.NONE))
|
||||
.withRecordLabelFormat("%s %s")
|
||||
.withRecordLabelFields("auditTableId", "recordId")
|
||||
.withRecordLabelFields("tableId", "recordId")
|
||||
.withPrimaryKeyField("id")
|
||||
.withField(new QFieldMetaData("id", QFieldType.INTEGER))
|
||||
.withField(new QFieldMetaData("auditTableId", QFieldType.INTEGER).withPossibleValueSourceName(TABLE_NAME_AUDIT_TABLE))
|
||||
.withField(new QFieldMetaData("tableId", QFieldType.INTEGER).withPossibleValueSourceName(QQQTable.TABLE_NAME))
|
||||
.withField(new QFieldMetaData("auditUserId", QFieldType.INTEGER).withPossibleValueSourceName(TABLE_NAME_AUDIT_USER))
|
||||
.withField(new QFieldMetaData("recordId", QFieldType.INTEGER))
|
||||
.withField(new QFieldMetaData("message", QFieldType.STRING).withMaxLength(250).withBehavior(ValueTooLongBehavior.TRUNCATE_ELLIPSIS))
|
||||
|
@ -27,9 +27,9 @@ 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.model.metadata.tables.TablesPossibleValueSourceMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.model.savedfilters.SavedFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.Script;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -48,8 +48,8 @@ public class TableTrigger extends QRecordEntity
|
||||
@QField(isEditable = false)
|
||||
private Instant modifyDate;
|
||||
|
||||
@QField(possibleValueSourceName = TablesPossibleValueSourceMetaDataProvider.NAME)
|
||||
private String tableName;
|
||||
@QField(possibleValueSourceName = QQQTable.TABLE_NAME, backendName = "qqq_table_id")
|
||||
private Integer tableId;
|
||||
|
||||
@QField(possibleValueSourceName = SavedFilter.TABLE_NAME)
|
||||
private Integer filterId;
|
||||
@ -191,40 +191,6 @@ public class TableTrigger extends QRecordEntity
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getTableName()
|
||||
{
|
||||
return tableName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public TableTrigger withTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for filterId
|
||||
**
|
||||
@ -390,4 +356,35 @@ public class TableTrigger extends QRecordEntity
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableId
|
||||
*******************************************************************************/
|
||||
public Integer getTableId()
|
||||
{
|
||||
return (this.tableId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableId
|
||||
*******************************************************************************/
|
||||
public void setTableId(Integer tableId)
|
||||
{
|
||||
this.tableId = tableId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableId
|
||||
*******************************************************************************/
|
||||
public TableTrigger withTableId(Integer tableId)
|
||||
{
|
||||
this.tableId = tableId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -32,6 +32,9 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
*******************************************************************************/
|
||||
public abstract class MetaDataProducer<T extends TopLevelMetaDataInterface>
|
||||
{
|
||||
public static final int DEFAULT_SORT_ORDER = 500;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Produce the metaData object. Generally, you don't want to add it to the instance
|
||||
@ -43,11 +46,13 @@ public abstract class MetaDataProducer<T extends TopLevelMetaDataInterface>
|
||||
|
||||
/*******************************************************************************
|
||||
** In case this producer needs to run before (or after) others, this method
|
||||
** can help influence that (e.g., if used by MetaDataProducerHelper).
|
||||
** can control influence that (e.g., if used by MetaDataProducerHelper).
|
||||
**
|
||||
** Smaller values run first.
|
||||
*******************************************************************************/
|
||||
public int getSortOrder()
|
||||
{
|
||||
return (500);
|
||||
return (DEFAULT_SORT_ORDER);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
package com.kingsrook.qqq.backend.core.model.metadata.frontend;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude.Include;
|
||||
@ -38,14 +39,15 @@ import com.kingsrook.qqq.backend.core.model.metadata.fields.QFieldType;
|
||||
@JsonInclude(Include.NON_NULL)
|
||||
public class QFrontendFieldMetaData
|
||||
{
|
||||
private String name;
|
||||
private String label;
|
||||
private QFieldType type;
|
||||
private boolean isRequired;
|
||||
private boolean isEditable;
|
||||
private boolean isHeavy;
|
||||
private String possibleValueSourceName;
|
||||
private String displayFormat;
|
||||
private String name;
|
||||
private String label;
|
||||
private QFieldType type;
|
||||
private boolean isRequired;
|
||||
private boolean isEditable;
|
||||
private boolean isHeavy;
|
||||
private String possibleValueSourceName;
|
||||
private String displayFormat;
|
||||
private Serializable defaultValue;
|
||||
|
||||
private List<FieldAdornment> adornments;
|
||||
|
||||
@ -69,6 +71,7 @@ public class QFrontendFieldMetaData
|
||||
this.possibleValueSourceName = fieldMetaData.getPossibleValueSourceName();
|
||||
this.displayFormat = fieldMetaData.getDisplayFormat();
|
||||
this.adornments = fieldMetaData.getAdornments();
|
||||
this.defaultValue = fieldMetaData.getDefaultValue();
|
||||
}
|
||||
|
||||
|
||||
@ -170,4 +173,14 @@ public class QFrontendFieldMetaData
|
||||
return possibleValueSourceName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for defaultValue
|
||||
**
|
||||
*******************************************************************************/
|
||||
public Serializable getDefaultValue()
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ package com.kingsrook.qqq.backend.core.model.metadata.layout;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.TopLevelMetaDataInterface;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.MetaDataWithPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.permissions.QPermissionRules;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
@ -36,7 +38,7 @@ import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
** 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, MetaDataWithPermissionRules
|
||||
public class QAppMetaData implements QAppChildMetaData, MetaDataWithPermissionRules, TopLevelMetaDataInterface
|
||||
{
|
||||
private String name;
|
||||
private String label;
|
||||
@ -414,4 +416,14 @@ public class QAppMetaData implements QAppChildMetaData, MetaDataWithPermissionRu
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void addSelfToInstance(QInstance qInstance)
|
||||
{
|
||||
qInstance.addApp(this);
|
||||
}
|
||||
}
|
||||
|
@ -53,8 +53,8 @@ public class QueryStat extends QRecordEntity
|
||||
@QField()
|
||||
private Integer firstResultMillis;
|
||||
|
||||
@QField(label = "Table", possibleValueSourceName = QQQTable.TABLE_NAME)
|
||||
private Integer qqqTableId;
|
||||
@QField(possibleValueSourceName = QQQTable.TABLE_NAME, backendName = "qqq_table_id")
|
||||
private Integer tableId;
|
||||
|
||||
@QField(maxLength = 100, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||
private String action;
|
||||
@ -413,31 +413,31 @@ public class QueryStat extends QRecordEntity
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for qqqTableId
|
||||
** Getter for tableId
|
||||
*******************************************************************************/
|
||||
public Integer getQqqTableId()
|
||||
public Integer getTableId()
|
||||
{
|
||||
return (this.qqqTableId);
|
||||
return (this.tableId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for qqqTableId
|
||||
** Setter for tableId
|
||||
*******************************************************************************/
|
||||
public void setQqqTableId(Integer qqqTableId)
|
||||
public void setTableId(Integer tableId)
|
||||
{
|
||||
this.qqqTableId = qqqTableId;
|
||||
this.tableId = tableId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for qqqTableId
|
||||
** Fluent setter for tableId
|
||||
*******************************************************************************/
|
||||
public QueryStat withQqqTableId(Integer qqqTableId)
|
||||
public QueryStat withTableId(Integer tableId)
|
||||
{
|
||||
this.qqqTableId = qqqTableId;
|
||||
this.tableId = tableId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
@ -42,8 +42,8 @@ public class QueryStatCriteriaField extends QRecordEntity
|
||||
@QField(possibleValueSourceName = QueryStat.TABLE_NAME)
|
||||
private Integer queryStatId;
|
||||
|
||||
@QField(label = "Table", possibleValueSourceName = QQQTable.TABLE_NAME)
|
||||
private Integer qqqTableId;
|
||||
@QField(possibleValueSourceName = QQQTable.TABLE_NAME, backendName = "qqq_table_id")
|
||||
private Integer tableId;
|
||||
|
||||
@QField(maxLength = 50, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||
private String name;
|
||||
@ -138,31 +138,31 @@ public class QueryStatCriteriaField extends QRecordEntity
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for qqqTableId
|
||||
** Getter for tableId
|
||||
*******************************************************************************/
|
||||
public Integer getQqqTableId()
|
||||
public Integer getTableId()
|
||||
{
|
||||
return (this.qqqTableId);
|
||||
return (this.tableId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for qqqTableId
|
||||
** Setter for tableId
|
||||
*******************************************************************************/
|
||||
public void setQqqTableId(Integer qqqTableId)
|
||||
public void setTableId(Integer tableId)
|
||||
{
|
||||
this.qqqTableId = qqqTableId;
|
||||
this.tableId = tableId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for qqqTableId
|
||||
** Fluent setter for tableId
|
||||
*******************************************************************************/
|
||||
public QueryStatCriteriaField withQqqTableId(Integer qqqTableId)
|
||||
public QueryStatCriteriaField withTableId(Integer tableId)
|
||||
{
|
||||
this.qqqTableId = qqqTableId;
|
||||
this.tableId = tableId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
@ -42,8 +42,8 @@ public class QueryStatJoinTable extends QRecordEntity
|
||||
@QField(possibleValueSourceName = QueryStat.TABLE_NAME)
|
||||
private Integer queryStatId;
|
||||
|
||||
@QField(label = "Table", possibleValueSourceName = QQQTable.TABLE_NAME)
|
||||
private Integer qqqTableId;
|
||||
@QField(possibleValueSourceName = QQQTable.TABLE_NAME, backendName = "qqq_table_id")
|
||||
private Integer tableId;
|
||||
|
||||
@QField(maxLength = 10, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||
private String type;
|
||||
@ -132,31 +132,31 @@ public class QueryStatJoinTable extends QRecordEntity
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for qqqTableId
|
||||
** Getter for tableId
|
||||
*******************************************************************************/
|
||||
public Integer getQqqTableId()
|
||||
public Integer getTableId()
|
||||
{
|
||||
return (this.qqqTableId);
|
||||
return (this.tableId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for qqqTableId
|
||||
** Setter for tableId
|
||||
*******************************************************************************/
|
||||
public void setQqqTableId(Integer qqqTableId)
|
||||
public void setTableId(Integer tableId)
|
||||
{
|
||||
this.qqqTableId = qqqTableId;
|
||||
this.tableId = tableId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for qqqTableId
|
||||
** Fluent setter for tableId
|
||||
*******************************************************************************/
|
||||
public QueryStatJoinTable withQqqTableId(Integer qqqTableId)
|
||||
public QueryStatJoinTable withTableId(Integer tableId)
|
||||
{
|
||||
this.qqqTableId = qqqTableId;
|
||||
this.tableId = tableId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,7 @@ public class QueryStatMetaDataProvider
|
||||
.withRecordLabelFields("id")
|
||||
.withPrimaryKeyField("id")
|
||||
.withFieldsFromEntity(QueryStat.class)
|
||||
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "action", "qqqTableId", "sessionId")))
|
||||
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "action", "tableId", "sessionId")))
|
||||
.withSection(new QFieldSection("data", new QIcon().withName("dataset"), Tier.T2, List.of("queryText", "startTimestamp", "firstResultTimestamp", "firstResultMillis")))
|
||||
.withSection(new QFieldSection("joins", new QIcon().withName("merge"), Tier.T2).withWidgetName(joinTablesJoinName + "Widget"))
|
||||
.withSection(new QFieldSection("criteria", new QIcon().withName("filter_alt"), Tier.T2).withWidgetName(criteriaFieldsJoinName + "Widget"))
|
||||
|
@ -42,8 +42,8 @@ public class QueryStatOrderByField extends QRecordEntity
|
||||
@QField(possibleValueSourceName = QueryStat.TABLE_NAME)
|
||||
private Integer queryStatId;
|
||||
|
||||
@QField(label = "Table", possibleValueSourceName = QQQTable.TABLE_NAME)
|
||||
private Integer qqqTableId;
|
||||
@QField(possibleValueSourceName = QQQTable.TABLE_NAME, backendName = "qqq_table_id")
|
||||
private Integer tableId;
|
||||
|
||||
@QField(maxLength = 50, valueTooLongBehavior = ValueTooLongBehavior.TRUNCATE_ELLIPSIS)
|
||||
private String name;
|
||||
@ -132,31 +132,31 @@ public class QueryStatOrderByField extends QRecordEntity
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for qqqTableId
|
||||
** Getter for tableId
|
||||
*******************************************************************************/
|
||||
public Integer getQqqTableId()
|
||||
public Integer getTableId()
|
||||
{
|
||||
return (this.qqqTableId);
|
||||
return (this.tableId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for qqqTableId
|
||||
** Setter for tableId
|
||||
*******************************************************************************/
|
||||
public void setQqqTableId(Integer qqqTableId)
|
||||
public void setTableId(Integer tableId)
|
||||
{
|
||||
this.qqqTableId = qqqTableId;
|
||||
this.tableId = tableId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for qqqTableId
|
||||
** Fluent setter for tableId
|
||||
*******************************************************************************/
|
||||
public QueryStatOrderByField withQqqTableId(Integer qqqTableId)
|
||||
public QueryStatOrderByField withTableId(Integer tableId)
|
||||
{
|
||||
this.qqqTableId = qqqTableId;
|
||||
this.tableId = tableId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ 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.model.tables.QQQTable;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -48,8 +49,8 @@ public class SavedFilter extends QRecordEntity
|
||||
@QField(isRequired = true)
|
||||
private String label;
|
||||
|
||||
@QField(isEditable = false)
|
||||
private String tableName;
|
||||
@QField(possibleValueSourceName = QQQTable.TABLE_NAME, backendName = "qqq_table_id")
|
||||
private Integer tableId;
|
||||
|
||||
@QField(isEditable = false)
|
||||
private String userId;
|
||||
@ -180,40 +181,6 @@ public class SavedFilter extends QRecordEntity
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public String getTableName()
|
||||
{
|
||||
return tableName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public void setTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableName
|
||||
**
|
||||
*******************************************************************************/
|
||||
public SavedFilter withTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for userId
|
||||
**
|
||||
@ -280,4 +247,35 @@ public class SavedFilter extends QRecordEntity
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableId
|
||||
*******************************************************************************/
|
||||
public Integer getTableId()
|
||||
{
|
||||
return (this.tableId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableId
|
||||
*******************************************************************************/
|
||||
public void setTableId(Integer tableId)
|
||||
{
|
||||
this.tableId = tableId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableId
|
||||
*******************************************************************************/
|
||||
public SavedFilter withTableId(Integer tableId)
|
||||
{
|
||||
this.tableId = tableId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ 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.model.metadata.tables.TablesPossibleValueSourceMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTable;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -52,8 +52,8 @@ public class Script extends QRecordEntity
|
||||
@QField(possibleValueSourceName = "scriptType")
|
||||
private Integer scriptTypeId;
|
||||
|
||||
@QField(possibleValueSourceName = TablesPossibleValueSourceMetaDataProvider.NAME)
|
||||
private String tableName;
|
||||
@QField(possibleValueSourceName = QQQTable.TABLE_NAME, backendName = "qqq_table_id")
|
||||
private Integer tableId;
|
||||
|
||||
@QField()
|
||||
private Integer maxBatchSize;
|
||||
@ -288,37 +288,6 @@ public class Script extends QRecordEntity
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableName
|
||||
*******************************************************************************/
|
||||
public String getTableName()
|
||||
{
|
||||
return (this.tableName);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableName
|
||||
*******************************************************************************/
|
||||
public void setTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableName
|
||||
*******************************************************************************/
|
||||
public Script withTableName(String tableName)
|
||||
{
|
||||
this.tableName = tableName;
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for maxBatchSize
|
||||
*******************************************************************************/
|
||||
@ -348,4 +317,35 @@ public class Script extends QRecordEntity
|
||||
return (this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Getter for tableId
|
||||
*******************************************************************************/
|
||||
public Integer getTableId()
|
||||
{
|
||||
return (this.tableId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Setter for tableId
|
||||
*******************************************************************************/
|
||||
public void setTableId(Integer tableId)
|
||||
{
|
||||
this.tableId = tableId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Fluent setter for tableId
|
||||
*******************************************************************************/
|
||||
public Script withTableId(Integer tableId)
|
||||
{
|
||||
this.tableId = tableId;
|
||||
return (this);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -66,6 +66,7 @@ import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwith
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.scripts.LoadScriptTestDetailsProcessStep;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.scripts.RunRecordScriptExtractStep;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.scripts.RunRecordScriptLoadStep;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.scripts.RunRecordScriptPreStep;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.scripts.RunRecordScriptTransformStep;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.scripts.StoreScriptRevisionProcessStep;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.scripts.TestScriptProcessStep;
|
||||
@ -96,7 +97,11 @@ public class ScriptsMetaDataProvider
|
||||
defineStandardScriptsPossibleValueSources(instance);
|
||||
defineStandardScriptsJoins(instance);
|
||||
defineStandardScriptsWidgets(instance);
|
||||
|
||||
// todo - change this from an enum-backed PVS to use qqqTable table, exposed in-app, in API
|
||||
// so api docs don't always need refreshed
|
||||
instance.addPossibleValueSource(TablesPossibleValueSourceMetaDataProvider.defineTablesPossibleValueSource(instance));
|
||||
|
||||
instance.addProcess(defineStoreScriptRevisionProcess());
|
||||
instance.addProcess(defineTestScriptProcess());
|
||||
instance.addProcess(defineLoadScriptTestDetailsProcess());
|
||||
@ -174,15 +179,25 @@ public class ScriptsMetaDataProvider
|
||||
.withLoadStepClass(RunRecordScriptLoadStep.class)
|
||||
.getProcessMetaData();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// add a screen before the extract step - where user selects their script //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
processMetaData.addStep(0, new QFrontendStepMetaData()
|
||||
.withName("input")
|
||||
.withComponent(new QFrontendComponentMetaData().withType(QComponentType.EDIT_FORM))
|
||||
.withFormField(new QFieldMetaData("scriptId", QFieldType.INTEGER).withPossibleValueSourceName(Script.TABLE_NAME)
|
||||
.withPossibleValueSourceFilter(new QQueryFilter(
|
||||
new QFilterCriteria("scriptType.name", QCriteriaOperator.EQUALS, SCRIPT_TYPE_NAME_RECORD),
|
||||
new QFilterCriteria("tableName", QCriteriaOperator.EQUALS, "${input.tableName}")
|
||||
new QFilterCriteria("tableId", QCriteriaOperator.EQUALS, "${input.tableId}")
|
||||
))));
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// now - insert a step before the input screen, where the table name gets read //
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
processMetaData.addStep(0, new QBackendStepMetaData()
|
||||
.withName("preStep")
|
||||
.withCode(new QCodeReference(RunRecordScriptPreStep.class)));
|
||||
|
||||
return (processMetaData);
|
||||
}
|
||||
|
||||
@ -383,16 +398,16 @@ public class ScriptsMetaDataProvider
|
||||
QTableMetaData tableMetaData = defineStandardTable(backendName, TableTrigger.TABLE_NAME, TableTrigger.class)
|
||||
.withRecordLabelFields("id")
|
||||
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id")))
|
||||
.withSection(new QFieldSection("contents", new QIcon().withName("data_object"), Tier.T2, List.of("tableName", "filterId", "scriptId", "priority", "postInsert", "postUpdate")))
|
||||
.withSection(new QFieldSection("contents", new QIcon().withName("data_object"), Tier.T2, List.of("tableId", "filterId", "scriptId", "priority", "postInsert", "postUpdate")))
|
||||
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")));
|
||||
|
||||
tableMetaData.getField("scriptId").withPossibleValueSourceFilter(new QQueryFilter(
|
||||
new QFilterCriteria("scriptType.name", QCriteriaOperator.EQUALS, SCRIPT_TYPE_NAME_RECORD),
|
||||
new QFilterCriteria("script.tableName", QCriteriaOperator.EQUALS, "${input.tableName}")
|
||||
new QFilterCriteria("script.tableId", QCriteriaOperator.EQUALS, "${input.tableId}")
|
||||
));
|
||||
|
||||
tableMetaData.getField("filterId").withPossibleValueSourceFilter(new QQueryFilter(
|
||||
new QFilterCriteria("tableName", QCriteriaOperator.EQUALS, "${input.tableName}")
|
||||
new QFilterCriteria("tableId", QCriteriaOperator.EQUALS, "${input.tableId}")
|
||||
));
|
||||
|
||||
return tableMetaData;
|
||||
@ -407,7 +422,7 @@ public class ScriptsMetaDataProvider
|
||||
{
|
||||
QTableMetaData tableMetaData = defineStandardTable(backendName, Script.TABLE_NAME, Script.class)
|
||||
.withSection(new QFieldSection("identity", new QIcon().withName("badge"), Tier.T1, List.of("id", "name", "scriptTypeId", "currentScriptRevisionId")))
|
||||
.withSection(new QFieldSection("recordScriptSettings", new QIcon().withName("table_rows"), Tier.T2, List.of("tableName", "maxBatchSize")))
|
||||
.withSection(new QFieldSection("recordScriptSettings", new QIcon().withName("table_rows"), Tier.T2, List.of("tableId", "maxBatchSize")))
|
||||
.withSection(new QFieldSection("contents", new QIcon().withName("data_object"), Tier.T2).withWidgetName("scriptViewer"))
|
||||
.withSection(new QFieldSection("dates", new QIcon().withName("calendar_month"), Tier.T3, List.of("createDate", "modifyDate")))
|
||||
.withSection(new QFieldSection("lines", new QIcon().withName("horizontal_rule"), Tier.T2).withWidgetName(QJoinMetaData.makeInferredJoinName(Script.TABLE_NAME, ScriptLog.TABLE_NAME)));
|
||||
|
@ -0,0 +1,137 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.tables;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** One-liner we can use to get a QQQTable record, or just its id (which we often want).
|
||||
** Will insert the record if it wasn't already there.
|
||||
** Also uses in-memory cache table, so rather cheap for normal use-case.
|
||||
*******************************************************************************/
|
||||
public class QQQTableAccessor
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QRecord getQQQTableRecord(String tableName) throws QException
|
||||
{
|
||||
/////////////////////////////
|
||||
// look in the cache table //
|
||||
/////////////////////////////
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(QQQTablesMetaDataProvider.QQQ_TABLE_CACHE_TABLE_NAME);
|
||||
getInput.setUniqueKey(MapBuilder.of("name", tableName));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
|
||||
////////////////////////
|
||||
// upon cache miss... //
|
||||
////////////////////////
|
||||
if(getOutput.getRecord() == null)
|
||||
{
|
||||
///////////////////////////////////////////////////////
|
||||
// insert the record (into the table, not the cache) //
|
||||
///////////////////////////////////////////////////////
|
||||
QTableMetaData tableMetaData = QContext.getQInstance().getTable(tableName);
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(QQQTable.TABLE_NAME);
|
||||
insertInput.setRecords(List.of(new QRecord().withValue("name", tableName).withValue("label", tableMetaData.getLabel())));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
|
||||
///////////////////////////////////
|
||||
// repeat the get from the cache //
|
||||
///////////////////////////////////
|
||||
getOutput = new GetAction().execute(getInput);
|
||||
}
|
||||
|
||||
return getOutput.getRecord();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QRecord getQQQTableRecord(Integer id) throws QException
|
||||
{
|
||||
/////////////////////////////
|
||||
// look in the cache table //
|
||||
/////////////////////////////
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(QQQTablesMetaDataProvider.QQQ_TABLE_CACHE_TABLE_NAME);
|
||||
getInput.setPrimaryKey(id);
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
|
||||
////////////////////////
|
||||
// upon cache miss... //
|
||||
////////////////////////
|
||||
if(getOutput.getRecord() == null)
|
||||
{
|
||||
GetInput sourceGetInput = new GetInput();
|
||||
sourceGetInput.setTableName(QQQTable.TABLE_NAME);
|
||||
sourceGetInput.setPrimaryKey(id);
|
||||
GetOutput sourceGetOutput = new GetAction().execute(sourceGetInput);
|
||||
|
||||
///////////////////////////////////
|
||||
// repeat the get from the cache //
|
||||
///////////////////////////////////
|
||||
getOutput = new GetAction().execute(sourceGetInput);
|
||||
}
|
||||
|
||||
return getOutput.getRecord();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static Integer getTableId(String tableName) throws QException
|
||||
{
|
||||
return (getQQQTableRecord(tableName).getValueInteger("id"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static String getTableName(Integer id) throws QException
|
||||
{
|
||||
return (getQQQTableRecord(id).getValueString("name"));
|
||||
}
|
||||
|
||||
}
|
@ -50,9 +50,20 @@ public class QQQTablesMetaDataProvider
|
||||
*******************************************************************************/
|
||||
public void defineAll(QInstance instance, String persistentBackendName, String cacheBackendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
{
|
||||
instance.addTable(defineQQQTable(persistentBackendName, backendDetailEnricher));
|
||||
instance.addTable(defineQQQTableCache(cacheBackendName, backendDetailEnricher));
|
||||
instance.addPossibleValueSource(defineQQQTablePossibleValueSource());
|
||||
if(instance.getTable(QQQTable.TABLE_NAME) == null)
|
||||
{
|
||||
instance.addTable(defineQQQTable(persistentBackendName, backendDetailEnricher));
|
||||
}
|
||||
|
||||
if(instance.getTable(QQQ_TABLE_CACHE_TABLE_NAME) == null)
|
||||
{
|
||||
instance.addTable(defineQQQTableCache(cacheBackendName, backendDetailEnricher));
|
||||
}
|
||||
|
||||
if(instance.getPossibleValueSource(QQQTable.TABLE_NAME) == null)
|
||||
{
|
||||
instance.addPossibleValueSource(defineQQQTablePossibleValueSource());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -127,6 +138,7 @@ public class QQQTablesMetaDataProvider
|
||||
return (new QPossibleValueSource()
|
||||
.withType(QPossibleValueSourceType.TABLE)
|
||||
.withName(QQQTable.TABLE_NAME)
|
||||
.withOrderByField("label")
|
||||
.withTableName(QQQTable.TABLE_NAME));
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,8 @@ package com.kingsrook.qqq.backend.core.processes.implementations.columnstats;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@ -303,14 +305,18 @@ public class ColumnStatsStep implements BackendStep
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// just in case any of these don't fit in an integer, use decimal for them all //
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
Aggregate countNonNullAggregate = new Aggregate(fieldName, AggregateOperator.COUNT).withFieldType(QFieldType.DECIMAL);
|
||||
Aggregate countDistinctAggregate = new Aggregate(fieldName, AggregateOperator.COUNT_DISTINCT).withFieldType(QFieldType.DECIMAL);
|
||||
Aggregate sumAggregate = new Aggregate(fieldName, AggregateOperator.SUM).withFieldType(QFieldType.DECIMAL);
|
||||
Aggregate avgAggregate = new Aggregate(fieldName, AggregateOperator.AVG).withFieldType(QFieldType.DECIMAL);
|
||||
Aggregate minAggregate = new Aggregate(fieldName, AggregateOperator.MIN);
|
||||
Aggregate maxAggregate = new Aggregate(fieldName, AggregateOperator.MAX);
|
||||
AggregateInput statsAggregateInput = new AggregateInput();
|
||||
Aggregate countTotalRowsAggregate = new Aggregate(table.getPrimaryKeyField(), AggregateOperator.COUNT).withFieldType(QFieldType.DECIMAL);
|
||||
Aggregate countNonNullAggregate = new Aggregate(fieldName, AggregateOperator.COUNT).withFieldType(QFieldType.DECIMAL);
|
||||
Aggregate countDistinctAggregate = new Aggregate(fieldName, AggregateOperator.COUNT_DISTINCT).withFieldType(QFieldType.DECIMAL);
|
||||
Aggregate sumAggregate = new Aggregate(fieldName, AggregateOperator.SUM).withFieldType(QFieldType.DECIMAL);
|
||||
Aggregate avgAggregate = new Aggregate(fieldName, AggregateOperator.AVG).withFieldType(QFieldType.DECIMAL);
|
||||
Aggregate minAggregate = new Aggregate(fieldName, AggregateOperator.MIN);
|
||||
Aggregate maxAggregate = new Aggregate(fieldName, AggregateOperator.MAX);
|
||||
|
||||
AggregateInput statsAggregateInput = new AggregateInput();
|
||||
statsAggregateInput.withAggregate(countTotalRowsAggregate);
|
||||
statsAggregateInput.withAggregate(countNonNullAggregate);
|
||||
|
||||
if(doCountDistinct)
|
||||
{
|
||||
statsAggregateInput.withAggregate(countDistinctAggregate);
|
||||
@ -332,6 +338,7 @@ public class ColumnStatsStep implements BackendStep
|
||||
statsAggregateInput.withAggregate(maxAggregate);
|
||||
}
|
||||
|
||||
BigDecimal totalRows = null;
|
||||
if(CollectionUtils.nullSafeHasContents(statsAggregateInput.getAggregates()))
|
||||
{
|
||||
statsAggregateInput.setTableName(tableName);
|
||||
@ -346,6 +353,8 @@ public class ColumnStatsStep implements BackendStep
|
||||
{
|
||||
AggregateResult statsAggregateResult = statsAggregateOutput.getResults().get(0);
|
||||
|
||||
totalRows = ValueUtils.getValueAsBigDecimal(statsAggregateResult.getAggregateValue(countTotalRowsAggregate));
|
||||
|
||||
statsRecord.setValue(countNonNullField.getName(), statsAggregateResult.getAggregateValue(countNonNullAggregate));
|
||||
if(doCountDistinct)
|
||||
{
|
||||
@ -388,6 +397,27 @@ public class ColumnStatsStep implements BackendStep
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// figure count%'s //
|
||||
/////////////////////
|
||||
if(totalRows == null)
|
||||
{
|
||||
totalRows = new BigDecimal(valueCounts.stream().mapToInt(r -> r.getValueInteger("count")).sum());
|
||||
}
|
||||
|
||||
if(totalRows != null && totalRows.compareTo(BigDecimal.ZERO) > 0)
|
||||
{
|
||||
BigDecimal oneHundred = new BigDecimal(100);
|
||||
for(QRecord valueCount : valueCounts)
|
||||
{
|
||||
BigDecimal percent = new BigDecimal(Objects.requireNonNullElse(valueCount.getValueInteger("count"), 0)).divide(totalRows, 4, RoundingMode.HALF_UP).multiply(oneHundred).setScale(2, RoundingMode.HALF_UP);
|
||||
valueCount.setValue("percent", percent);
|
||||
}
|
||||
|
||||
QFieldMetaData percentField = new QFieldMetaData("percent", QFieldType.DECIMAL).withDisplayFormat(DisplayFormat.PERCENT_POINT2).withLabel("Percent");
|
||||
QValueFormatter.setDisplayValuesInRecords(Map.of(fieldName, field, "percent", percentField), valueCounts);
|
||||
}
|
||||
|
||||
QInstanceEnricher qInstanceEnricher = new QInstanceEnricher(null);
|
||||
fields.forEach(qInstanceEnricher::enrichField);
|
||||
|
||||
|
@ -44,6 +44,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.savedfilters.SavedFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTableAccessor;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -100,7 +101,7 @@ public class QuerySavedFilterProcess implements BackendStep
|
||||
QueryInput input = new QueryInput();
|
||||
input.setTableName(SavedFilter.TABLE_NAME);
|
||||
input.setFilter(new QQueryFilter()
|
||||
.withCriteria(new QFilterCriteria("tableName", QCriteriaOperator.EQUALS, tableName))
|
||||
.withCriteria(new QFilterCriteria("tableId", QCriteriaOperator.EQUALS, QQQTableAccessor.getTableId(tableName)))
|
||||
.withOrderBy(new QFilterOrderBy("label")));
|
||||
|
||||
QueryOutput output = new QueryAction().execute(input);
|
||||
|
@ -42,6 +42,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QBackendStepMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.processes.QProcessMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.savedfilters.SavedFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTableAccessor;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -82,7 +83,7 @@ public class StoreSavedFilterProcess implements BackendStep
|
||||
QRecord qRecord = new QRecord()
|
||||
.withValue("id", runBackendStepInput.getValueInteger("id"))
|
||||
.withValue("label", runBackendStepInput.getValueString("label"))
|
||||
.withValue("tableName", runBackendStepInput.getValueString("tableName"))
|
||||
.withValue("tableId", QQQTableAccessor.getTableId(runBackendStepInput.getValueString("tableName")))
|
||||
.withValue("filterJson", runBackendStepInput.getValueString("filterJson"))
|
||||
.withValue("userId", runBackendStepInput.getSession().getUser().getIdReference());
|
||||
|
||||
|
@ -30,6 +30,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.Script;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTableAccessor;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamedwithfrontend.ExtractViaQueryStep;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
@ -58,6 +59,11 @@ public class RunRecordScriptExtractStep extends ExtractViaQueryStep
|
||||
|
||||
runBackendStepInput.addValue(FIELD_SOURCE_TABLE, tableName);
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// set this value, for the select-script possible-value filter //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
runBackendStepInput.addValue("tableId", QQQTableAccessor.getTableId(tableName));
|
||||
|
||||
Integer scriptId = runBackendStepInput.getValueInteger("scriptId");
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(Script.TABLE_NAME);
|
||||
|
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.processes.implementations.scripts;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.processes.BackendStep;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
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.tables.QQQTableAccessor;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.etl.streamed.StreamedETLProcess;
|
||||
import com.kingsrook.qqq.backend.core.utils.StringUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** pre-step for the run-record-script process. Help deal with this being
|
||||
** a generic process (e.g., no table name defined in the meta data).
|
||||
*******************************************************************************/
|
||||
public class RunRecordScriptPreStep implements BackendStep
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void run(RunBackendStepInput runBackendStepInput, RunBackendStepOutput runBackendStepOutput) throws QException
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// this is a generic (e.g., not table-specific) process - so we must be sure to set the tableName field in the expected slot. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
String tableName = runBackendStepInput.getValueString("tableName");
|
||||
if(!StringUtils.hasContent(tableName))
|
||||
{
|
||||
throw (new QException("Table name was not specified as input value"));
|
||||
}
|
||||
|
||||
runBackendStepInput.addValue(StreamedETLProcess.FIELD_SOURCE_TABLE, tableName);
|
||||
|
||||
/////////////////////////////////////////////////////////////////
|
||||
// set this value, for the select-script possible-value filter //
|
||||
/////////////////////////////////////////////////////////////////
|
||||
runBackendStepInput.addValue("tableId", QQQTableAccessor.getTableId(tableName));
|
||||
}
|
||||
|
||||
}
|
@ -159,6 +159,15 @@ public class StoreScriptRevisionProcessStep implements BackendStep
|
||||
.toQRecord());
|
||||
}
|
||||
}
|
||||
else if(StringUtils.hasContent(input.getValueString("contents")))
|
||||
{
|
||||
scriptRevisionFileRecords = new ArrayList<>();
|
||||
scriptRevisionFileRecords.add(new ScriptRevisionFile()
|
||||
.withScriptRevisionId(scriptRevisionId)
|
||||
.withFileName("Script.js")
|
||||
.withContents(input.getValueString("contents"))
|
||||
.toQRecord());
|
||||
}
|
||||
|
||||
if(CollectionUtils.nullSafeHasContents(scriptRevisionFileRecords))
|
||||
{
|
||||
|
@ -36,6 +36,7 @@ import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QSession;
|
||||
import com.kingsrook.qqq.backend.core.model.session.QUser;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTablesMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.processes.utils.GeneralProcessUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -59,6 +60,7 @@ class AuditActionTest extends BaseTest
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
new AuditsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
new QQQTablesMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
String userName = "John Doe";
|
||||
QContext.init(qInstance, new QSession().withUser(new QUser().withFullName(userName)));
|
||||
@ -69,7 +71,6 @@ class AuditActionTest extends BaseTest
|
||||
/////////////////////////////////////
|
||||
// make sure things can be fetched //
|
||||
/////////////////////////////////////
|
||||
GeneralProcessUtils.getRecordByFieldOrElseThrow("auditTable", "name", TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
GeneralProcessUtils.getRecordByFieldOrElseThrow("auditUser", "name", userName);
|
||||
QRecord auditRecord = GeneralProcessUtils.getRecordByFieldOrElseThrow("audit", "recordId", recordId);
|
||||
assertEquals("Test Audit", auditRecord.getValueString("message"));
|
||||
@ -85,6 +86,7 @@ class AuditActionTest extends BaseTest
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
new AuditsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
new QQQTablesMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
String userName = "John Doe";
|
||||
QContext.init(qInstance, new QSession().withUser(new QUser().withFullName(userName)));
|
||||
@ -123,6 +125,7 @@ class AuditActionTest extends BaseTest
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
new AuditsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
new QQQTablesMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
String userName = "John Doe";
|
||||
QContext.init(qInstance, new QSession().withUser(new QUser().withFullName(userName)));
|
||||
@ -137,7 +140,6 @@ class AuditActionTest extends BaseTest
|
||||
/////////////////////////////////////
|
||||
// make sure things can be fetched //
|
||||
/////////////////////////////////////
|
||||
GeneralProcessUtils.getRecordByFieldOrElseThrow("auditTable", "name", TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
GeneralProcessUtils.getRecordByFieldOrElseThrow("auditUser", "name", userName);
|
||||
QRecord auditRecord = GeneralProcessUtils.getRecordByFieldOrElseThrow("audit", "recordId", recordId1);
|
||||
assertEquals("Test Audit", auditRecord.getValueString("message"));
|
||||
@ -157,6 +159,7 @@ class AuditActionTest extends BaseTest
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
new AuditsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
new QQQTablesMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
String userName = "John Doe";
|
||||
QContext.init(qInstance, new QSession().withUser(new QUser().withFullName(userName)));
|
||||
@ -173,7 +176,6 @@ class AuditActionTest extends BaseTest
|
||||
/////////////////////////////////////
|
||||
// make sure things can be fetched //
|
||||
/////////////////////////////////////
|
||||
GeneralProcessUtils.getRecordByFieldOrElseThrow("auditTable", "name", TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
GeneralProcessUtils.getRecordByFieldOrElseThrow("auditUser", "name", userName);
|
||||
QRecord auditRecord = GeneralProcessUtils.getRecordByFieldOrElseThrow("audit", "recordId", recordId1);
|
||||
assertEquals("Test Audit", auditRecord.getValueString("message"));
|
||||
|
@ -37,6 +37,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.audits.AuditLevel;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.audits.QAuditRules;
|
||||
import com.kingsrook.qqq.backend.core.model.statusmessages.BadInputStatusMessage;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTablesMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryRecordStore;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -60,6 +61,7 @@ class DMLAuditActionTest extends BaseTest
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
new AuditsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
new QQQTablesMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
|
@ -70,7 +70,7 @@ class ChildInserterPostInsertCustomizerTest extends BaseTest
|
||||
void testEmptyCases() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
addPostInsertActionToTable(qInstance);
|
||||
addPostInsertActionToPersonTable(qInstance);
|
||||
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
@ -95,10 +95,21 @@ class ChildInserterPostInsertCustomizerTest extends BaseTest
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void addPostInsertActionToTable(QInstance qInstance)
|
||||
private static void addPostInsertActionToPersonTable(QInstance qInstance)
|
||||
{
|
||||
qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY)
|
||||
.withCustomizer(TableCustomizers.POST_INSERT_RECORD.getRole(), new QCodeReference(PersonPostInsertAddFavoriteShapeCustomizer.class));
|
||||
.withCustomizer(TableCustomizers.POST_INSERT_RECORD, new QCodeReference(PersonPostInsertAddFavoriteShapeCustomizer.class));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void addPostInsertActionToShapeTable(QInstance qInstance)
|
||||
{
|
||||
qInstance.getTable(TestUtils.TABLE_NAME_SHAPE)
|
||||
.withCustomizer(TableCustomizers.POST_INSERT_RECORD, new QCodeReference(ShapePostInsertAddPersonCustomizer.class));
|
||||
}
|
||||
|
||||
|
||||
@ -107,10 +118,10 @@ class ChildInserterPostInsertCustomizerTest extends BaseTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testSimpleCase() throws QException
|
||||
void testSimpleParentPointsAtChildCase() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
addPostInsertActionToTable(qInstance);
|
||||
addPostInsertActionToPersonTable(qInstance);
|
||||
|
||||
assertEquals(0, TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_SHAPE).size());
|
||||
|
||||
@ -135,10 +146,10 @@ class ChildInserterPostInsertCustomizerTest extends BaseTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testComplexCase() throws QException
|
||||
void testComplexParentPointsAtChildCase() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
addPostInsertActionToTable(qInstance);
|
||||
addPostInsertActionToPersonTable(qInstance);
|
||||
|
||||
assertEquals(0, TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_SHAPE).size());
|
||||
|
||||
@ -169,6 +180,34 @@ class ChildInserterPostInsertCustomizerTest extends BaseTest
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testSimpleChildPointsAtParentCase() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
addPostInsertActionToShapeTable(qInstance);
|
||||
|
||||
assertEquals(0, TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_PERSON_MEMORY).size());
|
||||
assertEquals(0, TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_SHAPE).size());
|
||||
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(TestUtils.TABLE_NAME_SHAPE);
|
||||
insertInput.setRecords(List.of(
|
||||
new QRecord().withValue("name", "Circle")
|
||||
));
|
||||
InsertOutput insertOutput = new InsertAction().execute(insertInput);
|
||||
Integer shapeId = insertOutput.getRecords().get(0).getValueInteger("id");
|
||||
|
||||
List<QRecord> personRecords = TestUtils.queryTable(qInstance, TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
assertEquals(1, personRecords.size());
|
||||
assertEquals(shapeId, personRecords.get(0).getValue("favoriteShapeId"));
|
||||
assertEquals("loves Circle", personRecords.get(0).getValue("lastName"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** for the person table - where we do PARENT_POINTS_AT_CHILD
|
||||
*******************************************************************************/
|
||||
public static class PersonPostInsertAddFavoriteShapeCustomizer extends ChildInserterPostInsertCustomizer
|
||||
{
|
||||
|
||||
@ -215,4 +254,47 @@ class ChildInserterPostInsertCustomizerTest extends BaseTest
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** for the shape table - where we do CHILD_POINTS_AT_PARENT
|
||||
*******************************************************************************/
|
||||
public static class ShapePostInsertAddPersonCustomizer extends ChildInserterPostInsertCustomizer
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public QRecord buildChildForRecord(QRecord parentRecord) throws QException
|
||||
{
|
||||
return (new QRecord()
|
||||
.withValue("firstName", "Someone who")
|
||||
.withValue("lastName", "loves " + parentRecord.getValue("name"))
|
||||
.withValue("favoriteShapeId", parentRecord.getValue("id")));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public String getChildTableName()
|
||||
{
|
||||
return (TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public RelationshipType getRelationshipType()
|
||||
{
|
||||
return (RelationshipType.CHILD_POINTS_AT_PARENT);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -31,6 +31,7 @@ import com.kingsrook.qqq.backend.core.actions.scripts.logging.Log4jCodeExecution
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.NoopCodeExecutionLogger;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.QCodeExecutionLoggerInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.StoreScriptLogAndScriptLogLineExecutionLogger;
|
||||
import com.kingsrook.qqq.backend.core.actions.scripts.logging.SystemOutExecutionLogger;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QCodeException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
@ -112,6 +113,21 @@ class ExecuteCodeActionTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testSystemOutLogger() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
ExecuteCodeInput executeCodeInput = setupInput(qInstance, Map.of("x", 4), new SystemOutExecutionLogger());
|
||||
ExecuteCodeOutput executeCodeOutput = new ExecuteCodeOutput();
|
||||
new ExecuteCodeAction().run(executeCodeInput, executeCodeOutput);
|
||||
assertEquals(16, executeCodeOutput.getOutput());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -49,6 +49,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.code.QCodeReference;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.statusmessages.BadInputStatusMessage;
|
||||
import com.kingsrook.qqq.backend.core.model.statusmessages.QWarningMessage;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTablesMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.ListBuilder;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||
@ -124,6 +125,7 @@ class DeleteActionTest extends BaseTest
|
||||
new InsertAction().execute(insertInput);
|
||||
|
||||
new AuditsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
new QQQTablesMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY).setAuditRules(new QAuditRules().withAuditLevel(AuditLevel.RECORD));
|
||||
|
||||
DeleteInput deleteInput = new DeleteInput();
|
||||
@ -154,6 +156,7 @@ class DeleteActionTest extends BaseTest
|
||||
new InsertAction().execute(insertInput);
|
||||
|
||||
new AuditsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
new QQQTablesMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY).setAuditRules(new QAuditRules().withAuditLevel(AuditLevel.RECORD));
|
||||
|
||||
DeleteInput deleteInput = new DeleteInput();
|
||||
|
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.tables.helpers;
|
||||
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.utils.SleepUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for ActionTimeoutHelper
|
||||
*******************************************************************************/
|
||||
class ActionTimeoutHelperTest extends BaseTest
|
||||
{
|
||||
boolean didCancel = false;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTimesOut()
|
||||
{
|
||||
didCancel = false;
|
||||
ActionTimeoutHelper actionTimeoutHelper = new ActionTimeoutHelper(10, TimeUnit.MILLISECONDS, () -> doCancel());
|
||||
actionTimeoutHelper.start();
|
||||
SleepUtils.sleep(50, TimeUnit.MILLISECONDS);
|
||||
assertTrue(didCancel);
|
||||
assertTrue(actionTimeoutHelper.getDidTimeout());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testGetsCancelled()
|
||||
{
|
||||
didCancel = false;
|
||||
ActionTimeoutHelper actionTimeoutHelper = new ActionTimeoutHelper(100, TimeUnit.MILLISECONDS, () -> doCancel());
|
||||
actionTimeoutHelper.start();
|
||||
SleepUtils.sleep(10, TimeUnit.MILLISECONDS);
|
||||
actionTimeoutHelper.cancel();
|
||||
assertFalse(didCancel);
|
||||
SleepUtils.sleep(200, TimeUnit.MILLISECONDS);
|
||||
assertFalse(didCancel);
|
||||
assertFalse(actionTimeoutHelper.getDidTimeout());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private void doCancel()
|
||||
{
|
||||
didCancel = true;
|
||||
}
|
||||
|
||||
}
|
@ -1,7 +1,29 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.processes.implementations.columnstats;
|
||||
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
@ -53,9 +75,20 @@ class ColumnStatsStepTest extends BaseTest
|
||||
@SuppressWarnings("unchecked")
|
||||
List<QRecord> valueCounts = (List<QRecord>) values.get("valueCounts");
|
||||
|
||||
assertThat(valueCounts.get(0).getValues()).hasFieldOrPropertyWithValue("lastName", "Simpson").hasFieldOrPropertyWithValue("count", 3);
|
||||
assertThat(valueCounts.get(1).getValues()).hasFieldOrPropertyWithValue("lastName", null).hasFieldOrPropertyWithValue("count", 2); // here's the assert for the "" and null record above.
|
||||
assertThat(valueCounts.get(2).getValues()).hasFieldOrPropertyWithValue("lastName", "Flanders").hasFieldOrPropertyWithValue("count", 1);
|
||||
assertThat(valueCounts.get(0).getValues())
|
||||
.hasFieldOrPropertyWithValue("lastName", "Simpson")
|
||||
.hasFieldOrPropertyWithValue("count", 3)
|
||||
.hasFieldOrPropertyWithValue("percent", new BigDecimal("50.00"));
|
||||
|
||||
assertThat(valueCounts.get(1).getValues())
|
||||
.hasFieldOrPropertyWithValue("lastName", null)
|
||||
.hasFieldOrPropertyWithValue("count", 2) // here's the assert for the "" and null record above.
|
||||
.hasFieldOrPropertyWithValue("percent", new BigDecimal("33.33"));
|
||||
|
||||
assertThat(valueCounts.get(2).getValues())
|
||||
.hasFieldOrPropertyWithValue("lastName", "Flanders")
|
||||
.hasFieldOrPropertyWithValue("count", 1)
|
||||
.hasFieldOrPropertyWithValue("percent", new BigDecimal("16.67"));
|
||||
}
|
||||
|
||||
}
|
@ -35,6 +35,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.savedfilters.SavedFiltersMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTablesMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.utils.JsonUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -56,6 +57,8 @@ class SavedFilterProcessTests extends BaseTest
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
new SavedFiltersMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
new QQQTablesMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
String tableName = TestUtils.TABLE_NAME_PERSON_MEMORY;
|
||||
|
||||
{
|
||||
|
@ -35,6 +35,7 @@ import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.Script;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTablesMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import com.kingsrook.qqq.backend.core.utils.collections.MapBuilder;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -55,6 +56,7 @@ class RunRecordScriptTest extends BaseTest
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
new ScriptsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
new QQQTablesMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY), List.of(new QRecord().withValue("id", 1)));
|
||||
TestUtils.insertRecords(qInstance, qInstance.getTable(Script.TABLE_NAME), List.of(new QRecord().withValue("id", 1).withTableName(TestUtils.TABLE_NAME_PERSON_MEMORY)));
|
||||
|
@ -37,6 +37,8 @@ import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.Script;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptType;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTableAccessor;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTablesMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@ -58,6 +60,8 @@ class TestScriptProcessStepTest extends BaseTest
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
new ScriptsMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
new QQQTablesMetaDataProvider().defineAll(qInstance, TestUtils.MEMORY_BACKEND_NAME, TestUtils.MEMORY_BACKEND_NAME, null);
|
||||
|
||||
InsertInput insertInput = new InsertInput();
|
||||
insertInput.setTableName(ScriptType.TABLE_NAME);
|
||||
insertInput.setRecords(List.of(new ScriptType()
|
||||
@ -71,7 +75,7 @@ class TestScriptProcessStepTest extends BaseTest
|
||||
insertInput.setRecords(List.of(new Script()
|
||||
.withName("TestScript")
|
||||
.withScriptTypeId(insertOutput.getRecords().get(0).getValueInteger("id"))
|
||||
.withTableName(TestUtils.TABLE_NAME_SHAPE)
|
||||
.withTableId(QQQTableAccessor.getTableId(TestUtils.TABLE_NAME_SHAPE))
|
||||
.toQRecord()));
|
||||
insertOutput = new InsertAction().execute(insertInput);
|
||||
|
||||
@ -89,7 +93,10 @@ class TestScriptProcessStepTest extends BaseTest
|
||||
// expect an error because the javascript module isn't available //
|
||||
//////////////////////////////////////////////////////////////////
|
||||
assertNotNull(output.getValue("exception"));
|
||||
assertThat((Exception) output.getValue("exception")).hasRootCauseInstanceOf(ClassNotFoundException.class);
|
||||
assertThat((Exception) output.getValue("exception"))
|
||||
.hasRootCauseInstanceOf(ClassNotFoundException.class)
|
||||
.rootCause()
|
||||
.hasMessageContaining("QJavaScriptExecutor");
|
||||
}
|
||||
|
||||
}
|
@ -32,6 +32,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.QBackendMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableBackendDetails;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.QBackendModuleInterface;
|
||||
import com.kingsrook.qqq.backend.module.api.actions.APICountAction;
|
||||
import com.kingsrook.qqq.backend.module.api.actions.APIDeleteAction;
|
||||
import com.kingsrook.qqq.backend.module.api.actions.APIGetAction;
|
||||
import com.kingsrook.qqq.backend.module.api.actions.APIInsertAction;
|
||||
import com.kingsrook.qqq.backend.module.api.actions.APIQueryAction;
|
||||
@ -136,7 +137,7 @@ public class APIBackendModule implements QBackendModuleInterface
|
||||
@Override
|
||||
public DeleteInterface getDeleteInterface()
|
||||
{
|
||||
return (null); //return (new RDBMSDeleteAction());
|
||||
return (new APIDeleteAction());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.api.actions;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.DeleteInterface;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public class APIDeleteAction extends AbstractAPIAction implements DeleteInterface
|
||||
{
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public DeleteOutput execute(DeleteInput deleteInput) throws QException
|
||||
{
|
||||
QTableMetaData table = deleteInput.getTable();
|
||||
preAction(deleteInput);
|
||||
return (apiActionUtil.doDelete(table, deleteInput));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Specify whether this particular module's update action can & should fetch
|
||||
** records before updating them, e.g., for audits or "not-found-checks"
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public boolean supportsPreFetchQuery()
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
|
||||
}
|
@ -43,6 +43,8 @@ import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.AbstractTableActionInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
@ -79,6 +81,7 @@ import org.apache.http.HttpEntityEnclosingRequest;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpDelete;
|
||||
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
@ -384,6 +387,41 @@ public class BaseAPIActionUtil
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*
|
||||
*******************************************************************************/
|
||||
public DeleteOutput doDelete(QTableMetaData table, DeleteInput deleteInput) throws QException
|
||||
{
|
||||
try
|
||||
{
|
||||
DeleteOutput deleteOutput = new DeleteOutput();
|
||||
|
||||
String urlSuffix = buildQueryStringForDelete(deleteInput.getQueryFilter(), deleteInput.getPrimaryKeys());
|
||||
String url = buildTableUrl(table);
|
||||
HttpDelete request = new HttpDelete(url + urlSuffix);
|
||||
|
||||
QHttpResponse response = makeRequest(table, request);
|
||||
if(response.getStatusCode() == 204)
|
||||
{
|
||||
deleteOutput.setDeletedRecordCount(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
deleteOutput.setDeletedRecordCount(0);
|
||||
}
|
||||
|
||||
return (deleteOutput);
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.error("Error in API Delete", e);
|
||||
throw new QException("Error executing Delete: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@ -601,6 +639,17 @@ public class BaseAPIActionUtil
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** method to build up delete string based on a given QFilter object
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected String buildQueryStringForDelete(QQueryFilter filter, List<Serializable> primaryKeys) throws QException
|
||||
{
|
||||
return ("");
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Do a default query string for a single-record GET - e.g., a query for just 1 record.
|
||||
*******************************************************************************/
|
||||
|
@ -69,7 +69,11 @@ public class QHttpResponse
|
||||
this.statusProtocolVersion = httpResponse.getStatusLine().getProtocolVersion().toString();
|
||||
}
|
||||
}
|
||||
this.content = EntityUtils.toString(httpResponse.getEntity());
|
||||
|
||||
if(this.statusCode == null || this.statusCode != 204)
|
||||
{
|
||||
this.content = EntityUtils.toString(httpResponse.getEntity());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -39,6 +39,7 @@ import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.insert.InsertInput;
|
||||
@ -422,6 +423,25 @@ class BaseAPIActionUtilTest extends BaseTest
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testDelete() throws QException
|
||||
{
|
||||
mockApiUtilsHelper.enqueueMockResponse("");
|
||||
mockApiUtilsHelper.enqueueMockResponse(new QHttpResponse().withStatusCode(204).withContent(null));
|
||||
|
||||
DeleteInput deleteInput = new DeleteInput();
|
||||
deleteInput.setTableName(TestUtils.MOCK_TABLE_NAME);
|
||||
deleteInput.setPrimaryKeys(List.of(1));
|
||||
DeleteOutput deleteOutput = new DeleteAction().execute(deleteInput);
|
||||
|
||||
// not sure what to assert in here...
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
|
@ -24,6 +24,7 @@ package com.kingsrook.qqq.backend.module.rdbms.actions;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.Instant;
|
||||
@ -91,6 +92,9 @@ public abstract class AbstractRDBMSAction implements QActionInterface
|
||||
|
||||
protected QueryStat queryStat;
|
||||
|
||||
protected PreparedStatement statement;
|
||||
protected boolean isCancelled = false;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -1094,4 +1098,28 @@ public abstract class AbstractRDBMSAction implements QActionInterface
|
||||
this.queryStat = queryStat;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
protected void doCancelQuery()
|
||||
{
|
||||
isCancelled = true;
|
||||
if(statement == null)
|
||||
{
|
||||
LOG.warn("Statement was null when requested to cancel query");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
statement.cancel();
|
||||
}
|
||||
catch(SQLException e)
|
||||
{
|
||||
LOG.warn("Error trying to cancel query (statement)", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,8 +27,11 @@ import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
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.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.Aggregate;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.aggregate.AggregateInput;
|
||||
@ -53,6 +56,7 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(RDBMSAggregateAction.class);
|
||||
|
||||
private ActionTimeoutHelper actionTimeoutHelper;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -102,8 +106,21 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
|
||||
|
||||
try(Connection connection = getConnection(aggregateInput))
|
||||
{
|
||||
QueryManager.executeStatement(connection, sql, ((ResultSet resultSet) ->
|
||||
statement = connection.prepareStatement(sql);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// set up & start an actionTimeoutHelper (note, internally it'll deal with the time being null or negative as meaning not to timeout) //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
actionTimeoutHelper = new ActionTimeoutHelper(aggregateInput.getTimeoutSeconds(), TimeUnit.SECONDS, new StatementTimeoutCanceller(statement, sql));
|
||||
actionTimeoutHelper.start();
|
||||
|
||||
QueryManager.executeStatement(statement, ((ResultSet resultSet) ->
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// once we've started getting results, go ahead and cancel the timeout //
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
actionTimeoutHelper.cancel();
|
||||
|
||||
while(resultSet.next())
|
||||
{
|
||||
setQueryStatFirstResultTime();
|
||||
@ -156,9 +173,30 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
if(actionTimeoutHelper != null && actionTimeoutHelper.getDidTimeout())
|
||||
{
|
||||
setQueryStatFirstResultTime();
|
||||
throw (new QUserFacingException("Aggregate query timed out."));
|
||||
}
|
||||
|
||||
if(isCancelled)
|
||||
{
|
||||
throw (new QUserFacingException("Aggregate query was cancelled."));
|
||||
}
|
||||
|
||||
LOG.warn("Error executing aggregate", e);
|
||||
throw new QException("Error executing aggregate", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(actionTimeoutHelper != null)
|
||||
{
|
||||
/////////////////////////////////////////
|
||||
// make sure the timeout got cancelled //
|
||||
/////////////////////////////////////////
|
||||
actionTimeoutHelper.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -199,4 +237,15 @@ public class RDBMSAggregateAction extends AbstractRDBMSAction implements Aggrega
|
||||
return (StringUtils.join(",", columns));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void cancelAction()
|
||||
{
|
||||
doCancelQuery();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,8 +27,11 @@ import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
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.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.count.CountOutput;
|
||||
@ -46,6 +49,8 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(RDBMSCountAction.class);
|
||||
|
||||
private ActionTimeoutHelper actionTimeoutHelper;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -84,8 +89,21 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf
|
||||
{
|
||||
long mark = System.currentTimeMillis();
|
||||
|
||||
QueryManager.executeStatement(connection, sql, ((ResultSet resultSet) ->
|
||||
statement = connection.prepareStatement(sql);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// set up & start an actionTimeoutHelper (note, internally it'll deal with the time being null or negative as meaning not to timeout) //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
actionTimeoutHelper = new ActionTimeoutHelper(countInput.getTimeoutSeconds(), TimeUnit.SECONDS, new StatementTimeoutCanceller(statement, sql));
|
||||
actionTimeoutHelper.start();
|
||||
|
||||
QueryManager.executeStatement(statement, ((ResultSet resultSet) ->
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// once we've started getting results, go ahead and cancel the timeout //
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
actionTimeoutHelper.cancel();
|
||||
|
||||
if(resultSet.next())
|
||||
{
|
||||
rs.setCount(resultSet.getInt("record_count"));
|
||||
@ -107,9 +125,41 @@ public class RDBMSCountAction extends AbstractRDBMSAction implements CountInterf
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
if(actionTimeoutHelper != null && actionTimeoutHelper.getDidTimeout())
|
||||
{
|
||||
setQueryStatFirstResultTime();
|
||||
throw (new QUserFacingException("Count timed out."));
|
||||
}
|
||||
|
||||
if(isCancelled)
|
||||
{
|
||||
throw (new QUserFacingException("Count was cancelled."));
|
||||
}
|
||||
|
||||
LOG.warn("Error executing count", e);
|
||||
throw new QException("Error executing count", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(actionTimeoutHelper != null)
|
||||
{
|
||||
/////////////////////////////////////////
|
||||
// make sure the timeout got cancelled //
|
||||
/////////////////////////////////////////
|
||||
actionTimeoutHelper.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void cancelAction()
|
||||
{
|
||||
doCancelQuery();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -33,9 +33,12 @@ import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import com.kingsrook.qqq.backend.core.actions.interfaces.QueryInterface;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.helpers.ActionTimeoutHelper;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QUserFacingException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
@ -60,6 +63,8 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(RDBMSQueryAction.class);
|
||||
|
||||
private ActionTimeoutHelper actionTimeoutHelper;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -136,14 +141,29 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
|
||||
|
||||
try
|
||||
{
|
||||
/////////////////////////////////////
|
||||
// create a statement from the SQL //
|
||||
/////////////////////////////////////
|
||||
statement = createStatement(connection, sql.toString(), queryInput);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// set up & start an actionTimeoutHelper (note, internally it'll deal with the time being null or negative as meaning not to timeout) //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
actionTimeoutHelper = new ActionTimeoutHelper(queryInput.getTimeoutSeconds(), TimeUnit.SECONDS, new StatementTimeoutCanceller(statement, sql));
|
||||
actionTimeoutHelper.start();
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// execute the query - iterate over results //
|
||||
//////////////////////////////////////////////
|
||||
QueryOutput queryOutput = new QueryOutput(queryInput);
|
||||
|
||||
PreparedStatement statement = createStatement(connection, sql.toString(), queryInput);
|
||||
QueryManager.executeStatement(statement, ((ResultSet resultSet) ->
|
||||
{
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
// once we've started getting results, go ahead and cancel the timeout //
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
actionTimeoutHelper.cancel();
|
||||
|
||||
ResultSetMetaData metaData = resultSet.getMetaData();
|
||||
while(resultSet.next())
|
||||
{
|
||||
@ -201,6 +221,14 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
|
||||
}
|
||||
finally
|
||||
{
|
||||
if(actionTimeoutHelper != null)
|
||||
{
|
||||
/////////////////////////////////////////
|
||||
// make sure the timeout got cancelled //
|
||||
/////////////////////////////////////////
|
||||
actionTimeoutHelper.cancel();
|
||||
}
|
||||
|
||||
if(needToCloseConnection)
|
||||
{
|
||||
connection.close();
|
||||
@ -209,6 +237,17 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
if(actionTimeoutHelper != null && actionTimeoutHelper.getDidTimeout())
|
||||
{
|
||||
setQueryStatFirstResultTime();
|
||||
throw (new QUserFacingException("Query timed out."));
|
||||
}
|
||||
|
||||
if(isCancelled)
|
||||
{
|
||||
throw (new QUserFacingException("Query was cancelled."));
|
||||
}
|
||||
|
||||
LOG.warn("Error executing query", e);
|
||||
throw new QException("Error executing query", e);
|
||||
}
|
||||
@ -282,20 +321,6 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private boolean filterOutHeavyFieldsIfNeeded(QFieldMetaData field, boolean shouldFetchHeavyFields)
|
||||
{
|
||||
if(!shouldFetchHeavyFields && field.getIsHeavy())
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** if we're not fetching heavy fields, instead just get their length. this
|
||||
** method wraps the field 'sql name' (e.g., column_name or table_name.column_name)
|
||||
@ -338,4 +363,14 @@ public class RDBMSQueryAction extends AbstractRDBMSAction implements QueryInterf
|
||||
return (statement);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void cancelAction()
|
||||
{
|
||||
doCancelQuery();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. 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.Statement;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QRuntimeException;
|
||||
import com.kingsrook.qqq.backend.core.logging.QLogger;
|
||||
import static com.kingsrook.qqq.backend.core.logging.LogUtils.logPair;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Helper to cancel statements that timeout.
|
||||
*******************************************************************************/
|
||||
public class StatementTimeoutCanceller implements Runnable
|
||||
{
|
||||
private static final QLogger LOG = QLogger.getLogger(StatementTimeoutCanceller.class);
|
||||
|
||||
private final Statement statement;
|
||||
private final String sql;
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Constructor
|
||||
**
|
||||
*******************************************************************************/
|
||||
public StatementTimeoutCanceller(Statement statement, CharSequence sql)
|
||||
{
|
||||
this.statement = statement;
|
||||
this.sql = sql.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
statement.cancel();
|
||||
LOG.info("Cancelled timed out statement", logPair("sql", sql));
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
LOG.warn("Error trying to cancel statement after timeout", e, logPair("sql", sql));
|
||||
}
|
||||
|
||||
throw (new QRuntimeException("Statement timed out and was cancelled."));
|
||||
}
|
||||
}
|
@ -1 +1 @@
|
||||
0.16.0
|
||||
0.18.0
|
||||
|
@ -986,9 +986,16 @@ public class ApiImplementation
|
||||
{
|
||||
String[] ids = paramMap.get(idParam).split(",");
|
||||
|
||||
QTableMetaData table = QContext.getQInstance().getTable(process.getTableName());
|
||||
QQueryFilter filter = new QQueryFilter(new QFilterCriteria(table.getPrimaryKeyField(), IN, Arrays.asList(ids)));
|
||||
runProcessInput.setCallback(getCallback(filter));
|
||||
if(StringUtils.hasContent(process.getTableName()))
|
||||
{
|
||||
QTableMetaData table = QContext.getQInstance().getTable(process.getTableName());
|
||||
QQueryFilter filter = new QQueryFilter(new QFilterCriteria(table.getPrimaryKeyField(), IN, Arrays.asList(ids)));
|
||||
runProcessInput.setCallback(getProcessCallback(filter));
|
||||
}
|
||||
else
|
||||
{
|
||||
runProcessInput.addValue(idParam, paramMap.get(idParam));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1517,7 +1524,7 @@ public class ApiImplementation
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static QProcessCallback getCallback(QQueryFilter filter)
|
||||
public static QProcessCallback getProcessCallback(QQueryFilter filter)
|
||||
{
|
||||
return new QProcessCallback()
|
||||
{
|
||||
|
@ -45,6 +45,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.QFieldSection;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.Tier;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.UniqueKey;
|
||||
import com.kingsrook.qqq.backend.core.utils.CollectionUtils;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -62,7 +63,8 @@ public class ApiInstanceMetaDataProvider
|
||||
*******************************************************************************/
|
||||
public static void defineAll(QInstance qInstance, String backendName, Consumer<QTableMetaData> backendDetailEnricher) throws QException
|
||||
{
|
||||
definePossibleValueSources(qInstance);
|
||||
definePossibleValueSourcesUsedByApiLogTable(qInstance);
|
||||
definePossibleValueSourcesForApiNameAndVersion(qInstance);
|
||||
defineAPILogTable(qInstance, backendName, backendDetailEnricher);
|
||||
defineAPILogUserTable(qInstance, backendName, backendDetailEnricher);
|
||||
}
|
||||
@ -72,7 +74,7 @@ public class ApiInstanceMetaDataProvider
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static void definePossibleValueSources(QInstance instance)
|
||||
public static void definePossibleValueSourcesUsedByApiLogTable(QInstance instance)
|
||||
{
|
||||
instance.addPossibleValueSource(new QPossibleValueSource()
|
||||
.withName(TABLE_NAME_API_LOG_USER)
|
||||
@ -104,7 +106,15 @@ public class ApiInstanceMetaDataProvider
|
||||
new QPossibleValue<>(429, "429 (Too Many Requests)"),
|
||||
new QPossibleValue<>(500, "500 (Internal Server Error)")
|
||||
)));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static void definePossibleValueSourcesForApiNameAndVersion(QInstance instance)
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// loop over api names and versions, building out possible values sources //
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
@ -121,14 +131,14 @@ public class ApiInstanceMetaDataProvider
|
||||
apiNamePossibleValues.add(new QPossibleValue<>(entry.getKey(), entry.getValue().getLabel()));
|
||||
|
||||
ApiInstanceMetaData apiInstanceMetaData = entry.getValue();
|
||||
allVersions.addAll(apiInstanceMetaData.getPastVersions());
|
||||
allVersions.addAll(apiInstanceMetaData.getSupportedVersions());
|
||||
allVersions.addAll(CollectionUtils.nonNullCollection(apiInstanceMetaData.getPastVersions()));
|
||||
allVersions.addAll(CollectionUtils.nonNullCollection(apiInstanceMetaData.getSupportedVersions()));
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// I think we don't want future-versions in this dropdown, I think... //
|
||||
// grr, actually todo maybe we want this to be a table-backed enum, with past/present/future columns //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
allVersions.addAll(apiInstanceMetaData.getFutureVersions());
|
||||
allVersions.addAll(CollectionUtils.nonNullCollection(apiInstanceMetaData.getFutureVersions()));
|
||||
}
|
||||
|
||||
instance.addPossibleValueSource(new QPossibleValueSource()
|
||||
|
@ -162,6 +162,9 @@ public class QJavalinImplementation
|
||||
private static final long MILLIS_BETWEEN_HOT_SWAPS = 2500;
|
||||
public static final long SLOW_LOG_THRESHOLD_MS = 1000;
|
||||
|
||||
private static final Integer DEFAULT_COUNT_TIMEOUT_SECONDS = 60;
|
||||
private static final Integer DEFAULT_QUERY_TIMEOUT_SECONDS = 60;
|
||||
|
||||
private static int DEFAULT_PORT = 8001;
|
||||
|
||||
private static Javalin service;
|
||||
@ -1075,6 +1078,7 @@ public class QJavalinImplementation
|
||||
countInput.setFilter(JsonUtils.toObject(filter, QQueryFilter.class));
|
||||
}
|
||||
|
||||
countInput.setTimeoutSeconds(DEFAULT_COUNT_TIMEOUT_SECONDS);
|
||||
countInput.setQueryJoins(processQueryJoinsParam(context));
|
||||
countInput.setIncludeDistinctCount(QJavalinUtils.queryParamIsTrue(context, "includeDistinct"));
|
||||
|
||||
@ -1131,6 +1135,7 @@ public class QJavalinImplementation
|
||||
queryInput.setTableName(table);
|
||||
queryInput.setShouldGenerateDisplayValues(true);
|
||||
queryInput.setShouldTranslatePossibleValues(true);
|
||||
queryInput.setTimeoutSeconds(DEFAULT_QUERY_TIMEOUT_SECONDS);
|
||||
|
||||
PermissionsHelper.checkTablePermissionThrowing(queryInput, TablePermissionSubType.READ);
|
||||
|
||||
|
@ -26,7 +26,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.ProcessSummaryLine;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.RunProcessOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.processes.Status;
|
||||
@ -52,7 +52,7 @@ class QJavalinAccessLoggerTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testDefaultOn() throws QInstanceValidationException
|
||||
void testDefaultOn() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
new QJavalinImplementation(qInstance, new QJavalinMetaData());
|
||||
@ -74,7 +74,7 @@ class QJavalinAccessLoggerTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTurnedOffByCode() throws QInstanceValidationException
|
||||
void testTurnedOffByCode() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
new QJavalinImplementation(qInstance, new QJavalinMetaData()
|
||||
@ -97,7 +97,7 @@ class QJavalinAccessLoggerTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTurnedOffBySystemPropertyWithJavalinMetaData() throws QInstanceValidationException
|
||||
void testTurnedOffBySystemPropertyWithJavalinMetaData() throws QException
|
||||
{
|
||||
System.setProperty(DISABLED_PROPERTY, "true");
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
@ -114,7 +114,7 @@ class QJavalinAccessLoggerTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testTurnedOffBySystemPropertyWithoutJavalinMetaData() throws QInstanceValidationException
|
||||
void testTurnedOffBySystemPropertyWithoutJavalinMetaData() throws QException
|
||||
{
|
||||
System.setProperty(DISABLED_PROPERTY, "true");
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
@ -131,7 +131,7 @@ class QJavalinAccessLoggerTest
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testFilter() throws QInstanceValidationException
|
||||
void testFilter() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
new QJavalinImplementation(qInstance, new QJavalinMetaData()
|
||||
|
@ -27,7 +27,7 @@ import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.authentication.TableBasedAuthenticationMetaData;
|
||||
@ -61,7 +61,7 @@ public class QJavalinImplementationAuthenticationTest extends QJavalinTestBase
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeEach
|
||||
public void beforeEach() throws QInstanceValidationException
|
||||
public void beforeEach() throws QException
|
||||
{
|
||||
Unirest.config().reset().enableCookieManagement(false);
|
||||
setupTableBasedAuthenticationInstance();
|
||||
@ -188,7 +188,7 @@ public class QJavalinImplementationAuthenticationTest extends QJavalinTestBase
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
static void setupTableBasedAuthenticationInstance() throws QInstanceValidationException
|
||||
static void setupTableBasedAuthenticationInstance() throws QException
|
||||
{
|
||||
QInstance qInstance = TestUtils.defineInstance();
|
||||
TableBasedAuthenticationMetaData tableBasedAuthenticationMetaData = new TableBasedAuthenticationMetaData();
|
||||
|
@ -22,6 +22,7 @@
|
||||
package com.kingsrook.qqq.backend.javalin;
|
||||
|
||||
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QInstanceValidationException;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryRecordStore;
|
||||
@ -60,7 +61,7 @@ public class QJavalinTestBase
|
||||
**
|
||||
*******************************************************************************/
|
||||
@BeforeAll
|
||||
public static void beforeAll() throws QInstanceValidationException
|
||||
public static void beforeAll() throws QException
|
||||
{
|
||||
qJavalinImplementation = new QJavalinImplementation(TestUtils.defineInstance());
|
||||
QJavalinProcessHandler.setAsyncStepTimeoutMillis(250);
|
||||
|
@ -68,6 +68,7 @@ import com.kingsrook.qqq.backend.core.model.metadata.tables.AssociatedScript;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.tables.QTableMetaData;
|
||||
import com.kingsrook.qqq.backend.core.model.savedfilters.SavedFiltersMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.model.scripts.ScriptsMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.model.tables.QQQTablesMetaDataProvider;
|
||||
import com.kingsrook.qqq.backend.core.processes.implementations.mock.MockBackendStep;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.ConnectionManager;
|
||||
import com.kingsrook.qqq.backend.module.rdbms.jdbc.QueryManager;
|
||||
@ -138,7 +139,7 @@ public class TestUtils
|
||||
** Define the q-instance for testing (h2 rdbms and 'person' table)
|
||||
**
|
||||
*******************************************************************************/
|
||||
public static QInstance defineInstance()
|
||||
public static QInstance defineInstance() throws QException
|
||||
{
|
||||
QInstance qInstance = new QInstance();
|
||||
qInstance.setAuthentication(defineAuthentication());
|
||||
@ -154,6 +155,8 @@ public class TestUtils
|
||||
qInstance.addPossibleValueSource(definePossibleValueSourcePerson());
|
||||
defineWidgets(qInstance);
|
||||
|
||||
new QQQTablesMetaDataProvider().defineAll(qInstance, BACKEND_NAME_MEMORY, BACKEND_NAME_MEMORY, null);
|
||||
|
||||
qInstance.addBackend(defineMemoryBackend());
|
||||
try
|
||||
{
|
||||
|
Reference in New Issue
Block a user